Protocol and format API¶
Worked examples are available for How to use the IPv4 model, How to use the DNS model, How to build and decode PNG files, and How to decode and encode MessagePack values.
IP, TCP, and UDP¶
- class rustruct.protocols.IPv4(**kwargs)[source]¶
- classmethod build(*, source, destination, payload, protocol=None, options=b'', ttl=64, version=4, dscp=0, ecn=0, identification=0, reserved=False, df=False, mf=False, fragment_offset=0, checksum=0)[source]¶
Compute ihl/total_length/protocol, then build one ready-to-pack instance. protocol is inferred from a registered payload (e.g. TCP/UDP); it must be given explicitly when payload is raw bytes for an unregistered protocol number.
DNS¶
DNS message (RFC 1035).
Domain-name compression doesn’t fit rustruct’s declarative model: a pointer’s target is an absolute offset into the whole message, and writing one requires remembering every name suffix already emitted anywhere earlier in the message – state that outlives any single field or nested scope. So this module is not one rustruct.Codec for the whole message: DNSFlags/ A/AAAA (no domain name involved) are real rustruct.Struct classes, while everything that touches a domain name (Question, ResourceRecord, and the NS/CNAME/SOA/PTR/MX/TXT record types) is a small hand-written class operating on a shared bytearray + a dict context.
Domain names are plain strings (“example.com”; the root is “”; a trailing dot is accepted and dropped). Labels are transcoded byte-for-byte as latin-1. Compression pointers are followed on read (backwards only, so hostile messages cannot loop) and emitted on write whenever a name suffix was already written; pack with compress=False to disable that.
- class rustruct.protocols.dns.DNS(id: int = 0, flags: DNSFlags = <factory>, questions: list = <factory>, answers: list = <factory>, authorities: list = <factory>, additionals: list = <factory>)[source]¶
A whole DNS message; the section counts are computed on pack().
- class rustruct.protocols.dns.DNSFlags(**kwargs)[source]¶
The second header word: QR, opcode, AA/TC/RD/RA, Z and RCODE.
- class rustruct.protocols.dns.Question(name: str, qtype: rustruct.protocols.dns.RRType = <RRType.A: 1>, qclass: rustruct.protocols.dns.DNSClass = <DNSClass.IN: 1>)[source]¶
- class rustruct.protocols.dns.ResourceRecord(name: str, rclass: DNSClass = DNSClass.IN, ttl: int = 0, data: object = None)[source]¶
rtype is never stored here: a known record type carries its own class-level RTYPE, and UnknownRData already carries the wire tag it was decoded with – storing a third, possibly-inconsistent copy on the record itself would just invite them drifting apart.
- class rustruct.protocols.dns.A(**kwargs)[source]¶
No domain name – a plain 4-byte address, fully declarative.
- class rustruct.protocols.dns.SOA(mname: str, rname: str, serial: int, refresh: int, retry: int, expire: int, minimum: int)[source]¶
PNG¶
- class rustruct.formats.image.png.IHDR(**kwargs)[source]¶
The mandatory, always-first chunk: dimensions and pixel format.
MessagePack¶
MessagePack (https://github.com/msgpack/msgpack/blob/master/spec.md).
MessagePack’s tag byte packs the type discriminant and, for several ranges (fixmap/fixarray/fixstr, positive/negative fixint), the value itself into the same bits: hi4 selects the range, lo4 is either a sub-tag or the count/value directly. Value decodes exactly one tag’s worth of wire content – for a container, that’s just the element count, not the elements.
A MessagePack value is recursive to unbounded depth, which a single rustruct schema cannot express (see docs/explanation/schema-model.md, “What a schema cannot express”). decode/encode supply that recursion in plain Python instead, walking an explicit stack rather than recursing, so neither risks RecursionError regardless of nesting depth.
Deliberately out of scope: ext types (ext8/16/32, fixext1-16) and float32 on encode (Value always encodes floats as float64; both widths decode on read).
- class rustruct.formats.msgpack.Value(**kwargs)[source]¶
One MessagePack tag’s worth of wire content – a container’s elements are not part of this schema (see the module docstring); decode/encode drive Value.unpack_from/Value(…).pack() in a loop to build/consume them.
- rustruct.formats.msgpack.decode(buf)[source]¶
Decode a buffer holding exactly one MessagePack value.
- rustruct.formats.msgpack.decode_from(buf, offset=0)[source]¶
Decode one MessagePack value starting at offset, returning (value, new_offset) – the streaming-friendly building block decode itself is written in terms of. Iterative: a work stack of in-progress containers stands in for the call stack a recursive-descent decoder would use, so nesting depth is bounded only by available memory, not by Python’s own recursion limit.
- rustruct.formats.msgpack.encode(value)[source]¶
Encode a Python value (None/bool/int/float/bytes/str/ list/dict, with dict keys and values themselves encodable) as MessagePack, choosing the most compact tag that fits – the same preference order the reference implementation uses, so the two interoperate byte-for-byte on the common cases (see tests/formats/test_msgpack.py).
Iterative, like decode_from: a preorder walk over an explicit work stack, so encoding a value does not risk RecursionError regardless of how deeply it nests.