Source code for sofab._varint

"""Varint (base-128 LE) and ZigZag codec — the hot path.

This module is deliberately small and free of higher-level concepts so it can
later be replaced by a compiled accelerator (mypyc/Cython, or a PyO3 binding
over ``corelib-rs``) without touching the public API. Keep the signatures
stable.
"""

from __future__ import annotations

from typing import Callable

from .types import MASK64, SofaDecodeError, SofaIncompleteError


[docs] def zigzag_encode(v: int) -> int: """Map a signed int to unsigned: ``(n << 1) ^ (n >> 63)`` (64-bit).""" return ((v << 1) ^ (v >> 63)) & MASK64
[docs] def zigzag_decode(u: int) -> int: """Inverse of :func:`zigzag_encode`: ``(z >> 1) ^ -(z & 1)``.""" return (u >> 1) ^ -(u & 1)
def encode_varint(value: int) -> bytes: """Encode an unsigned 64-bit value as a base-128 little-endian varint. Matches the C ``_varint_encode`` do/while loop: ``0`` encodes to a single ``0x00`` byte. """ out = bytearray() while True: byte = value & 0x7F value >>= 7 if value: out.append(byte | 0x80) else: out.append(byte) return bytes(out) def decode_varint(read_byte: Callable[[], int | None], first: int | None = None) -> int: """Decode a varint by pulling one byte at a time from ``read_byte``. ``read_byte`` returns the next byte as an ``int`` or ``None`` at EOF. Pass ``first`` to supply an already-read leading byte. Raises :class:`SofaIncompleteError` when the bytes end mid-varint (truncation, §7 INCOMPLETE) and :class:`SofaDecodeError` on a >64-bit overflow (§7 INVALID), mirroring the C decoder (overflow once the shift reaches 64 bits with continuation set). """ value = 0 shift = 0 while True: byte = first if first is not None else read_byte() first = None if byte is None: raise SofaIncompleteError("truncated varint") # Reject an overlong (>64-bit) varint before OR-ing: if this byte's 7 # payload bits would spill past bit 63 they are unrepresentable in u64 # and must be INVALID, not silently masked away on return (§4.1/§6.3, # issue #43). ``room`` is the bits left below 64. room = 64 - shift if room < 7 and (byte & 0x7F) >> room: raise SofaDecodeError("overlong varint") value |= (byte & 0x7F) << shift shift += 7 if not (byte & 0x80): return value & MASK64 if shift >= 64: raise SofaDecodeError("overlong varint")