Merge pull request 'Make parser reject state sticky after WeaselJson_REJECT' (#15) from weaselbot/weaseljson:weaselbot/issue-12 into main

Reviewed-on: weaselab/weaseljson#15
This commit is contained in:
2026-06-19 17:17:10 +00:00
2 changed files with 40 additions and 2 deletions
+15 -2
View File
@@ -139,6 +139,7 @@ struct Parser3 {
stackPtr = stack(); stackPtr = stack();
std::ignore = push({N_VALUE, N_WHITESPACE, T_EOF}); std::ignore = push({N_VALUE, N_WHITESPACE, T_EOF});
inKey = false; inKey = false;
rejected = false;
utf8Codepoint = 0; utf8Codepoint = 0;
utf16Surrogate = 0; utf16Surrogate = 0;
minCodepoint = 0; minCodepoint = 0;
@@ -161,6 +162,7 @@ struct Parser3 {
NumDfa numDfa; NumDfa numDfa;
Utf8Dfa strDfa; Utf8Dfa strDfa;
bool inKey = false; bool inKey = false;
bool rejected = false;
#ifndef HAS_MUSTTAIL #ifndef HAS_MUSTTAIL
char *stashBufForTrampoline; char *stashBufForTrampoline;
@@ -1062,18 +1064,29 @@ constexpr inline struct ContinuationTable {
inline WeaselJsonStatus Parser3::parse(char *buf, int len) { inline WeaselJsonStatus Parser3::parse(char *buf, int len) {
this->dataBegin = this->writeBuf = buf; this->dataBegin = this->writeBuf = buf;
if (this->rejected) [[unlikely]] {
return WeaselJson_REJECT;
}
#ifdef HAS_MUSTTAIL #ifdef HAS_MUSTTAIL
// The continuation returns a value in 0..3 here (kBounce is only used by the // 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 // no-musttail trampoline below), so the conversion back to the enum is in
// range. // range.
return WeaselJsonStatus( ContinuationStatus status =
symbolTables.continuations[top()](this, buf, buf + len)); symbolTables.continuations[top()](this, buf, buf + len);
if (status == WeaselJson_REJECT) {
this->rejected = true;
}
return WeaselJsonStatus(status);
#else #else
this->stashBufForTrampoline = buf; this->stashBufForTrampoline = buf;
ContinuationStatus result; ContinuationStatus result;
while ((result = symbolTables.continuations[top()]( while ((result = symbolTables.continuations[top()](
this, stashBufForTrampoline, buf + len)) == kBounce) this, stashBufForTrampoline, buf + len)) == kBounce)
; ;
if (result == WeaselJson_REJECT) {
this->rejected = true;
}
return WeaselJsonStatus(result); return WeaselJsonStatus(result);
#endif #endif
} }
+25
View File
@@ -202,6 +202,31 @@ TEST_CASE("parser3") {
} }
} }
TEST_CASE("rejected state is sticky") {
auto c = noopCallbacks();
auto *parser = WeaselJsonParser_create(1024, &c, nullptr, 0);
REQUIRE(parser != nullptr);
// Feed a valid prefix, then an invalid byte. The parser must reject.
std::string chunk1 = "1";
REQUIRE(WeaselJsonParser_parse(parser, chunk1.data(), chunk1.size()) ==
WeaselJson_AGAIN);
std::string chunk2 = "x";
REQUIRE(WeaselJsonParser_parse(parser, chunk2.data(), chunk2.size()) ==
WeaselJson_REJECT);
// After a reject, every later call must also reject, including the EOF
// finish call that would otherwise return OK.
REQUIRE(WeaselJsonParser_parse(parser, nullptr, 0) == WeaselJson_REJECT);
// Further data chunks must also stay rejected.
std::string chunk3 = " ";
REQUIRE(WeaselJsonParser_parse(parser, chunk3.data(), chunk3.size()) ==
WeaselJson_REJECT);
WeaselJsonParser_destroy(parser);
}
TEST_CASE("create rejects too-small stack") { TEST_CASE("create rejects too-small stack") {
auto c = noopCallbacks(); auto c = noopCallbacks();
// The parser needs room for the bootstrap symbols pushed by reset(); a stack // The parser needs room for the bootstrap symbols pushed by reset(); a stack