Hand-rolled JSON byte scanner. Resolves a PathStep list against a JSON document in an Array[Byte] — the whole array, or a range [from, until) of it — and returns the byte span [start, end) of the resolved value, or -1 for the start if the path doesn't match. Every helper threads the range's until as its bound, so a ranged scan never reads past it.
Why hand-rolled rather than reusing jsoniter-scala-core's JsonReader: the scanner only needs to skip-with-backtracking, not decode. Embedding JsonReader would force us to thread its mark / rollback state through every call site, and its API doesn't expose byte offsets directly. 100-ish LoC of dispatch is cheaper than the impedance mismatch.
The scanner is permissive about whitespace (skips it everywhere) and strict about structure (unbalanced quotes / brackets short-circuit to "miss"). It does NOT validate the document; an invalid JSON whose path step happens to resolve will return a span — decoding is the layer that surfaces validity failures.
Resolve path against bytes. Returns the byte span of the focused value (start, end), or Miss if any step fails to match. Rejects PathStep.Wildcard — wildcards produce 0-or-many results and don't fit a single-Span return; route them through findAll. The whole-array case of the ranged overload below.
Resolve path against bytes. Returns the byte span of the focused value (start, end), or Miss if any step fails to match. Rejects PathStep.Wildcard — wildcards produce 0-or-many results and don't fit a single-Span return; route them through findAll. The whole-array case of the ranged overload below.
Resolve path against the document in bytes[from, until), reading nothing outside that range: a value that runs past until is malformed, exactly as one that runs past the end of an array of its own. Spans are absolute positions in bytes. The caller guarantees 0 <= from <= until <= bytes.length (JsonSlice checks it once, at construction).
Resolve path against the document in bytes[from, until), reading nothing outside that range: a value that runs past until is malformed, exactly as one that runs past the end of an array of its own. Spans are absolute positions in bytes. The caller guarantees 0 <= from <= until <= bytes.length (JsonSlice checks it once, at construction).
Resolve path against bytes, returning every span that matches. Wildcard steps fan out across array elements; a path with no wildcards returns 0 or 1 spans. Spans are returned in document order. On any structural mismatch (wrong context for a step, malformed JSON), the sub-tree is silently skipped — the caller sees fewer spans, not an error. The whole-array case of the ranged overload below.
Resolve path against bytes, returning every span that matches. Wildcard steps fan out across array elements; a path with no wildcards returns 0 or 1 spans. Spans are returned in document order. On any structural mismatch (wrong context for a step, malformed JSON), the sub-tree is silently skipped — the caller sees fewer spans, not an error. The whole-array case of the ranged overload below.
findAll over the document in bytes[from, until), reading nothing outside that range. Spans are absolute positions in bytes; same precondition on the range as the ranged find.
findAll over the document in bytes[from, until), reading nothing outside that range. Spans are absolute positions in bytes; same precondition on the range as the ranged find.