diff options
Diffstat (limited to 'source/util')
| -rw-r--r-- | source/util/Bytes.mc | 60 |
1 files changed, 60 insertions, 0 deletions
diff --git a/source/util/Bytes.mc b/source/util/Bytes.mc index 1008fd2..a32475a 100644 --- a/source/util/Bytes.mc +++ b/source/util/Bytes.mc @@ -47,6 +47,66 @@ module Bytes { }) as ByteArray; } + // Decode UTF-8 bytes to a string. Empty input gives "". Malformed UTF-8 + // or a NUL byte gives null (callers treat that as damaged data). The + // bytes are checked first because convertEncodedString stops the app + // with an uncatchable system error on invalid UTF-8. + function toUtf8(data as ByteArray) as String? { + if (data.size() == 0) { + return ""; + } + if (!isValidUtf8(data)) { + return null; + } + return StringUtil.convertEncodedString(data, { + :fromRepresentation => StringUtil.REPRESENTATION_BYTE_ARRAY, + :toRepresentation => StringUtil.REPRESENTATION_STRING_PLAIN_TEXT, + :encoding => StringUtil.CHAR_ENCODING_UTF8 + }) as String; + } + + // True for well-formed UTF-8 (RFC 3629) without NUL bytes: no overlong + // forms, no surrogates (U+D800-DFFF), nothing above U+10FFFF. + function isValidUtf8(data as ByteArray) as Boolean { + var i = 0; + var n = data.size(); + while (i < n) { + var b = data[i]; + var more = 0; + var min = 0; + var cp = 0; + if (b == 0) { + return false; + } else if (b < 0x80) { + i++; + continue; + } else if (b >= 0xC2 && b <= 0xDF) { + more = 1; min = 0x80; cp = b & 0x1F; + } else if (b >= 0xE0 && b <= 0xEF) { + more = 2; min = 0x800; cp = b & 0x0F; + } else if (b >= 0xF0 && b <= 0xF4) { + more = 3; min = 0x10000; cp = b & 0x07; + } else { + return false; + } + if (i + more >= n) { + return false; + } + for (var k = 1; k <= more; k++) { + var c = data[i + k]; + if ((c & 0xC0) != 0x80) { + return false; + } + cp = (cp << 6) | (c & 0x3F); + } + if (cp < min || cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF)) { + return false; + } + i += more + 1; + } + return true; + } + // A new array holding a followed by b; neither input is changed. function concat(a as ByteArray, b as ByteArray) as ByteArray { var out = new [0]b; |
