c/lib/dec.c | 13 ++++++++----- c/lib/utf8.c | 243 +++++++++++++++++++++++++++++++++-------------------- c/lib/utf8.h | 12 ++++++------ c/t/.gitignore | 1 + c/t/t.list | 1 + c/t/utf8-validness.t.c | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++ diff --git a/c/lib/dec.c b/c/lib/dec.c index f2c7f22e2218f27950d1bd74d559f8bf0cfe9b0a8c1b0f10ecd6df3ad64ccf26..8cc1c2da16a4fc7271c190697bd4cee4a59086e715834a2261e55d3252f23d89 100644 --- a/c/lib/dec.c +++ b/c/lib/dec.c @@ -104,13 +104,16 @@ if (atom->typ == KEKSItemStr) { if (l > PTRDIFF_MAX) { return KEKSErrLenTooBig; } - size_t cpl = 0; - uint32_t cp = KEKSUTF8InvalidCp; - for (size_t n = 0; n < l; n += cpl) { - cpl = KEKSUTF8CpDecode(&cp, atom->v.str.ptr + (ptrdiff_t)n, l - n); - if (cp == KEKSUTF8InvalidCp) { + int state = 0; + long cp = 0; + for (size_t n = 0; n < l; n++) { + state = KEKSUTF8Decode(state, &cp, (int)(atom->v.str.ptr[n])); + if (state == KEKSUTF8Reject) { return KEKSErrBadUTF8; } + } + if (state != KEKSUTF8Accept) { + return KEKSErrBadUTF8; } } return KEKSErrNo; diff --git a/c/lib/utf8.c b/c/lib/utf8.c index df223933046bdf7e6b81cd1453e48700fd8d7ba6fc56d8f32d90dcd5cc2080b1..a125338fe4aa62435947f322a98b9695613f1ca0fc9e2b0299bdd5f1ea30c6dc 100644 --- a/c/lib/utf8.c +++ b/c/lib/utf8.c @@ -1,96 +1,159 @@ -// This code is stripped copy-paste of https://libs.suckless.org/libgrapheme/ -// grapheme_decode_utf8 function, licenced under ISC-License. -// Copyright 2019-2022 Laslo Hunhold +// This code is based on https://github.com/skeeto/scratch.git's +// parsers/utf8_decode.c. Below is its documentation string. -#include -#include -#include +/* Branchless UTF-8 decoder automaton + * + * This automaton accepts one byte of input at a time, eventually either + * entering an ACCEPT state (0) and leaving a code point, or entering + * the REJECT state (-1). The state and code point must be zero on the + * first call of a new code point. + * + * The state value is always in the range -1 to 7. + * + * This is free and unencumbered software released into the public domain. + */ #include "utf8.h" -#define BETWEEN(c, l, u) (((c) >= (l)) && ((c) <= (u))) - -const uint32_t KEKSUTF8InvalidCp = 0xFFFD; - -static const struct { - uint32_t mincp; - uint8_t lower; - uint8_t upper; - char _pad[2]; -} lut[] = { - [0] = - { - /* 0xxxxxxx */ - .lower = 0x00, /* 00000000 */ - .upper = 0x7F, /* 01111111 */ - .mincp = (uint32_t)0, - }, - [1] = - { - /* 110xxxxx */ - .lower = 0xC0, /* 11000000 */ - .upper = 0xDF, /* 11011111 */ - .mincp = (uint32_t)1 << (uint8_t)7, - }, - [2] = - { - /* 1110xxxx */ - .lower = 0xE0, /* 11100000 */ - .upper = 0xEF, /* 11101111 */ - .mincp = (uint32_t)1 << (uint8_t)11, - }, - [3] = - { - /* 11110xxx */ - .lower = 0xF0, /* 11110000 */ - .upper = 0xF7, /* 11110111 */ - .mincp = (uint32_t)1 << (uint8_t)16, - }, -}; - -size_t -KEKSUTF8CpDecode(uint32_t *cp, const unsigned char *str, const size_t len) +int +KEKSUTF8Decode(int state, long *cp, int byte) { - assert(cp != NULL); - if (str == NULL || len == 0) { - (*cp) = KEKSUTF8InvalidCp; - return 0; - } - if (str[0] == 0) { - (*cp) = KEKSUTF8InvalidCp; - return 1; - } - size_t off = 0; - for (off = 0; off < 4; off++) { - if (BETWEEN(str[0], lut[off].lower, lut[off].upper)) { - (*cp) = str[0] - lut[off].lower; - break; - } - } - if (off == 4) { - (*cp) = KEKSUTF8InvalidCp; - return 1; - } - size_t i = 0; - if ((1 + off) > len) { - (*cp) = KEKSUTF8InvalidCp; - for (i = 0; 1 + i < len; i++) { - if (!BETWEEN(str[1 + i], 0x80, 0xBF)) { - break; - } - } - return ((1 + i) < len) ? (1 + i) : (1 + off); - } - for (i = 1; i <= off; i++) { - if (!BETWEEN(str[i], 0x80, 0xBF)) { - (*cp) = KEKSUTF8InvalidCp; - return 1 + (i - 1); - } - (*cp) = (*cp << (uint8_t)6) | // NOLINT(hicpp-signed-bitwise) - (str[i] & (uint8_t)0x3F); - } - if ((*cp < lut[off].mincp) || BETWEEN(*cp, 0xD800, 0xDFFF) || (*cp > 0x10FFFF)) { - (*cp) = KEKSUTF8InvalidCp; - } - return 1 + off; + // clang-format off + static const signed char table[8][256] = { + {+0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +3, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +4, +2, +2, + +5, +6, +6, +6, +7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, +1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, +2, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1} + }; + // clang-format on + static const unsigned char masks[2][8] = { + {0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f, 0x3f}, + {0x7f, 0x1f, 0x0f, 0x0f, 0x0f, 0x07, 0x07, 0x07}}; + int next = table[state][byte]; + *cp = (*cp << 6) | (byte & masks[!state][next & 7]); + return next; } diff --git a/c/lib/utf8.h b/c/lib/utf8.h index accabb5fa9ef0179aedc52b57926c7119b241b0772a3ba3bb6cdbeecdb23ecc5..7b0bc8ee9acf7216e093742baec3656ea32b395910eb9e5dd755dedf0e893260 100644 --- a/c/lib/utf8.h +++ b/c/lib/utf8.h @@ -1,12 +1,12 @@ #ifndef KEKS_UTF8_H #define KEKS_UTF8_H -#include -#include - -extern const uint32_t KEKSUTF8InvalidCp; +enum { + KEKSUTF8Accept = 0, + KEKSUTF8Reject = -1, +}; -size_t -KEKSUTF8CpDecode(uint32_t *cp, const unsigned char *str, const size_t len); +int +KEKSUTF8Decode(int state, long *cp, int byte); #endif // KEKS_UTF8_H diff --git a/c/t/.gitignore b/c/t/.gitignore index 0469704708d3a546fdd3e20758e5e046f2a87afe8af69b21b456880e92cb3b2a..53beac15ba3b735ea5c2237b46d025d0f9898b3a073c62dd31ac2c34f744aebb 100644 --- a/c/t/.gitignore +++ b/c/t/.gitignore @@ -4,3 +4,4 @@ /magic.t /nil.t /tai.t /tai64.t +/utf8-validness.t diff --git a/c/t/t.list b/c/t/t.list index 012aedf259dd8da203a1478d8787ebf60ef21a976352c4a190488344c70e4411..5200d730371698125604845db6efc0951052eb9ec0a6dfe7d15121c129e968d1 100644 --- a/c/t/t.list +++ b/c/t/t.list @@ -3,3 +3,4 @@ magic.t nil.t tai.t tai64.t +utf8-validness.t diff --git a/c/t/utf8-validness.t.c b/c/t/utf8-validness.t.c new file mode 100644 index 0000000000000000000000000000000000000000..442e0b66ba5b119e5e13a03e158eca11eadb1d0128322a098634cc3629e97fdf --- /dev/null +++ b/c/t/utf8-validness.t.c @@ -0,0 +1,211 @@ +// This code is based on https://github.com/skeeto/scratch.git' +// parsers/KEKSUTF8Decode.c + +#include + +#include + +#include "tap.h" + +#define IS_SURROGATE(c) ((c) >= 0xd800L && (c) <= 0xdfffL) + +static void * +utf8_encode(void *buf, long c) +{ + unsigned char *s = buf; + if (c >= (1L << 16)) { + s[0] = 0xf0 | (c >> 18); + s[1] = 0x80 | ((c >> 12) & 0x3f); + s[2] = 0x80 | ((c >> 6) & 0x3f); + s[3] = 0x80 | ((c >> 0) & 0x3f); + return s + 4; + } else if (c >= (1L << 11)) { + s[0] = 0xe0 | (c >> 12); + s[1] = 0x80 | ((c >> 6) & 0x3f); + s[2] = 0x80 | ((c >> 0) & 0x3f); + return s + 3; + } else if (c >= (1L << 7)) { + s[0] = 0xc0 | (c >> 6); + s[1] = 0x80 | ((c >> 0) & 0x3f); + return s + 2; + } else { + s[0] = c; + return s + 1; + } +} + +static int +try_decode(const unsigned char *buf, int len) +{ + long cp = 0; + int i, state = 0; + for (i = 0, state = 0; i < len; i++) { + state = KEKSUTF8Decode(state, &cp, buf[i]); + if (state == KEKSUTF8Reject || state == KEKSUTF8Accept) { + break; + } + } + return state; +} + +int +main(void) +{ + long cp; + + for (cp = 0; cp <= 0x1fffff; cp++) { + long out = 0; + int state = 0; + unsigned char *p; + unsigned char buf[4]; + unsigned char *end = utf8_encode(buf, cp); + + for (p = buf; p < end; p++) { + state = KEKSUTF8Decode(state, &out, *p); + if (state == KEKSUTF8Reject || state == KEKSUTF8Accept) { + break; + } + } + + if (cp > 0x10ffff) { + switch (state) { + case KEKSUTF8Accept: + ok(false, "accepted U+%06lx (as U+%06lx)", cp, out); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + + } else if (IS_SURROGATE(cp)) { + switch (state) { + case KEKSUTF8Accept: + ok(false, "accepted U+%06lx (as U+%06lx)", cp, out); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx (surrogate)", cp); + break; + } + + } else { + switch (state) { + case KEKSUTF8Accept: + if (cp != out) { + ok(false, "wrong decode U+%06lx != U+%06lx", cp, out); + } + break; + case KEKSUTF8Reject: + ok(false, "rejected U+%06lx", cp); + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + } + } + + for (cp = 0; cp <= 0x007fL; cp++) { + unsigned char buf[4]; + + buf[0] = 0xc0 | (cp >> 6); + buf[1] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 2)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (2) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + + buf[0] = 0xe0; + buf[1] = 0x80 | (cp >> 6); + buf[2] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 3)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (3) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + + buf[0] = 0xf0; + buf[1] = 0x80; + buf[2] = 0x80 | (cp >> 6); + buf[3] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 4)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (4) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + } + + for (cp = 0x0080L; cp <= 0x07ffL; cp++) { + unsigned char buf[4]; + + buf[0] = 0xe0; + buf[1] = 0x80 | (cp >> 6); + buf[2] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 3)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (3) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + + buf[0] = 0xf0; + buf[1] = 0x80; + buf[2] = 0x80 | (cp >> 6); + buf[3] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 4)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (4) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + } + + for (cp = 0x0800L; cp <= 0xffffL; cp++) { + unsigned char buf[4]; + + buf[0] = 0xf0; + buf[1] = 0x80 | (cp >> 12); + buf[2] = 0x80 | ((cp >> 6) & 0x3f); + buf[3] = 0x80 | (cp & 0x3f); + switch (try_decode(buf, 4)) { + case KEKSUTF8Accept: + ok(false, "accepted overly-long (4) U+%06lx", cp); + break; + case KEKSUTF8Reject: + break; + default: + ok(false, "incomplete U+%06lx", cp); + break; + } + } + + ok(true, "dummy"); + return TAPStatus(); +}