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
+90 -76
View File
@@ -17,20 +17,29 @@
namespace parser3 {
// Calling a continuation with buf == bufEnd means end of input
typedef PRESERVE_NONE WeaselJsonStatus (*Continuation)(struct Parser3 *,
char *buf, char *bufEnd);
// The internal result of a parse step: a WeaselJsonStatus value, or kBounce.
// It's a plain int (not WeaselJsonStatus) so the no-musttail trampoline's
// 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,
char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_string2(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_string(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf,
char *bufEnd);
// Calling a continuation with buf == bufEnd means end of input
typedef PRESERVE_NONE ContinuationStatus (*Continuation)(struct Parser3 *,
char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_object2(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_array2(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_string2(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_string(Parser3 *self, char *buf,
char *bufEnd);
inline PRESERVE_NONE ContinuationStatus n_number(Parser3 *self, char *buf,
char *bufEnd);
// These appear in the stack of the pushdown
// automata
@@ -121,8 +130,8 @@ struct Parser3 {
return *(stackPtr - 1);
}
static PRESERVE_NONE WeaselJsonStatus keepGoing(Parser3 *self, char *buf,
char *bufEnd);
static PRESERVE_NONE ContinuationStatus keepGoing(Parser3 *self, char *buf,
char *bufEnd);
Symbol *stack() const { return (Symbol *)(this + 1); }
@@ -152,7 +161,8 @@ struct Parser3 {
#endif
};
inline PRESERVE_NONE WeaselJsonStatus skipWhitespace(char *&buf, char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus skipWhitespace(char *&buf,
char *bufEnd) {
constexpr int kStride = 4;
for (;;) {
if (bufEnd - buf < kStride) [[unlikely]] {
@@ -171,8 +181,8 @@ inline PRESERVE_NONE WeaselJsonStatus skipWhitespace(char *&buf, char *bufEnd) {
}
}
inline PRESERVE_NONE WeaselJsonStatus n_whitespace(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_whitespace(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) {
self->pop();
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
@@ -184,8 +194,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_whitespace(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
}
inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_number(Parser3 *self, char *buf,
char *bufEnd) {
if (buf != bufEnd) {
buf = (char *)self->numDfa.scan(buf, bufEnd);
if (buf == bufEnd) {
@@ -204,9 +214,9 @@ inline PRESERVE_NONE WeaselJsonStatus n_number(Parser3 *self, char *buf,
// Advance buf until double quote, backslash, invalid utf8, or codepoint <
// 0x20
template <class V>
inline PRESERVE_NONE WeaselJsonStatus scan_string_impl(Parser3 *self,
char *&buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus scan_string_impl(Parser3 *self,
char *&buf,
char *bufEnd) {
const auto before = buf;
// Advance buf past characters that transition the accept state to itself
@@ -253,34 +263,34 @@ inline PRESERVE_NONE WeaselJsonStatus scan_string_impl(Parser3 *self,
#ifdef __x86_64__
constexpr int kLanes = 32;
template WeaselJsonStatus
template ContinuationStatus
scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>(Parser3 *, 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 *&,
char *);
__attribute__((target("default"))) inline PRESERVE_NONE WeaselJsonStatus
__attribute__((target("default"))) inline PRESERVE_NONE ContinuationStatus
scan_string(Parser3 *self, char *&buf, char *bufEnd) {
MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_SSE>>(
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) {
MUSTTAIL return scan_string_impl<simd<int8_t, kLanes, sse::Simd_x86_AVX2>>(
self, buf, bufEnd);
}
#else
inline PRESERVE_NONE WeaselJsonStatus scan_string(Parser3 *self, char *&buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus scan_string(Parser3 *self, char *&buf,
char *bufEnd) {
MUSTTAIL return scan_string_impl<simd<int8_t, 32>>(self, buf, bufEnd);
}
#endif
inline PRESERVE_NONE WeaselJsonStatus n_value(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_value(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -392,8 +402,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_value(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
}
inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_object2(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -428,8 +438,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_object2(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_object3(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_object3(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -474,8 +484,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_object3(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_array2(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -500,8 +510,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_array2(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_array3(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_array3(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -532,8 +542,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_array3(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_string(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_string(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -559,8 +569,8 @@ inline int32_t read4_hex(const char *buf) {
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) {
inline PRESERVE_NONE ContinuationStatus n_string2(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
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,
char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus
n_string_following_escape(Parser3 *self, char *buf, char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -701,8 +710,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_string_following_escape(Parser3 *self,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
}
inline PRESERVE_NONE WeaselJsonStatus t_hex(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus t_hex(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -721,8 +730,8 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
}
inline PRESERVE_NONE WeaselJsonStatus t_hex2(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus t_hex2(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -810,8 +819,8 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex2(Parser3 *self, char *buf,
MUSTTAIL return Parser3::keepGoing(self, buf, bufEnd);
}
inline PRESERVE_NONE WeaselJsonStatus t_hex3(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus t_hex3(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -872,9 +881,9 @@ inline PRESERVE_NONE WeaselJsonStatus t_hex3(Parser3 *self, char *buf,
}
template <char kChar>
inline PRESERVE_NONE WeaselJsonStatus singleCharInString(Parser3 *self,
char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus singleCharInString(Parser3 *self,
char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -891,8 +900,8 @@ inline PRESERVE_NONE WeaselJsonStatus singleCharInString(Parser3 *self,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_true(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_true(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -909,8 +918,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_true(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_false(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_false(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -927,8 +936,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_false(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus n_null(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus n_null(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -946,8 +955,8 @@ inline PRESERVE_NONE WeaselJsonStatus n_null(Parser3 *self, char *buf,
}
template <char kChar>
inline PRESERVE_NONE WeaselJsonStatus singleChar(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus singleChar(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -963,8 +972,8 @@ inline PRESERVE_NONE WeaselJsonStatus singleChar(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus t_colon(Parser3 *self, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus t_colon(Parser3 *self, char *buf,
char *bufEnd) {
if (buf == bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -985,8 +994,8 @@ inline PRESERVE_NONE WeaselJsonStatus t_colon(Parser3 *self, char *buf,
}
}
inline PRESERVE_NONE WeaselJsonStatus t_eof(Parser3 *, char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus t_eof(Parser3 *, char *buf,
char *bufEnd) {
if (buf != bufEnd) [[unlikely]] {
return WeaselJson_REJECT;
}
@@ -997,7 +1006,8 @@ constexpr inline struct ContinuationTable {
constexpr ContinuationTable() {
// Defaults
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");
return WeaselJson_REJECT;
};
@@ -1035,26 +1045,30 @@ inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
this->dataBegin = this->writeBuf = buf;
#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
this->stashBufForTrampoline = buf;
WeaselJsonStatus result;
ContinuationStatus result;
while ((result = symbolTables.continuations[top()](
this, stashBufForTrampoline, buf + len)) == WeaselJsonStatus(-1))
this, stashBufForTrampoline, buf + len)) == kBounce)
;
return result;
return WeaselJsonStatus(result);
#endif
}
inline PRESERVE_NONE WeaselJsonStatus Parser3::keepGoing(Parser3 *self,
char *buf,
char *bufEnd) {
inline PRESERVE_NONE ContinuationStatus Parser3::keepGoing(Parser3 *self,
char *buf,
char *bufEnd) {
#ifdef HAS_MUSTTAIL
MUSTTAIL return symbolTables.continuations[self->top()](self, buf, bufEnd);
#else
self->stashBufForTrampoline = buf;
(void)bufEnd;
return WeaselJsonStatus(-1);
return kBounce;
#endif
}