Named constants

The core recognises a closed set of field kinds, digest algorithms, byte orders, text encodings and error kinds. Each of these has a name you can import, so a schema does not have to spell them as bare strings and a typo is visible to a type checker and an editor rather than only to compile().

Every one of them is an enum.StrEnum, so a member is the string it replaces. Naming a value never changes what gets compiled, and plain strings keep working everywhere they worked before:

from rustruct import Algo, ByteOrder, Kind, compile

named = compile(
    (
        ("tag", Kind.U8, {}),
        ("crc", Kind.DIGEST, {"algo": Algo.CRC32, "over": "*"}),
    ),
    byteorder=ByteOrder.BIG,
)
plain = compile(
    (
        ("tag", "u8", {}),
        ("crc", "digest", {"algo": "crc32", "over": "*"}),
    ),
    byteorder="big",
)
assert named.pack({"tag": 7}) == plain.pack({"tag": 7})
assert Kind.U8 == "u8"

Field kinds

rustruct.Kind names every kind compile() accepts: the fixed scalars U8/I8 through U64/I64, F32, F64 and BOOL; the byte and text kinds RAW, BYTES, STR and CSTR; BITS and FLAGS; the composites STRUCT, ARRAY, SWITCH and COND; and DIGEST.

Annotate a position that accepts one as rustruct.vocab.KindArg, not as Kind — plain strings are part of the contract, and a bare Kind annotation would reject them.

Digest algorithms

rustruct.Algo names what rustruct.digest() can compute. CRCs and the Internet checksum produce an integer field; the hashes produce bytes of their natural width.

Member

Wire name

Field width

Notes

Algo.CRC16_CCITT

crc16_ccitt

2 bytes

CRC-16/IBM-3740, also known as CCITT-FALSE

Algo.CRC16_IBM

crc16_ibm

2 bytes

CRC-16/ARC

Algo.CRC32

crc32

4 bytes

the usual zlib/PNG polynomial

Algo.CRC32C

crc32c

4 bytes

Castagnoli

Algo.CRC64_XZ

crc64_xz

8 bytes

Algo.IP

ip

2 bytes

RFC 1071 Internet checksum (IPv4 headers)

Algo.MD5

md5

16 bytes

bytes, not an integer

Algo.SHA1

sha1

20 bytes

bytes, not an integer

Algo.SHA256

sha256

32 bytes

bytes, not an integer

Pass poly=, init=, xorout=, refin= or refout= to vary a CRC model away from its preset.

Byte order

rustruct.ByteOrder has BIG, LITTLE and NETWORK. NETWORK compiles to exactly what BIG does, and documents intent the way the standard library’s struct format character ! does. It is a member in its own right rather than an enum alias, so it carries the value "network" and does not compare equal to BIG.

There is deliberately no NATIVE: the core refuses it, because it would make the wire format depend on whichever machine happened to do the encoding.

Text encodings

rustruct.Encoding has UTF8, ASCII and LATIN1 — a much smaller set than Python’s own codec registry, because these three are what the core implements. Spellings are normalised exactly as the core normalises them, so "UTF-8", "utf8" and "utf_8" all resolve to Encoding.UTF8, and "us-ascii" and "iso-8859-1" resolve to Encoding.ASCII and Encoding.LATIN1.

errors= is not an enum: the core implements exactly one policy, "strict".

Flag rest policy

rustruct.RestPolicy decides what a flags field does with bits that no name in it covers: KEEP reports them under the rustruct.REST_KEY key ("_rest") and accepts one back on pack, STRICT rejects any uncovered set bit, and IGNORE drops them on read and writes zeros.

Error kinds

rustruct.ErrorKind names every value that can appear as InvalidDataError.kind or PackError.kind, so branching on one does not depend on retyping a string correctly:

from rustruct import ErrorKind, InvalidDataError, compile

codec = compile((("x", "u32", {}),))
try:
    codec.unpack(b"\x00")
except InvalidDataError as exc:
    assert exc.kind == ErrorKind.TRUNCATED

Unpack can report TRUNCATED, TRAILING, RANGE, NEGATIVE_LEN, OVERFLOW, DIV_ZERO, UNTERMINATED, NUL_IN_CSTR, NO_CASE, DECODE, LIMIT, DEPTH, CHECKSUM, CONST and RESERVED_BITS. Pack can report MISSING, LENGTH, INDIVISIBLE, INCONSISTENT, BUFFER, UNKNOWN_FLAG, TYPE and ENCODE.

DEPTH is the one a schema from rustruct.compile() cannot produce: unpacking allows 64 nested structures, and a schema needing more is refused at compile time rather than left to fail on every input.

Expression operators

rustruct.BinOp names the heads an expression tuple can carry: ADD, SUB, MUL, DIV, SHL, SHR, AND, OR, XOR, EQ, NE, LT, LE, GT and GE. Ordinary Python operators on a field reference build these, so it is mainly of interest when writing expression tuples by hand — see Low-level schema language.

Signatures

class rustruct.Kind(*values)[source]

Every field kind rustruct.compile() recognises.

tests/test_vocabulary.py compiles a minimal schema for each member, so a kind that stops being accepted fails a test rather than drifting silently.

U8 = 'u8'
I8 = 'i8'
U16 = 'u16'
I16 = 'i16'
U32 = 'u32'
I32 = 'i32'
U64 = 'u64'
I64 = 'i64'
F32 = 'f32'
F64 = 'f64'
BOOL = 'bool'
RAW = 'raw'
BYTES = 'bytes'
STR = 'str'
CSTR = 'cstr'
BITS = 'bits'
FLAGS = 'flags'
STRUCT = 'struct'
ARRAY = 'array'
SWITCH = 'switch'
COND = 'cond'
DIGEST = 'digest'
class rustruct.Algo(*values)[source]

Digest algorithms rustruct.digest() can compute.

The CRC members name Rocksoft models; pass poly=/init=/ xorout=/refin=/refout= to vary one. CRCs and IP produce an integer field; the hashes produce bytes of their natural width (16, 20 and 32 bytes respectively).

CRC16_CCITT = 'crc16_ccitt'

CRC-16/IBM-3740, also known as CCITT-FALSE.

CRC16_IBM = 'crc16_ibm'

CRC-16/ARC.

CRC32 = 'crc32'
CRC32C = 'crc32c'

Castagnoli.

CRC64_XZ = 'crc64_xz'
IP = 'ip'

RFC 1071 Internet checksum (IPv4 headers).

MD5 = 'md5'
SHA1 = 'sha1'
SHA256 = 'sha256'
class rustruct.ByteOrder(*values)[source]

Wire byte order.

NETWORK compiles to exactly what BIG does – same program, same bytes – and exists as its own spelling because it documents intent the way the struct module’s ! does. It is not an alias in the enum sense: it is a distinct member with the value "network", so ByteOrder.NETWORK == ByteOrder.BIG is false. Compare what a codec produces, not the member you passed.

There is deliberately no NATIVE: the core refuses it, since it would make the wire format depend on the machine doing the encoding.

BIG = 'big'
LITTLE = 'little'
NETWORK = 'network'
class rustruct.Encoding(*values)[source]

Text encodings str/cstr fields can use.

A much smaller set than Python’s own codec registry – the core implements exactly these three. The only set here whose wire spellings are not name.lower(), and the only one that accepts aliases, so it stays a written-out class: "UTF-8", "utf8" and "utf_8" all resolve to UTF8, and "us-ascii"/"iso-8859-1" resolve to ASCII/LATIN1.

UTF8 = 'utf-8'
ASCII = 'ascii'
LATIN1 = 'latin-1'
class rustruct.RestPolicy(*values)[source]

What a flags field does with bits no name in it covers.

KEEP = 'keep'

Report the leftover under REST_KEY, and accept one back on pack.

STRICT = 'strict'

Reject any set bit that no name covers.

IGNORE = 'ignore'

Drop them on read, write zeros on pack.

class rustruct.ErrorKind(*values)[source]

The kind attribute of rustruct.InvalidDataError and rustruct.PackError.

A closed set (crates/rustruct/src/error.rs, “closed list, v1”) that until now reached Python as an unnamed string, so if exc.kind == "truncated" was a typo away from a branch that could never be taken. Members compare equal to those strings, so existing comparisons keep working unchanged.

TRUNCATED = 'truncated'

The buffer ended before the schema did.

TRAILING = 'trailing'

Bytes left over after a full unpack().

RANGE = 'range'
NEGATIVE_LEN = 'negative_len'
OVERFLOW = 'overflow'
DIV_ZERO = 'div_zero'
UNTERMINATED = 'unterminated'

A cstr ran to the end of its region without a NUL.

NUL_IN_CSTR = 'nul_in_cstr'
NO_CASE = 'no_case'

A switch tag matched no case and there was no default.

DECODE = 'decode'

Bytes that are not valid text in the field’s encoding.

LIMIT = 'limit'

A length or count exceeded its max.

DEPTH = 'depth'

Nesting exceeded the 64-frame limit.

Not reachable from rustruct.compile(), which refuses a schema that deep rather than letting it fail on every input.

CHECKSUM = 'checksum'
CONST = 'const'
RESERVED_BITS = 'reserved_bits'
MISSING = 'missing'

No value supplied for a field that needs one.

LENGTH = 'length'
INDIVISIBLE = 'indivisible'
INCONSISTENT = 'inconsistent'

A supplied value contradicts what the schema derives.

BUFFER = 'buffer'

The destination buffer is too small or not writable.

UNKNOWN_FLAG = 'unknown_flag'
TYPE = 'type'

A value of the wrong Python type for the field.

ENCODE = 'encode'
class rustruct.BinOp(*values)[source]

Operations an expression tuple’s head can name.

Ordinary Python operators on a field reference build these, so this is mostly of interest when writing expression tuples by hand.

ADD = 'add'
SUB = 'sub'
MUL = 'mul'
DIV = 'div'
SHL = 'shl'
SHR = 'shr'
AND = 'and'
OR = 'or'
XOR = 'xor'
EQ = 'eq'
NE = 'ne'
LT = 'lt'
LE = 'le'
GT = 'gt'
GE = 'ge'
rustruct.REST_KEY

The key RestPolicy.KEEP reports uncovered flag bits under: "_rest".