SofaBuffers TypeScript - v0.11.0
    Preparing search index...

    Interface Visitor

    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 skip half of the two per-field intents §6.7.2 allows (the other being read: 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 id and its depth (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 / blob payload 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.

    interface Visitor {
        arrayBegin?(id: number, kind: ArrayKind, count: number): void;
        arrayBulk?(id: number, kind: ArrayKind, count: number): ArrayTarget | null;
        arrayEnd?(id: number): void;
        blob?(
            id: number,
            total: number,
            offset: number,
            src: Uint8Array,
            start: number,
            end: number,
        ): void;
        fieldBegin?(id: number, wire: WireType): void;
        fixlenBegin?(id: number, subtype: FixlenSubtype, total: number): void;
        fp32?(id: number, value: number, bits: number): void;
        fp64?(id: number, value: number): void;
        sequenceBegin?(id: number, depth: number): boolean | void;
        sequenceEnd?(id: number, depth: number): void;
        signed?(id: number, value: number | bigint, lo: number, hi: number): void;
        string?(
            id: number,
            total: number,
            offset: number,
            src: Uint8Array,
            start: number,
            end: number,
        ): void;
        unsigned?(id: number, value: number | bigint, lo: number, hi: number): void;
    }
    Index
    • Offer 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.

      Parameters

      Returns ArrayTarget | null

    • A piece of a blob field — see string for the src/start/end contract.

      Parameters

      • id: number
      • total: number
      • offset: number
      • src: Uint8Array
      • start: number
      • end: number

      Returns void

    • A 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.

      Parameters

      Returns void

    • Start 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.

      Parameters

      Returns void

    • An 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.

      Parameters

      • id: number
      • value: number
      • bits: number

      Returns void

    • An IEEE-754 64-bit double field. value is exact — a double is 64 bits wide.

      Parameters

      • id: number
      • value: number

      Returns void

    • Start 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.

      Parameters

      • id: number
      • depth: number

      Returns boolean | void

    • End of the nested sequence opened by field id at depth.

      Parameters

      • id: number
      • depth: number

      Returns void

    • A 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.

      Parameters

      • id: number
      • value: number | bigint
      • lo: number
      • hi: number

      Returns void

    • A 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.

      Parameters

      • id: number
      • total: number
      • offset: number
      • src: Uint8Array
      • start: number
      • end: number

      Returns void

    • An 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.

      Parameters

      • id: number
      • value: number | bigint
      • lo: number
      • hi: number

      Returns void