Return int (not WeaselJsonStatus) from internal continuations

The no-musttail trampoline used WeaselJsonStatus(-1) as a re-dispatch
sentinel, but WeaselJsonStatus has no fixed underlying type, so its valid
range is only [0,3]; forming -1 is undefined behavior in C++. A compiler
may assume the value is in range (e.g. -fstrict-enums) and fold the
trampoline's '== WeaselJsonStatus(-1)' check to false, breaking the loop.

Introduce an internal 'using ContinuationStatus = int' (a WeaselJsonStatus
value, or kBounce) for the Continuation typedef, every continuation/helper
function, and keepGoing. parse() converts back to WeaselJsonStatus only at
the public boundary, where the value is always 0..3. The public enum is
unchanged, so C consumers are unaffected.
This commit is contained in:
2026-06-15 00:34:29 -04:00
parent b3dac03f70
commit 8191df404d
+59 -45
View File
@@ -17,19 +17,28 @@
namespace parser3 { namespace parser3 {
// Calling a continuation with buf == bufEnd means end of input // The internal result of a parse step: a WeaselJsonStatus value, or kBounce.
typedef PRESERVE_NONE WeaselJsonStatus (*Continuation)(struct Parser3 *, // It's a plain int (not WeaselJsonStatus) so the no-musttail trampoline's
char *buf, char *bufEnd); // out-of-range sentinel never forms an out-of-range enum value (that would be
// UB, since the public WeaselJsonStatus has no fixed underlying type). parse()
// converts back to WeaselJsonStatus at the boundary, where it is always 0..3.
using ContinuationStatus = int;
[[maybe_unused]] inline constexpr ContinuationStatus kBounce = -1;
inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf, // Calling a continuation with buf == bufEnd means end of input
typedef PRESERVE_NONE ContinuationStatus (*Continuation)(struct Parser3 *,
char *buf,
char *bufEnd); char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf,
inline PRESERVE_NONE ContinuationStatus n_object2(Parser3 *self, char *buf,
char *bufEnd); char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_string2(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_array2(Parser3 *self, char *buf,
char *bufEnd); char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_string(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_string2(Parser3 *self, char *buf,
char *bufEnd); char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_string(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_number(Parser3 *self, char *buf,
char *bufEnd); char *bufEnd);
// These appear in the stack of the pushdown // These appear in the stack of the pushdown
@@ -121,7 +130,7 @@ struct Parser3 {
return *(stackPtr - 1); return *(stackPtr - 1);
} }
static PRESERVE_NONE WeaselJsonStatus keepGoing(Parser3 *self, char *buf, static PRESERVE_NONE ContinuationStatus keepGoing(Parser3 *self, char *buf,
char *bufEnd); char *bufEnd);
Symbol *stack() const { return (Symbol *)(this + 1); } Symbol *stack() const { return (Symbol *)(this + 1); }
@@ -152,7 +161,8 @@ struct Parser3 {
#endif #endif
}; };
inline PRESERVE_NONE WeaselJsonStatus skipWhitespace(char *&buf, char *bufEnd) { inline PRESERVE_NONE ContinuationStatus skipWhitespace(char *&buf,
char *bufEnd) {
constexpr int kStride = 4; constexpr int kStride = 4;
for (;;) { for (;;) {
if (bufEnd - buf < kStride) [[unlikely]] { if (bufEnd - buf < kStride) [[unlikely]] {
@@ -171,7 +181,7 @@ inline PRESERVE_NONE WeaselJsonStatus skipWhitespace(char *&buf, char *bufEnd) {
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_whitespace(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_whitespace(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) { if (buf == bufEnd) {
self->pop(); self->pop();
@@ -184,7 +194,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_whitespace(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
} }
inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_number(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf != bufEnd) { if (buf != bufEnd) {
buf = (char *)self->numDfa.scan(buf, bufEnd); buf = (char *)self->numDfa.scan(buf, bufEnd);
@@ -204,7 +214,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf,
// Advance buf until double quote, backslash, invalid utf8, or codepoint < // Advance buf until double quote, backslash, invalid utf8, or codepoint <
// 0x20 // 0x20
template <class V> template <class V>
inline PRESERVE_NONE WeaselJsonStatus scan_string_impl(Parser3 *self, inline PRESERVE_NONE ContinuationStatus scan_string_impl(Parser3 *self,
char *&buf, char *&buf,
char *bufEnd) { char *bufEnd) {
const auto before = buf; const auto before = buf;
@@ -253,33 +263,33 @@ inline PRESERVE_NONE WeaselJsonStatus scan_string_impl(Parser3 *self,
#ifdef __x86_64__ #ifdef __x86_64__
constexpr int kLanes = 32; constexpr int kLanes = 32;
template WeaselJsonStatus template ContinuationStatus
scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>(Parser3 *, char *&, scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>(Parser3 *, char *&,
char *); char *);
template __attribute__((target("avx2"))) WeaselJsonStatus template __attribute__((target("avx2"))) ContinuationStatus
scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_AVX2>>(Parser3 *, char *&, scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_AVX2>>(Parser3 *, char *&,
char *); char *);
__attribute__((target("default"))) inline PRESERVE_NONE WeaselJsonStatus __attribute__((target("default"))) inline PRESERVE_NONE ContinuationStatus
scan_string(Parser3 *self, char *&buf, char *bufEnd) { scan_string(Parser3 *self, char *&buf, char *bufEnd) {
MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>( MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>(
self, buf, bufEnd); self, buf, bufEnd);
} }
__attribute__((target("avx2"))) inline PRESERVE_NONE WeaselJsonStatus __attribute__((target("avx2"))) inline PRESERVE_NONE ContinuationStatus
scan_string(Parser3 *self, char *&buf, char *bufEnd) { scan_string(Parser3 *self, char *&buf, char *bufEnd) {
MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_AVX2>>( MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_AVX2>>(
self, buf, bufEnd); self, buf, bufEnd);
} }
#else #else
inline PRESERVE_NONE WeaselJsonStatus scan_string(Parser3 *self, char *&buf, inline PRESERVE_NONE ContinuationStatus scan_string(Parser3 *self, char *&buf,
char *bufEnd) { char *bufEnd) {
MUSTTAIL return scan_string_impl<simd<int8_t, 32>>(self, buf, bufEnd); MUSTTAIL return scan_string_impl<simd<int8_t, 32>>(self, buf, bufEnd);
} }
#endif #endif
inline PRESERVE_NONE WeaselJsonStatus n_value(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -392,7 +402,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_value(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
} }
inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_object2(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -428,7 +438,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_object3(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_object3(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -474,7 +484,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_object3(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_array2(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -500,7 +510,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_array3(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_array3(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -532,7 +542,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_array3(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_string(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_string(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -559,7 +569,7 @@ inline int32_t read4_hex(const char *buf) {
tables.hex[uint8_t(buf[2])] << 4 | tables.hex[uint8_t(buf[3])] << 0; tables.hex[uint8_t(buf[2])] << 4 | tables.hex[uint8_t(buf[3])] << 0;
} }
inline PRESERVE_NONE WeaselJsonStatus n_string2(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_string2(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -661,9 +671,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_string2(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_string_following_escape(Parser3 *self, inline PRESERVE_NONE ContinuationStatus
char *buf, n_string_following_escape(Parser3 *self, char *buf, char *bufEnd) {
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
} }
@@ -701,7 +710,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_string_following_escape(Parser3 *self,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
} }
inline PRESERVE_NONE WeaselJsonStatus t_hex(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus t_hex(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -721,7 +730,7 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
} }
inline PRESERVE_NONE WeaselJsonStatus t_hex2(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus t_hex2(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -810,7 +819,7 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex2(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd); MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
} }
inline PRESERVE_NONE WeaselJsonStatus t_hex3(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus t_hex3(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -872,7 +881,7 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex3(Parser3 *self, char *buf,
} }
template <char kChar> template <char kChar>
inline PRESERVE_NONE WeaselJsonStatus singleCharInString(Parser3 *self, inline PRESERVE_NONE ContinuationStatus singleCharInString(Parser3 *self,
char *buf, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
@@ -891,7 +900,7 @@ inline PRESERVE_NONE WeaselJsonStatus singleCharInString(Parser3 *self,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_true(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_true(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -909,7 +918,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_true(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_false(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_false(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -927,7 +936,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_false(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus n_null(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus n_null(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -946,7 +955,7 @@ inline PRESERVE_NONE WeaselJsonStatus n_null(Parser3 *self, char *buf,
} }
template <char kChar> template <char kChar>
inline PRESERVE_NONE WeaselJsonStatus singleChar(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus singleChar(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -963,7 +972,7 @@ inline PRESERVE_NONE WeaselJsonStatus singleChar(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus t_colon(Parser3 *self, char *buf, inline PRESERVE_NONE ContinuationStatus t_colon(Parser3 *self, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf == bufEnd) [[unlikely]] { if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -985,7 +994,7 @@ inline PRESERVE_NONE WeaselJsonStatus t_colon(Parser3 *self, char *buf,
} }
} }
inline PRESERVE_NONE WeaselJsonStatus t_eof(Parser3 *, char *buf, inline PRESERVE_NONE ContinuationStatus t_eof(Parser3 *, char *buf,
char *bufEnd) { char *bufEnd) {
if (buf != bufEnd) [[unlikely]] { if (buf != bufEnd) [[unlikely]] {
return WeaselJson_REJECT; return WeaselJson_REJECT;
@@ -997,7 +1006,8 @@ constexpr inline struct ContinuationTable {
constexpr ContinuationTable() { constexpr ContinuationTable() {
// Defaults // Defaults
for (int i = 0; i < N_SYMBOL_COUNT; ++i) { for (int i = 0; i < N_SYMBOL_COUNT; ++i) {
continuations[i] = +[](struct Parser3 *, char *, char *) PRESERVE_NONE { continuations[i] = +[](struct Parser3 *, char *, char *)
PRESERVE_NONE -> ContinuationStatus {
printf("unimplemented\n"); printf("unimplemented\n");
return WeaselJson_REJECT; return WeaselJson_REJECT;
}; };
@@ -1035,18 +1045,22 @@ inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
this->dataBegin = this->writeBuf = buf; this->dataBegin = this->writeBuf = buf;
#ifdef HAS_MUSTTAIL #ifdef HAS_MUSTTAIL
return symbolTables.continuations[top()](this, buf, buf + len); // The continuation returns a value in 0..3 here (kBounce is only used by the
// no-musttail trampoline below), so the conversion back to the enum is in
// range.
return WeaselJsonStatus(
symbolTables.continuations[top()](this, buf, buf + len));
#else #else
this->stashBufForTrampoline = buf; this->stashBufForTrampoline = buf;
WeaselJsonStatus result; ContinuationStatus result;
while ((result = symbolTables.continuations[top()]( while ((result = symbolTables.continuations[top()](
this, stashBufForTrampoline, buf + len)) == WeaselJsonStatus(-1)) this, stashBufForTrampoline, buf + len)) == kBounce)
; ;
return result; return WeaselJsonStatus(result);
#endif #endif
} }
inline PRESERVE_NONE WeaselJsonStatus Parser3::keepGoing(Parser3 *self, inline PRESERVE_NONE ContinuationStatus Parser3::keepGoing(Parser3 *self,
char *buf, char *buf,
char *bufEnd) { char *bufEnd) {
#ifdef HAS_MUSTTAIL #ifdef HAS_MUSTTAIL
@@ -1054,7 +1068,7 @@ inline PRESERVE_NONE WeaselJsonStatus Parser3::keepGoing(Parser3 *self,
#else #else
self->stashBufForTrampoline = buf; self->stashBufForTrampoline = buf;
(void)bufEnd; (void)bufEnd;
return WeaselJsonStatus(-1); return kBounce;
#endif #endif
} }