Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions include/sonic/internal/arch/simd_skip.h
Original file line number Diff line number Diff line change
Expand Up @@ -677,6 +677,16 @@ class SkipScanner2 {
JsonGeneratorInterface<serializeFlags> *jsonGenerator,
const JsonGeneratorFactory<serializeFlags> &jsonGeneratorFactory,
const int64_t idx) {
// On-demand scanning is forward-only, so a negative index cannot be
// resolved without knowing the array length up front. Report that
// explicitly: the loop below compares `cur_idx == idx` with cur_idx >= 0,
// which a negative idx never satisfies, so without this check the scan
// walks the whole array and the caller receives kErrorNoneNoMatch -- the
// same result as a key that genuinely is not there.
if (idx < 0) {
setError(SonicError::kUnsupportedJsonPath);
return false;
}
RETURN_FALSE_IF_PARSE_ERROR(consume('['));
int64_t cur_idx = 0;
bool dirty = false;
Expand Down
27 changes: 27 additions & 0 deletions tests/jsonpath_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,33 @@ TEST(JsonPathDom, NegativeIndexSupportedButOnDemandUnsupported) {
EXPECT_EQ(std::get<0>(ondemand_got), "");
}

TEST(JsonPathOnDemand, NegativeIndexReportsUnsupportedNotNoMatch) {
// A negative index is a feature this backend does not implement, which is
// not the same thing as the path being absent from the document. Reporting
// both as kErrorNoneNoMatch leaves callers unable to tell "sonic cannot
// evaluate this path" from "the document has no such member", so a path the
// DOM backend resolves fine looks merely missing here.
for (const char* path : {"$[-1]", "$[-3]", "$[-9]"}) {
auto got = GetByJsonPathOnDemand<kSerializeJavaStyleFlag>("[0,1,2]", path);
EXPECT_EQ(std::get<1>(got), kUnsupportedJsonPath) << "path: " << path;
EXPECT_EQ(std::get<0>(got), "") << "path: " << path;
}

// Nested arrays reach the same code through a different call site.
auto nested = GetByJsonPathOnDemand<kSerializeJavaStyleFlag>(
R"({"a":[10,11,12]})", "$.a[-1]");
EXPECT_EQ(std::get<1>(nested), kUnsupportedJsonPath);

// A genuinely absent member must keep reporting no-match, so the two
// outcomes stay distinguishable.
auto absent_key =
GetByJsonPathOnDemand<kSerializeJavaStyleFlag>(R"({"a":1})", "$.zz");
EXPECT_EQ(std::get<1>(absent_key), kErrorNoneNoMatch);
auto absent_index =
GetByJsonPathOnDemand<kSerializeJavaStyleFlag>("[0,1,2]", "$[9]");
EXPECT_EQ(std::get<1>(absent_index), kErrorNoneNoMatch);
}

TEST(JsonPathDump, SerializeCoversEmptySingleAndMulti) {
Document doc;
doc.Parse(R"({"a":null,"s":"abc","arr":[null,"x",1]})");
Expand Down