Skip to content

Commit 9506def

Browse files
committed
fix: UTF-8 parsing
1 parent 89e4e73 commit 9506def

1 file changed

Lines changed: 53 additions & 5 deletions

File tree

Binary/UTF8/Get.lean

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,60 @@ private def chars_to_string (xs : Array Char) : String :=
1717

1818
@[always_inline, specialize]
1919
def satisfy (p : Char → Bool) : Get Char := do
20-
let b ← getThe UInt8
21-
let c := byteToChar b
22-
if p c then
23-
return c
20+
let b ← pending (getThe UInt8)
21+
let b1 := UInt8.toUInt32 b
22+
if b1 <= 127 then
23+
let c := Char.ofNat b.toNat
24+
if p c then
25+
return c
26+
else
27+
fail "unexpected byte"
28+
else if 194 ≤ b1 && b1 ≤ 223 then -- 2-byte sequence
29+
let b2 ← pending (getThe UInt8)
30+
let b2u := UInt8.toUInt32 b2
31+
if (b2u &&& 192) != 128 then fail "invalid utf8 continuation"
32+
let cp := ((b1 &&& 31) <<< 6) ||| (b2u &&& 63)
33+
let c := Char.ofNat cp.toNat
34+
if p c then
35+
return c
36+
else
37+
fail "unexpected byte"
38+
else if 224 ≤ b1 && b1 ≤ 239 then -- 3-byte sequence
39+
let b2 ← pending (getThe UInt8)
40+
let b3 ← pending (getThe UInt8)
41+
let b2u := UInt8.toUInt32 b2
42+
let b3u := UInt8.toUInt32 b3
43+
if (b2u &&& 192) != 128 || (b3u &&& 192) != 128 then fail "invalid utf8 continuation"
44+
-- prevent overlongs and surrogates
45+
if b1 == 224 && b2u < 160 then fail "overlong utf8 sequence"
46+
if b1 == 237 && b2u > 159 then fail "utf8 surrogate"
47+
let cp := ((b1 &&& 15) <<< 12) ||| ((b2u &&& 63) <<< 6) ||| (b3u &&& 63)
48+
let c := Char.ofNat cp.toNat
49+
if p c then
50+
return c
51+
else
52+
fail "unexpected byte"
53+
else if 240 ≤ b1 && b1 ≤ 244 then -- 4-byte sequence
54+
let b2 ← pending (getThe UInt8)
55+
let b3 ← pending (getThe UInt8)
56+
let b4 ← pending (getThe UInt8)
57+
let b2u := UInt8.toUInt32 b2
58+
let b3u := UInt8.toUInt32 b3
59+
let b4u := UInt8.toUInt32 b4
60+
if (b2u &&& 192) != 128 || (b3u &&& 192) != 128 || (b4u &&& 192) != 128 then
61+
fail "invalid utf8 continuation"
62+
if b1 == 240 && b2u < 144 then fail "overlong utf8 sequence"
63+
if b1 == 244 && b2u > 143 then fail "utf8 codepoint too large"
64+
let cp := ((b1 &&& 7) <<< 18) ||| ((b2u &&& 63) <<< 12) ||| ((b3u &&& 63) <<< 6) ||| (b4u &&& 63)
65+
let cpNat := cp.toNat
66+
if cpNat > 0x10FFFF then fail "utf8 codepoint out of range"
67+
let c := Char.ofNat cpNat
68+
if p c then
69+
return c
70+
else
71+
fail "unexpected byte"
2472
else
25-
fail "unexpected byte"
73+
fail "invalid utf8 leading byte"
2674

2775
@[always_inline]
2876
def pchar (c : Char) : Get Char := satisfy (· == c)

0 commit comments

Comments
 (0)