Import with #import "std/json_scan".

JsonSpan :: struct { start: s32; end: s32; after: s32; valid: bool; }

A small allocation-free JSON scanner. Positions are UTF-8 byte offsets. String spans retain escapes; member lookup matches unescaped ASCII names.

JsonNumber :: struct { value: float64; after: s32; valid: bool; }
JsonRange :: struct { start: s32; end: s32; }

Owned decoded bytes. The caller may consume value with BuilderFinish or let normal vector cleanup release it; valid distinguishes an empty string

JsonText :: struct { value: Vec(u8); valid: bool; }
Space :: (text: string, at: s32) -> s32
Digit :: (ch: u8) -> bool
Hex :: (ch: u8) -> bool
ReadString :: (text: string, at: s32) -> JsonSpan
HexValue :: (ch: u8) -> s32
ReadHex4 :: (text: string, at: s32, end: s32) -> s32

The four hex digits at `at` as a number, or -1.

AppendUtf8 :: (output: []u8, length: s32, codepoint: s32) -> s32

Appends codepoint as UTF-8, leaving room for a NUL. Returns the new length, or -1 when it does not fit.

ReadStringText :: (text: string, at: s32, output: []u8) -> s32

Copies the JSON string at `at` into output as UTF-8 with its escapes decoded, including \uXXXX surrogate pairs; a lone surrogate becomes U+FFFD. Returns the byte length, or -1 when the value is not a string or does not fit. Output is always NUL-terminated.

ReadStringValue :: (text: string, at: s32, max_bytes: s32) -> JsonText

Decode into owned storage without a maximum-size temporary buffer. Runs of ordinary UTF-8 append directly; escape fragments need at most four bytes. max_bytes bounds decoded bytes, not the serialized span (escapes shrink).

SpanEqualsASCII :: (text: string, span: JsonSpan, expected: string) -> bool
SpanContainsFoldASCII :: (text: string, span: JsonSpan, needle: string) -> bool
ReadNumber :: (text: string, at: s32) -> JsonNumber
MatchLiteral :: (text: string, at: s32, literal: string) -> bool
ValueEnd :: (text: string, at: s32, depth: s32) -> s32
FieldAt :: (text: string, object_at: s32, name: string) -> s32
ObjectPositions :: (text: string, object_at: s32, names: []string, output: []s32) -> bool

Index several unescaped ASCII member names in one validated pass. Missing members receive -1; duplicate keys keep their first value, like FieldAt. Positions borrow the current text and are usable only when this returns true. The output must have room for every requested name; extra cells are untouched.

ElementAt :: (text: string, array_at: s32, index: s32) -> s32
ArrayPositions :: (text: string, array_at: s32, output: []s32) -> s32

Counts and validates an array while recording the first output.count element offsets. Returns the complete count, even when output is shorter, or -1 for malformed input. Offsets are borrowed positions, never cached string storage; callers use them only after success and clamp access to their buffer length.

ArrayRanges :: (text: string, array_at: s32, output: []JsonRange) -> s32

Counts and validates the complete array, retaining the first output.count value ranges. Compare or reuse values only after a nonnegative return; ranges are byte offsets and do not own the underlying document.

ArrayRangesReuse :: (text: string, array_at: s32, output: []JsonRange, previous_text: string, previous_ranges: []JsonRange) -> s32

Like ArrayRanges, using ranges from a successful scan of previous_text. The caller must preserve those bytes and ranges until this call returns. Only complete, byte-identical values reuse validation; changed values and uncached trailing elements are parsed normally. Array delimiters are always checked, including when the output slice is empty or shorter than the array. Output must not alias previous_ranges, whose offsets belong to the old text.

WalkArray :: (text: string, array_at: s32, positions: []s32, ranges: []JsonRange, previous_text: string, previous_ranges: []JsonRange) -> s32
ArrayCount :: (text: string, array_at: s32) -> s32

Source ↗ · All modules