sofab.decoder module

SofaBuffers pull decoder (Go-style IStream equivalent).

The decoder reads from any object exposing read(n) -> bytes (a file, a socket made file-like, an io.BytesIO, or a chunk-feeding wrapper). It pulls exactly what it needs, so it satisfies the format’s streaming requirement for blocking readers; large blob/string/array payloads are read in bulk.

Hot-path model — “advance a cursor over a contiguous buffer” (protobuf’s trick). Incoming bytes are accumulated into a single contiguous buffer (self._buf) and parsed by advancing an integer cursor (self._pos) with direct indexing — no per-byte function call, no intermediate copies. When the cursor reaches the end mid-item the decoder transparently refills from the reader and continues, so the same code path serves both a fully-buffered message and a reader that dribbles one byte at a time. See _varint / _read_varints / _read_exact below.

Typical use:

dec = Decoder(reader)
while (field := dec.next()) is not None:
    if field.id == 1:
        value = dec.unsigned()
    else:
        dec.skip()
class sofab.decoder.Decoder(reader, *, chunk_size=65536, max_array_count=None, max_string_len=None, max_blob_len=None)[source]

Bases: object

Pull-decodes a SofaBuffers stream field by field.

Call next() to advance to each field, then one of the typed read methods (unsigned(), string(), read_float64_array(), …) to consume its value, or skip() to discard it. Alternatively hand a sofab.Visitor to drive() for callback-style decoding.

Parameters:
  • reader (_Reader)

  • chunk_size (int)

  • max_array_count (int | None)

  • max_string_len (int | None)

  • max_blob_len (int | None)

property field: Field | None

The most recently returned Field.

next()[source]

Advance to the next field. Returns None at clean EOF.

Any value left unconsumed from the previous field is skipped first.

Return type:

Field | None

skip()[source]

Skip the current field’s value, or an entire (nested) sequence if the current field is a sequence start.

Return type:

None

drive(visitor)[source]

Pull the whole stream, dispatching each field to visitor’s typed hooks (see sofab.Visitor). A visitor may decline a field via on_field / on_sequence_begin returning False to skip it without paying the decode cost.

Parameters:

visitor (sofab.Visitor)

Return type:

None

unsigned()[source]

Consume the current field as an unsigned integer.

Raises SofaStateError if the current field is not unsigned.

Return type:

int

signed()[source]

Consume the current field as a ZigZag-decoded signed integer.

Raises SofaStateError if the current field is not signed.

Return type:

int

bool()[source]

Consume the current unsigned field as a boolean (non-zero is true).

Return type:

bool

float32()[source]

Consume the current fixlen field as a 32-bit IEEE-754 float.

Raises SofaStateError if the field is not an fp32 fixlen, or SofaDecodeError if its payload is not 4 bytes.

Return type:

float

float64()[source]

Consume the current fixlen field as a 64-bit IEEE-754 float.

Raises SofaStateError if the field is not an fp64 fixlen, or SofaDecodeError if its payload is not 8 bytes.

Return type:

float

fixlen_len()[source]

Return the current fixlen field’s payload byte length without consuming it.

The length is read straight from the field’s length header, so this is a pure peek: it does not advance the decoder and a following string()/bytes()/float32()/float64() still reads the same field. It lets a caller bound a string or blob against its schema maxlen using the exact wire byte length the decoder already parsed — checked before allocation, and without re-encoding a decoded str just to measure it (the string field is UTF-8 on the wire, so the payload byte length is the length maxlen bounds).

Raises SofaStateError if the current field is not a fixlen value.

Return type:

int

string()[source]

Consume the current fixlen field as a UTF-8 decoded string.

Raises SofaStateError if the field is not a STRING fixlen, or SofaDecodeError if the payload is not valid UTF-8.

Return type:

str

bytes()[source]

Consume the current fixlen field as a raw byte blob.

Raises SofaStateError if the field is not a BLOB fixlen.

Return type:

bytes

read_unsigned_array()[source]

Consume the current field as a list of unsigned integers.

Raises SofaStateError if the field is not an unsigned array.

Return type:

list[int]

read_signed_array()[source]

Consume the current field as a list of ZigZag-decoded signed integers.

Raises SofaStateError if the field is not a signed array.

Return type:

list[int]

read_float32_array()[source]

Consume the current field as a list of 32-bit IEEE-754 floats.

Raises SofaStateError if the field is not an fp32 array.

Return type:

list[float]

read_float64_array()[source]

Consume the current field as a list of 64-bit IEEE-754 floats.

Raises SofaStateError if the field is not an fp64 array.

Return type:

list[float]