Changes in the validation of UTF-8
All UTF-8 encoding functionality (including the escape sequence '\u') accepts all values from the original UTF-8 specification (with sequences of up to six bytes). By default, the decoding functions in the UTF-8 library do not accept invalid Unicode code points, such as surrogates. A new parameter 'nonstrict' makes them accept all code points up to (2^31)-1, as in the original UTF-8 specification.
This commit is contained in:
@@ -21,62 +21,59 @@ local justone = "^" .. utf8.charpattern .. "$"
|
||||
|
||||
-- 't' is the list of codepoints of 's'
|
||||
local function checksyntax (s, t)
|
||||
-- creates a string "return '\u{t[1]}...\u{t[n]}'"
|
||||
local ts = {"return '"}
|
||||
for i = 1, #t do ts[i + 1] = string.format("\\u{%x}", t[i]) end
|
||||
ts[#t + 2] = "'"
|
||||
ts = table.concat(ts)
|
||||
-- its execution should result in 's'
|
||||
assert(assert(load(ts))() == s)
|
||||
end
|
||||
|
||||
assert(utf8.offset("alo", 5) == nil)
|
||||
assert(utf8.offset("alo", -4) == nil)
|
||||
|
||||
-- 't' is the list of codepoints of 's'
|
||||
local function check (s, t)
|
||||
local l = utf8.len(s)
|
||||
-- 'check' makes several tests over the validity of string 's'.
|
||||
-- 't' is the list of codepoints of 's'.
|
||||
local function check (s, t, nonstrict)
|
||||
local l = utf8.len(s, 1, -1, nonstrict)
|
||||
assert(#t == l and len(s) == l)
|
||||
assert(utf8.char(table.unpack(t)) == s)
|
||||
assert(utf8.char(table.unpack(t)) == s) -- 't' and 's' are equivalent
|
||||
|
||||
assert(utf8.offset(s, 0) == 1)
|
||||
|
||||
checksyntax(s, t)
|
||||
|
||||
local t1 = {utf8.codepoint(s, 1, -1)}
|
||||
-- creates new table with all codepoints of 's'
|
||||
local t1 = {utf8.codepoint(s, 1, -1, nonstrict)}
|
||||
assert(#t == #t1)
|
||||
for i = 1, #t do assert(t[i] == t1[i]) end
|
||||
for i = 1, #t do assert(t[i] == t1[i]) end -- 't' is equal to 't1'
|
||||
|
||||
for i = 1, l do
|
||||
for i = 1, l do -- for all codepoints
|
||||
local pi = utf8.offset(s, i) -- position of i-th char
|
||||
local pi1 = utf8.offset(s, 2, pi) -- position of next char
|
||||
assert(string.find(string.sub(s, pi, pi1 - 1), justone))
|
||||
assert(utf8.offset(s, -1, pi1) == pi)
|
||||
assert(utf8.offset(s, i - l - 1) == pi)
|
||||
assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi)))
|
||||
for j = pi, pi1 - 1 do
|
||||
assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi, pi, nonstrict)))
|
||||
for j = pi, pi1 - 1 do
|
||||
assert(utf8.offset(s, 0, j) == pi)
|
||||
end
|
||||
for j = pi + 1, pi1 - 1 do
|
||||
assert(not utf8.len(s, j))
|
||||
end
|
||||
assert(utf8.len(s, pi, pi) == 1)
|
||||
assert(utf8.len(s, pi, pi1 - 1) == 1)
|
||||
assert(utf8.len(s, pi) == l - i + 1)
|
||||
assert(utf8.len(s, pi1) == l - i)
|
||||
assert(utf8.len(s, 1, pi) == i)
|
||||
assert(utf8.len(s, pi, pi, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, pi1 - 1, nonstrict) == 1)
|
||||
assert(utf8.len(s, pi, -1, nonstrict) == l - i + 1)
|
||||
assert(utf8.len(s, pi1, -1, nonstrict) == l - i)
|
||||
assert(utf8.len(s, 1, pi, -1, nonstrict) == i)
|
||||
end
|
||||
|
||||
local i = 0
|
||||
for p, c in utf8.codes(s) do
|
||||
for p, c in utf8.codes(s, nonstrict) do
|
||||
i = i + 1
|
||||
assert(c == t[i] and p == utf8.offset(s, i))
|
||||
assert(utf8.codepoint(s, p) == c)
|
||||
end
|
||||
assert(i == #t)
|
||||
|
||||
i = 0
|
||||
for p, c in utf8.codes(s) do
|
||||
i = i + 1
|
||||
assert(c == t[i] and p == utf8.offset(s, i))
|
||||
assert(utf8.codepoint(s, p, p, nonstrict) == c)
|
||||
end
|
||||
assert(i == #t)
|
||||
|
||||
@@ -105,13 +102,17 @@ do -- error indication in utf8.len
|
||||
check("\xF4\x9F\xBF\xBF", 1)
|
||||
end
|
||||
|
||||
-- error in utf8.codes
|
||||
checkerror("invalid UTF%-8 code",
|
||||
function ()
|
||||
local s = "ab\xff"
|
||||
for c in utf8.codes(s) do assert(c) end
|
||||
end)
|
||||
|
||||
-- errors in utf8.codes
|
||||
do
|
||||
local function errorcodes (s)
|
||||
checkerror("invalid UTF%-8 code",
|
||||
function ()
|
||||
for c in utf8.codes(s) do assert(c) end
|
||||
end)
|
||||
end
|
||||
errorcodes("ab\xff")
|
||||
errorcodes("\u{110000}")
|
||||
end
|
||||
|
||||
-- error in initial position for offset
|
||||
checkerror("position out of range", utf8.offset, "abc", 1, 5)
|
||||
@@ -141,14 +142,22 @@ do
|
||||
assert(#t == 0)
|
||||
checkerror("out of range", utf8.codepoint, s, -(#s + 1), 1)
|
||||
checkerror("out of range", utf8.codepoint, s, 1, #s + 1)
|
||||
-- surrogates
|
||||
assert(utf8.codepoint("\u{D7FF}") == 0xD800 - 1)
|
||||
assert(utf8.codepoint("\u{E000}") == 0xDFFF + 1)
|
||||
assert(utf8.codepoint("\u{D800}", 1, 1, true) == 0xD800)
|
||||
assert(utf8.codepoint("\u{DFFF}", 1, 1, true) == 0xDFFF)
|
||||
assert(utf8.codepoint("\u{7FFFFFFF}", 1, 1, true) == 0x7FFFFFFF)
|
||||
end
|
||||
|
||||
assert(utf8.char() == "")
|
||||
assert(utf8.char(97, 98, 99) == "abc")
|
||||
assert(utf8.char(0, 97, 98, 99, 1) == "\0abc\1")
|
||||
|
||||
assert(utf8.codepoint(utf8.char(0x10FFFF)) == 0x10FFFF)
|
||||
assert(utf8.codepoint(utf8.char(0x7FFFFFFF), 1, 1, true) == (1<<31) - 1)
|
||||
|
||||
checkerror("value out of range", utf8.char, 0x10FFFF + 1)
|
||||
checkerror("value out of range", utf8.char, 0x7FFFFFFF + 1)
|
||||
checkerror("value out of range", utf8.char, -1)
|
||||
|
||||
local function invalid (s)
|
||||
checkerror("invalid UTF%-8 code", utf8.codepoint, s)
|
||||
@@ -158,6 +167,10 @@ end
|
||||
-- UTF-8 representation for 0x11ffff (value out of valid range)
|
||||
invalid("\xF4\x9F\xBF\xBF")
|
||||
|
||||
-- surrogates
|
||||
invalid("\u{D800}")
|
||||
invalid("\u{DFFF}")
|
||||
|
||||
-- overlong sequences
|
||||
invalid("\xC0\x80") -- zero
|
||||
invalid("\xC1\xBF") -- 0x7F (should be coded in 1 byte)
|
||||
@@ -183,6 +196,21 @@ s = "\0 \x7F\z
|
||||
s = string.gsub(s, " ", "")
|
||||
check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF})
|
||||
|
||||
do
|
||||
-- original UTF-8 values
|
||||
local s = "\u{4000000}\u{7FFFFFFF}"
|
||||
assert(#s == 12)
|
||||
check(s, {0x4000000, 0x7FFFFFFF}, true)
|
||||
|
||||
s = "\u{200000}\u{3FFFFFF}"
|
||||
assert(#s == 10)
|
||||
check(s, {0x200000, 0x3FFFFFF}, true)
|
||||
|
||||
s = "\u{10000}\u{1fffff}"
|
||||
assert(#s == 8)
|
||||
check(s, {0x10000, 0x1FFFFF}, true)
|
||||
end
|
||||
|
||||
x = "日本語a-4\0éó"
|
||||
check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user