deserializer
stdlib/core/deserializer.hk: the Deserializer format abstraction and the built-in binary deserializer.
Deserializer is the read side of the format-generic serialization framework (HANKI.md §16), the inverse of Serializer: each method reads one structural event from a source and advances it, returning a DecodeError (with the byte offset) on truncation or a bad tag. Decode impls name the events and stay format-agnostic.
BinaryDeserializer reads the built-in fixed-width binary format over a BytesReader cursor, the inverse of BinarySerializer. The cursor tracks position, and no offset is threaded by hand. take_struct! reads nothing (a struct's field count is static); a framed format would read and check an array header instead.
Every read is total: a short read becomes Err(Truncated), never a crash. Each method is an action (!) with an empty effect row, like the BytesReader it drives (HANKI.md §4).
DecodeError
type DecodeError
Truncated(int)
BadTag(u8, int)
BadUtf8(int)
TooDeep(int)
end
Why a decode failed, with the byte offset at which it was detected.
Deserializer
trait Deserializer
take_bool!
def take_bool!(self) -> Result<bool, DecodeError>
Reads one bool from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u8!
def take_u8!(self) -> Result<u8, DecodeError>
Reads one u8 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u16!
def take_u16!(self) -> Result<u16, DecodeError>
Reads one u16 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u32!
def take_u32!(self) -> Result<u32, DecodeError>
Reads one u32 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u64!
def take_u64!(self) -> Result<u64, DecodeError>
Reads one u64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_i32!
def take_i32!(self) -> Result<i32, DecodeError>
Reads one i32 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_i64!
def take_i64!(self) -> Result<i64, DecodeError>
Reads one i64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_f64!
def take_f64!(self) -> Result<f64, DecodeError>
Reads one f64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_string!
def take_string!(self) -> Result<string, DecodeError>
Reads one string from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_bytes!
def take_bytes!(self) -> Result<bytes, DecodeError>
Reads one bytes from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
takeissome!
def take_is_some!(self) -> Result<bool, DecodeError>
true if a Some payload follows, false for None. @no-doctest: structural decode op; the round-trip tests below exercise it
take_seq!
def take_seq!(self) -> Result<int, DecodeError>
The element / pair count of a sequence or map. @no-doctest: structural decode op; the round-trip tests below exercise it
take_map!
def take_map!(self) -> Result<int, DecodeError>
Reads a map header, returning its length.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_struct!
def take_struct!(self, fields: int) -> Result<(), DecodeError>
Read (and, for a framed format, check) a struct header of fields fields. @no-doctest: structural decode op; the round-trip tests below exercise it
take_variant!
def take_variant!(self) -> Result<u8, DecodeError>
A sum's variant tag. @no-doctest: structural decode op; the round-trip tests below exercise it
position!
def position!(self) -> int
The current read offset, for locating a DecodeError (e.g. the byte a bad variant tag was read from). @no-doctest: structural decode op; the round-trip tests below exercise it
remaining!
def remaining!(self) -> int
Unread bytes left in the source. A decoded collection bounds its element count against this (a sequence of N elements needs at least N wire bytes), so a crafted huge count can't drive an unbounded decode loop. @no-doctest: structural decode op; the round-trip tests below exercise it
take_int!
def take_int!(self) -> Result<int, DecodeError>
-- the exact tier ------------------------------------------------------
The read side of Serializer's exact-tier events, in the same shape and for the same reason: each has a default body over the events above, and a Deserializer written before they existed goes on compiling. take_int! is the ground case; the other two reduce to it through the exact decomposition, sentinels included. Reads one arbitrary-precision int written by put_int!: the class byte, then the digits when it says finite.
A class byte outside 0 ..= 3 is BadTag, and digits int.parse rejects are BadTag at the same offset. The payload is structurally a string, and Truncated/BadUtf8 are already spoken for by take_string!.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_decimal!
def take_decimal!(self) -> Result<decimal, DecodeError>
Reads one decimal from the unscaled/scale pair put_decimal! wrote.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_rational!
def take_rational!(self) -> Result<rational, DecodeError>
Reads one rational from the numerator/denominator pair put_rational! wrote.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_f32!
def take_f32!(self) -> Result<f32, DecodeError>
Reads one f32 from the IEEE bit pattern put_f32! wrote. Every 32-bit pattern is some f32, and this cannot fail beyond the underlying read.
@no-doctest: structural decode op; the round-trip tests below exercise it
intsentinel
def _int_sentinel(class: u8) -> Option<int>
The inverse of serializer._int_class for the three sentinel classes; None for a class byte that is neither a sentinel nor the finite 0.
BinaryDeserializer
struct BinaryDeserializer
src: BytesReader
end
The built-in fixed-width binary format over a BytesReader cursor.
BytesReader
BytesReader, or bytes.BytesReader, is a runtime-managed native resource handle (HANKI.md §4): live native state the per-actor memory manager owns, released when the last handle drops. It is single-owner - never copied, moved across a send - and has no fields of its own, so its methods are its whole surface. A handle is minted by an API that opens one; it is never constructed.
detake!
def _de_take!(src: BytesReader, n: int) -> Result<bytes, DecodeError>
delenprefixed!
def _de_lenprefixed!(src: BytesReader) -> Result<bytes, DecodeError>
A u32 big-endian length prefix, then that many bytes.
beu16
def _be_u16(b: bytes) -> u16
beu32
def _be_u32(b: bytes) -> u32
beu64
def _be_u64(b: bytes) -> u64
deu32!
def _de_u32!(src: BytesReader) -> Result<u32, DecodeError>
u32 read shared by length prefixes, counts, and the public take_u32!.
deu64!
def _de_u64!(src: BytesReader) -> Result<u64, DecodeError>
u64 read shared by the public take_u64! / take_i64! / take_f64!.
impl Deserializer<BinaryDeserializer>
take_bool!
def take_bool!(self) -> Result<bool, DecodeError>
Reads one bool from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u8!
def take_u8!(self) -> Result<u8, DecodeError>
Reads one u8 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u16!
def take_u16!(self) -> Result<u16, DecodeError>
Reads one u16 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u32!
def take_u32!(self) -> Result<u32, DecodeError>
Reads one u32 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_u64!
def take_u64!(self) -> Result<u64, DecodeError>
Reads one u64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_i32!
def take_i32!(self) -> Result<i32, DecodeError>
Reads one i32 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_i64!
def take_i64!(self) -> Result<i64, DecodeError>
Reads one i64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_f64!
def take_f64!(self) -> Result<f64, DecodeError>
Reads one f64 from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_string!
def take_string!(self) -> Result<string, DecodeError>
Reads one string from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_bytes!
def take_bytes!(self) -> Result<bytes, DecodeError>
Reads one bytes from the input.
@no-doctest: structural decode op; the round-trip tests below exercise it
takeissome!
def take_is_some!(self) -> Result<bool, DecodeError>
Reads an optional's presence flag; the value follows when true.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_seq!
def take_seq!(self) -> Result<int, DecodeError>
Reads a seq header, returning its length.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_map!
def take_map!(self) -> Result<int, DecodeError>
Reads a map header, returning its length.
@no-doctest: structural decode op; the round-trip tests below exercise it
take_struct!
def take_struct!(self, fields: int) -> Result<(), DecodeError>
A struct's field count is static, and the binary format reads no framing. @no-doctest: structural decode op; the round-trip tests below exercise it
take_variant!
def take_variant!(self) -> Result<u8, DecodeError>
Reads a sum variant's tag; its payload follows.
@no-doctest: structural decode op; the round-trip tests below exercise it
position!
def position!(self) -> int
The current read offset, in bytes.
@no-doctest: structural decode op; the round-trip tests below exercise it
remaining!
def remaining!(self) -> int
How many bytes remain unread.
@no-doctest: structural decode op; the round-trip tests below exercise it