OptionalarrayStart of an array; count elements of kind follow.
OptionalarrayOffer of the bulk hand-off: return the destination this visitor has
already allocated for array id and the decoder fills it directly, one write
per element and no callback at all; return null (or leave this method
unimplemented) to be served element by element as before.
Called once per array, after arrayBegin and before the first element —
so a receiver cap on count is still compared where it always was, in
arrayBegin, and a rejected array is never offered. An array that is empty
on the wire is offered too, with count of 0: there is nothing to write,
but a destination held across fields would otherwise still hold the previous
array's elements, and its length is the only place this array's emptiness
could show.
kind is the element kind and it decides which destination is legal
(IntegerArrayTarget for Unsigned / Signed,
FloatArrayTarget for Fp32 / Fp64); a destination that contradicts
it, or that is shorter than count, is a caller mistake and is refused with
SofabErrorCode.Argument before any element is written. Declining on
a kind this visitor did not expect is always available — and is the right
answer, since null costs nothing but the call.
See ArrayTarget for what the decoder then guarantees: ascending
writes, the element bound enforced here and only here, the destination held
until arrayEnd across as many feed calls as the chunking takes, and a
partially filled destination if an element is refused.
OptionalarrayEnd of an array.
OptionalblobA piece of a blob field — see string for the src/start/end contract.
OptionalfieldA field header: its id and wire type, announced the moment the header
varint is complete — before the value, and before the value's own header word
(a fixlen length word, an array count word, the fields of a nested sequence).
An observation point for a reader that wants the field stream as it arrives — which id, in which scope, in which order — without implementing the value callbacks it would otherwise take to see the same thing.
A schema bound does not belong here. The header settles id and wire,
and nothing else. An element id past the schema count (MESSAGE_SPEC
§7.1/§5.1) looks decidable from the id alone, and it is not: §7.3 applies that
bound only to a field whose subtype has confirmed it is the declared one,
and a contradicting subtype is skipped rather than rejected. The subtype
arrives in the fixlen word, so the verdict is due at fixlenBegin.
CORELIB_PLAN §4.1.1 makes the timing normative: a message ending inside that
word is INCOMPLETE even when the id would violate a schema bound, because
the low 3 bits of an unfinished varint must not influence an outcome even
though they are already arithmetically fixed.
Called exactly once per field, in every scope, for every wire type — the sequence end marker excepted: it closes a scope rather than opening a field and its id is discarded (§4.9). For a nested sequence it fires before sequenceBegin.
Throwing from it rejects the field — for a verdict the header really does settle on its own, such as an id this reader will not accept in any shape.
OptionalfixlenStart of a string/blob field: total payload bytes follow, in one or more
string/blob calls.
The counterpart of arrayBegin, and it exists for the same reason: a
receiver-side bound on the declared length is decided by this word, not by
the payload. Without it a visitor could only see total once payload bytes
arrive, so a message that ends right after an over-bound length word would
escape the check and degrade to INCOMPLETE, where §5.2.3 requires INVALID.
Called exactly once per field, before any payload call — including for a zero-length payload, which is still announced here and then delivered as one empty range.
Optionalfp32An IEEE-754 32-bit float field.
value is a JS number — a 64-bit double — and widening a signaling NaN
into a double quiets it (sets the is-quiet bit), so value cannot represent
an fp32 sNaN faithfully. bits is the exact 4 wire bytes as one little-endian
32-bit word, which can: re-encode from it with
OStream.writeFp32Bits and the payload round-trips bit-for-bit
(§4.6/§6.5). It is the "32-bit bits accessor" §6.5 names, and it is always
present — a number costs nothing to pass and needs no opt-in flag, where the
byte view it replaces was an allocation per value and a borrowed slice §6.7
forbids.
Optionalfp64An IEEE-754 64-bit double field. value is exact — a double is 64 bits wide.
OptionalsequenceStart of a nested sequence — a fresh id scope (§4.9) — opened by field id
at depth (1 at the root).
Return false to decline the whole subtree: no callback of any kind fires
inside it, nesting included, its own sequenceEnd included, and a scope
opened within it is never offered either. Return anything else (or nothing) to
descend, and the nested fields arrive on this same visitor with their own ids
and depth + 1.
A declined subtree is still parsed — a sequence is framed by markers, not by
a length, so its end has to be found — but nothing in it is decoded into
existence: no piece is reported and no value is built. No receiver cap fires
inside one either (§6.2.1's "a skipped field is never capped"), and that falls
out of the structure rather than needing a rule: a cap is compared by the
handler this stream would have called, and a declined scope calls none. Format
ceilings (ARRAY_MAX, FIXLEN_MAX, MAX_DEPTH, the varint bound) still apply
everywhere: they bound what the wire may express.
OptionalsequenceEnd of the nested sequence opened by field id at depth.
OptionalsignedA signed integer field. value is number-first like unsigned
(|value| ≤ 2^53-1 ⇒ number); lo / hi are the decoded
(zig-zag-undone) two's-complement halves.
OptionalstringA piece of a UTF-8 string field: the bytes src[start..end), at offset of
a total-byte payload.
src is the caller's own chunk — the exact array passed to
IStream.feed (or to decode) — handed back with the piece's
coordinates (§6.6.3). The decoder builds no view over it, keeps no storage,
and hands out no borrowed slice of its own (§6.6, §6.7). Once feed returns,
the caller may reuse that memory, so a consumer that wants the value copies it
out during the call: PayloadAcc joins pieces into a buffer it
owns, and decodeUtf8 turns a range straight into a string.
The bytes are not validated. §6.4.5 puts the UTF-8 check where a string is
materialized — a piece may end mid-code-point, and a skipped field is never
validated at all — so on this surface the caller who materializes owns the
check. decodeUtf8 is it, and a hand-rolled one must be built fatal
(new TextDecoder("utf-8", { fatal: true })): JavaScript's default
TextDecoder substitutes U+FFFD, which §6.4 forbids in either direction.
OptionalunsignedAn unsigned integer field.
value is number-first: a number when the value fits exactly
(≤ 2^53-1, covering ids, u8..u32 and small u64s) and a bigint only beyond
that. lo / hi are the exact 64 bits as two unsigned 32-bit halves — the
ones the varint reader already holds — for a consumer that wants the value
bit-exactly without going through bigint arithmetic (Long.fromBits
builds a Long from them). Both describe the same value; use whichever fits.
Receives decoded fields from an IStream — the one decode surface (§5.3.1). Every method is optional and defaults to a no-op, so a visitor implements only the fields it cares about and silently skips the rest, which is the
skiphalf of the two per-field intents §6.7.2 allows (the other beingread: take the value, in the call).One visitor per message, not per scope. Nested sequences arrive as sequenceBegin / sequenceEnd events on this same object, each carrying the sequence's
idand itsdepth(1 for a sequence opened at the root). Generated code routes on those two numbers, which it knows statically from the schema.Nothing handed to a visitor outlives the call. A
string/blobpayload is reported in pieces as a range of the caller's own fed chunk (§6.6.3): the decoder creates no view over it and holds no storage of its own (§6.6, §6.7), so a consumer that wants the value copies it out — during the call — into storage it owns. PayloadAcc and decodeUtf8 are the ready-made way to do that.