#pragma once #include #include #include #include #include #include #include #include "musttail.h" #include "preserve_none.h" #include "simd.h" #include "tables.h" #include "weaseljson.h" namespace parser3 { typedef PRESERVE_NONE WeaselJsonStatus (*Continuation)(struct Parser3 *, char *buf, char *bufEnd); // These appear in the stack of the pushdown // automata enum Symbol : uint8_t { N_VALUE, N_OBJECT2, N_OBJECT3, N_ARRAY2, N_ARRAY3, N_STRING, N_STRING2, N_STRING_FOLLOWING_ESCAPE, N_WHITESPACE, N_NUMBER, N_TRUE, N_FALSE, N_NULL, T_R, T_U, // u inside of a string T_U2, T_A, T_L, T_S, T_COLON, T_HEX, T_HEX2, T_HEX3, T_EOF, T_BACKSLASH, N_SYMBOL_COUNT, // Must be last }; struct Parser3 { Parser3(const WeaselJsonCallbacks *callbacks, void *userdata, int stackSize) : callbacks(callbacks), userdata(userdata), stackSize(stackSize) { reset(); } [[nodiscard]] WeaselJsonStatus parse(char *buf, int len) { complete = len == 0; this->dataBegin = this->writeBuf = buf; return keepGoing(this, buf, buf + len); } void flushNumber(bool done, char *buf) { int len = buf - dataBegin; assert(len >= 0); if (done || len > 0) { callbacks->on_number_data(userdata, dataBegin, len, done); } } void flushString(bool done) { int len = writeBuf - dataBegin; assert(len >= 0); if (done || len > 0) { callbacks->on_string_data(userdata, dataBegin, len, done); } } [[nodiscard]] bool empty() const { return stackPtr == stack(); } void pop() { assert(!empty()); --stackPtr; } [[nodiscard]] WeaselJsonStatus push(std::initializer_list symbols) { if (stackPtr >= stack() + stackSize - symbols.size()) [[unlikely]] { return WeaselJson_OVERFLOW; } for (int i = symbols.size() - 1; i >= 0; --i) { *stackPtr++ = *(symbols.begin() + i); } return WeaselJson_OK; } [[nodiscard]] Symbol top() const { assert(!empty()); return *(stackPtr - 1); } static PRESERVE_NONE WeaselJsonStatus keepGoing(Parser3 *self, char *buf, char *bufEnd); Symbol *stack() const { return (Symbol *)(this + 1); } void reset() { stackPtr = stack(); complete = false; std::ignore = push({N_VALUE, N_WHITESPACE, T_EOF}); } // Used for flushing pending data with on_*_data callbacks char *dataBegin; // Used for unescaping string data in place char *writeBuf; WeaselJsonCallbacks const *const callbacks; void *const userdata; Symbol *stackPtr; uint32_t utf8Codepoint; uint32_t utf16Surrogate; uint32_t minCodepoint; int const stackSize; bool complete; NumDfa numDfa; Utf8Dfa strDfa; }; inline PRESERVE_NONE WeaselJsonStatus skipWhitespace(char *&buf, char *bufEnd) { constexpr int kStride = 4; for (;;) { if (bufEnd - buf < kStride) [[unlikely]] { while (buf != bufEnd && tables.whitespace[uint8_t(*buf)]) { ++buf; } return buf == bufEnd ? WeaselJson_AGAIN : WeaselJson_OK; } for (int i = 0; i < kStride; ++i) { if (tables.whitespace[uint8_t(*buf)]) { ++buf; } else { return WeaselJson_OK; } } } } inline PRESERVE_NONE WeaselJsonStatus n_whitespace(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) { self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf, char *bufEnd) { buf = (char *)self->numDfa.scan(buf, bufEnd); if (buf == bufEnd && !self->complete) { self->flushNumber(false, buf); return WeaselJson_AGAIN; } if (!self->numDfa.accept()) [[unlikely]] { return WeaselJson_REJECT; } self->flushNumber(true, buf); self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } // Advance buf until double quote, backslash, invalid utf8, or codepoint < // 0x20 template inline PRESERVE_NONE WeaselJsonStatus scan_string_impl(Parser3 *self, char *&buf, char *bufEnd) { const auto before = buf; // Advance buf past characters that transition the accept state to itself if (self->strDfa.accept()) { for (;;) { if (bufEnd - buf < V::lanes) [[unlikely]] { break; } auto v = V{(int8_t *)buf}; int normal = (v != V::splat('"') & v != V::splat('\\') & v >= V::splat(0x20)) .count_leading_nonzero_lanes(); buf += normal; if (normal < V::lanes) { break; } } } buf = (char *)self->strDfa.scan(buf, bufEnd); int len = buf - before; if (self->writeBuf != before) { memmove(self->writeBuf, before, len); } self->writeBuf += len; if (buf == bufEnd) { self->flushString(false); return WeaselJson_AGAIN; } if (!self->strDfa.accept()) [[unlikely]] { return WeaselJson_REJECT; } return WeaselJson_OK; } #ifdef __x86_64__ constexpr int kLanes = 32; template WeaselJsonStatus scan_string_impl>(Parser3 *, char *&, char *); template __attribute__((target("avx2"))) WeaselJsonStatus scan_string_impl>(Parser3 *, char *&, char *); __attribute__((target("default"))) inline PRESERVE_NONE WeaselJsonStatus scan_string(Parser3 *self, char *&buf, char *bufEnd) { MUSTTAIL return scan_string_impl>( self, buf, bufEnd); } __attribute__((target("avx2"))) inline PRESERVE_NONE WeaselJsonStatus scan_string(Parser3 *self, char *&buf, char *bufEnd) { MUSTTAIL return scan_string_impl>( self, buf, bufEnd); } #else inline PRESERVE_NONE WeaselJsonStatus scan_string(Parser3 *self, char *buf, char *bufEnd) { MUSTTAIL return scan_string_impl>(self, buf, bufEnd); } #endif inline PRESERVE_NONE WeaselJsonStatus n_value(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } switch (*buf) { case '{': self->callbacks->on_begin_object(self->userdata); ++buf; self->pop(); if (auto s = self->push({N_OBJECT2})) { return s; } break; case '[': self->callbacks->on_begin_array(self->userdata); ++buf; self->pop(); if (auto s = self->push({N_ARRAY2})) { return s; } break; case '"': ++buf; self->dataBegin = self->writeBuf = buf; self->pop(); self->strDfa.reset(); if (auto s2 = self->push({N_STRING2})) { return s2; } break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '-': self->dataBegin = buf; self->pop(); self->numDfa.reset(); if (auto s2 = self->push({N_NUMBER})) { return s2; } break; case 't': ++buf; self->pop(); if (bufEnd - buf >= 3) { if (memcmp(buf, "rue", 3) == 0) { self->callbacks->on_true_literal(self->userdata); buf += 3; } else [[unlikely]] { return WeaselJson_REJECT; } } else { if (auto s = self->push({T_R, T_U, N_TRUE})) { return s; } } break; case 'f': ++buf; self->pop(); if (bufEnd - buf >= 4) { if (memcmp(buf, "alse", 4) == 0) { self->callbacks->on_false_literal(self->userdata); buf += 4; } else [[unlikely]] { return WeaselJson_REJECT; } } else { if (auto s = self->push({T_A, T_L, T_S, N_FALSE})) { return s; } } break; case 'n': ++buf; self->pop(); if (bufEnd - buf >= 3) { if (memcmp(buf, "ull", 3) == 0) { self->callbacks->on_null_literal(self->userdata); buf += 3; } else [[unlikely]] { return WeaselJson_REJECT; } } else { if (auto s = self->push({T_U, T_L, N_NULL})) { return s; } } break; default: [[unlikely]] return WeaselJson_REJECT; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } switch (*buf) { case '}': ++buf; self->pop(); self->callbacks->on_end_object(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); case '"': ++buf; self->dataBegin = self->writeBuf = buf; self->pop(); self->strDfa.reset(); if (auto s = self->push({N_STRING2, T_COLON, N_VALUE, N_OBJECT3})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); default: [[unlikely]] return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_object3(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } switch (*buf) { case '}': ++buf; self->pop(); self->callbacks->on_end_object(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); case ',': ++buf; self->pop(); if (auto s = self->push({N_STRING, T_COLON, N_VALUE, N_OBJECT3})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); default: [[unlikely]] return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } switch (*buf) { case ']': ++buf; self->pop(); self->callbacks->on_end_array(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); default: self->pop(); if (auto s = self->push({N_VALUE, N_ARRAY3})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } } inline PRESERVE_NONE WeaselJsonStatus n_array3(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } switch (*buf) { case ']': ++buf; self->pop(); self->callbacks->on_end_array(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); case ',': ++buf; self->pop(); if (auto s = self->push({N_VALUE, N_ARRAY3})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); default: [[unlikely]] return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_string(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = skipWhitespace(buf, bufEnd)) { return s; } if (*buf != '"') [[unlikely]] { return WeaselJson_REJECT; } ++buf; self->dataBegin = self->writeBuf = buf; self->pop(); self->strDfa.reset(); if (auto s = self->push({N_STRING2})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline int32_t read4_hex(const char *buf) { return tables.hex[uint8_t(buf[0])] << 12 | tables.hex[uint8_t(buf[1])] << 8 | tables.hex[uint8_t(buf[2])] << 4 | tables.hex[uint8_t(buf[3])] << 0; } inline PRESERVE_NONE WeaselJsonStatus n_string2(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (auto s = scan_string(self, buf, bufEnd)) { return s; } switch (*buf) { case '"': self->flushString(true); ++buf; self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); case '\\': ++buf; if (bufEnd - buf < /*strlen("u0000\\u0000")*/ 11) { if (auto s = self->push({N_STRING_FOLLOWING_ESCAPE})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } else { if (*buf == 'u') { ++buf; int32_t codepoint = read4_hex(buf); if (codepoint < 0) [[unlikely]] { return WeaselJson_REJECT; } buf += 4; if (0xd800 <= codepoint && codepoint <= 0xdfff) { // utf-16 surrogate int32_t codepoint2 = read4_hex(buf + 2); if (!(buf[0] == '\\' && buf[1] == 'u' && 0xdc00 <= codepoint2 && codepoint2 <= 0xdfff)) [[unlikely]] { return WeaselJson_REJECT; } codepoint = 0x10000 + (codepoint - 0xd800) * 0x400 + (codepoint2 - 0xdc00); assert(codepoint >= 0x10000); if (codepoint > 0x10FFFF) [[unlikely]] { return WeaselJson_REJECT; } buf += 6; assert(codepoint < 0x10ffff); self->writeBuf[3] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[2] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[1] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[0] = (0b00000111 & codepoint) | 0b11110000; self->writeBuf += 4; } else { if (codepoint < 0x80) { *self->writeBuf++ = codepoint; } else if (codepoint < 0x800) { self->writeBuf[1] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[0] = (0b00011111 & codepoint) | 0b11000000; self->writeBuf += 2; } else { assert(codepoint < 0x10000); self->writeBuf[2] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[1] = (0b00111111 & codepoint) | 0b10000000; codepoint >>= 6; self->writeBuf[0] = (0b00001111 & codepoint) | 0b11100000; self->writeBuf += 3; } } } else { auto unescaped = tables.unescape[uint8_t(*buf++)]; if (unescaped == 0) [[unlikely]] { return WeaselJson_REJECT; } *self->writeBuf++ = unescaped; } MUSTTAIL return n_string2(self, buf, bufEnd); } default: [[unlikely]] return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_string_following_escape(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } assert(self->strDfa.accept()); switch (*buf) { case '"': case '\\': case '/': case 'b': case 'f': case 'n': case 'r': case 't': *self->writeBuf++ = tables.unescape[uint8_t(*buf++)]; self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); case 'u': ++buf; self->utf8Codepoint = 0; self->pop(); if (auto s = self->push({T_HEX, T_HEX, T_HEX, T_HEX2})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); default: [[unlikely]] return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus t_hex(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } auto hexVal = tables.hex[uint8_t(*buf)]; if (hexVal < 0) [[unlikely]] { return WeaselJson_REJECT; } self->utf8Codepoint <<= 4; self->utf8Codepoint |= hexVal; ++buf; self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline PRESERVE_NONE WeaselJsonStatus t_hex2(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } auto hexVal = tables.hex[uint8_t(*buf)]; if (hexVal < 0) [[unlikely]] { return WeaselJson_REJECT; } self->utf8Codepoint <<= 4; self->utf8Codepoint |= hexVal; ++buf; // Write codepoint in utf-8 if there's room in the user provided buffer. If // there's not room, flush, write into a temp buffer, and flush again. char tmp[3]; if (self->utf8Codepoint < 0x80) { assert(buf - self->writeBuf >= 1); *self->writeBuf++ = self->utf8Codepoint; } else if (self->utf8Codepoint < 0x800) { bool useTmp = buf - self->writeBuf < 2; char *p = tmp; if (useTmp) [[unlikely]] { self->flushString(false); } auto &w = useTmp ? p : self->writeBuf; w[1] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[0] = (0b00011111 & self->utf8Codepoint) | 0b11000000; w += 2; if (useTmp) [[unlikely]] { self->callbacks->on_string_data(self->userdata, tmp, 2, false); } } else { assert(self->utf8Codepoint < 0x10000); if (0xd800 <= self->utf8Codepoint && self->utf8Codepoint <= 0xdfff) { // utf-16 surrogate self->utf16Surrogate = self->utf8Codepoint; self->utf8Codepoint = 0; self->pop(); if (auto s = self->push({T_BACKSLASH, T_U2, T_HEX, T_HEX, T_HEX, T_HEX3})) { return s; } MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } bool useTmp = buf - self->writeBuf < 3; char *p = tmp; if (useTmp) [[unlikely]] { self->flushString(false); } auto &w = useTmp ? p : self->writeBuf; w[2] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[1] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[0] = (0b00001111 & self->utf8Codepoint) | 0b11100000; w += 3; if (useTmp) [[unlikely]] { self->callbacks->on_string_data(self->userdata, tmp, 3, false); } } self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline PRESERVE_NONE WeaselJsonStatus t_hex3(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } auto hexVal = tables.hex[uint8_t(*buf)]; if (hexVal < 0) [[unlikely]] { return WeaselJson_REJECT; } self->utf8Codepoint <<= 4; self->utf8Codepoint |= hexVal; ++buf; if (!(0xdc00 <= self->utf8Codepoint && self->utf8Codepoint <= 0xdfff)) [[unlikely]] { return WeaselJson_REJECT; } // Decode utf16 surrogate pair self->utf8Codepoint = 0x10000 + (self->utf16Surrogate - 0xd800) * 0x400 + (self->utf8Codepoint - 0xdc00); // Write codepoint in utf-8 if there's room in the user provided buffer. If // there's not room, flush, write into a temp buffer, and flush again. char tmp[4]; assert(self->utf8Codepoint >= 0x10000); if (self->utf8Codepoint > 0x10FFFF) [[unlikely]] { return WeaselJson_REJECT; } bool useTmp = buf - self->writeBuf < 4; char *p = tmp; if (useTmp) [[unlikely]] { self->flushString(false); } auto &w = useTmp ? p : self->writeBuf; w[3] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[2] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[1] = (0b00111111 & self->utf8Codepoint) | 0b10000000; self->utf8Codepoint >>= 6; w[0] = (0b00000111 & self->utf8Codepoint) | 0b11110000; w += 4; if (useTmp) [[unlikely]] { self->callbacks->on_string_data(self->userdata, tmp, 4, false); } self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } inline PRESERVE_NONE WeaselJsonStatus n_true(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (*buf == 'e') { ++buf; self->pop(); self->callbacks->on_true_literal(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } else [[unlikely]] { return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_false(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (*buf == 'e') { ++buf; self->pop(); self->callbacks->on_false_literal(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } else [[unlikely]] { return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus n_null(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if (*buf == 'l') { ++buf; self->pop(); self->callbacks->on_null_literal(self->userdata); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } else [[unlikely]] { return WeaselJson_REJECT; } } template inline PRESERVE_NONE WeaselJsonStatus singleChar(Parser3 *self, char *buf, char *bufEnd) { if (buf == bufEnd) [[unlikely]] { return WeaselJson_REJECT; } if constexpr (kSkipWhitespace) { if (auto s = skipWhitespace(buf, bufEnd)) { return s; } } if (*buf == kChar) { ++buf; self->pop(); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); } else [[unlikely]] { return WeaselJson_REJECT; } } inline PRESERVE_NONE WeaselJsonStatus t_eof(Parser3 *self, char *buf, char *bufEnd) { if (buf != bufEnd) [[unlikely]] { return WeaselJson_REJECT; } return self->complete ? WeaselJson_OK : WeaselJson_AGAIN; } constexpr inline struct ContinuationTable { constexpr ContinuationTable() { // Defaults for (int i = 0; i < N_SYMBOL_COUNT; ++i) { continuations[i] = +[](struct Parser3 *, char *, char *) PRESERVE_NONE { printf("unimplemented\n"); return WeaselJson_REJECT; }; } continuations[N_VALUE] = n_value; continuations[N_OBJECT2] = n_object2; continuations[N_OBJECT3] = n_object3; continuations[N_ARRAY2] = n_array2; continuations[N_ARRAY3] = n_array3; continuations[N_STRING] = n_string; continuations[N_STRING2] = n_string2; continuations[N_STRING_FOLLOWING_ESCAPE] = n_string_following_escape; continuations[N_WHITESPACE] = n_whitespace; continuations[N_NUMBER] = n_number; continuations[N_TRUE] = n_true; continuations[N_FALSE] = n_false; continuations[N_NULL] = n_null; continuations[T_R] = singleChar<'r'>; continuations[T_U] = singleChar<'u'>; continuations[T_U2] = singleChar<'u'>; continuations[T_A] = singleChar<'a'>; continuations[T_L] = singleChar<'l'>; continuations[T_S] = singleChar<'s'>; continuations[T_COLON] = singleChar<':', true>; continuations[T_HEX] = t_hex; continuations[T_HEX2] = t_hex2; continuations[T_HEX3] = t_hex3; continuations[T_EOF] = t_eof; continuations[T_BACKSLASH] = singleChar<'\\'>; symbolNames[N_VALUE] = "n_value"; symbolNames[N_OBJECT2] = "n_object2"; symbolNames[N_OBJECT3] = "n_object3"; symbolNames[N_ARRAY2] = "n_array2"; symbolNames[N_ARRAY3] = "n_array3"; symbolNames[N_STRING] = "n_string"; symbolNames[N_STRING2] = "n_string2"; symbolNames[N_STRING_FOLLOWING_ESCAPE] = "n_string_following_escape"; symbolNames[N_WHITESPACE] = "n_whitespace"; symbolNames[N_NUMBER] = "n_number"; symbolNames[N_TRUE] = "n_true"; symbolNames[N_FALSE] = "n_false"; symbolNames[N_NULL] = "n_null"; symbolNames[T_R] = "singleChar<'r'>"; symbolNames[T_U] = "singleChar<'u'>"; symbolNames[T_U2] = "singleChar<'u'> (in string)"; symbolNames[T_A] = "singleChar<'a'>"; symbolNames[T_L] = "singleChar<'l'>"; symbolNames[T_S] = "singleChar<'s'>"; symbolNames[T_COLON] = "singleChar<':'>"; symbolNames[T_HEX] = "t_hex"; symbolNames[T_HEX2] = "t_hex2"; symbolNames[T_HEX3] = "t_hex3"; symbolNames[T_EOF] = "t_eof"; symbolNames[T_BACKSLASH] = "singleChar<'\\'>"; } Continuation continuations[N_SYMBOL_COUNT]{}; const char *symbolNames[N_SYMBOL_COUNT]{}; } symbolTables; inline PRESERVE_NONE WeaselJsonStatus Parser3::keepGoing(Parser3 *self, char *buf, char *bufEnd) { if (bufEnd - buf == 0) { if (!self->complete) { switch (self->top()) { case N_STRING2: case N_STRING_FOLLOWING_ESCAPE: case T_HEX: case T_HEX2: case T_HEX3: case T_BACKSLASH: case T_U2: self->flushString(false); break; case N_STRING: // The beginning of the string is in the future in this // state. There's no data to flush yet case N_VALUE: case N_OBJECT2: case N_OBJECT3: case N_ARRAY2: case N_ARRAY3: case N_WHITESPACE: case N_NUMBER: case N_TRUE: case N_FALSE: case N_NULL: case T_R: case T_U: case T_A: case T_L: case T_S: case T_COLON: case T_EOF: case N_SYMBOL_COUNT: break; default: __builtin_unreachable(); } return WeaselJson_AGAIN; } } // printf("%s\n", symbolTables.symbolNames[self->top()]); MUSTTAIL return symbolTables.continuations[self->top()](self, buf, bufEnd); } } // namespace parser3