StructLayoutToolkitCodec
sltcodec is a small package for decoding and encoding bytearrays according to structured layout definitions. It uses sltcore for bit-level access and provides a simple API for turning a layout definition into binary data and back again.
Installation
pip install sltcodec
Primitive Type Set
PRIMITIVE_TYPES is the public set of built-in field types understood by the codec.
from sltcodec import PRIMITIVE_TYPES
print(PRIMITIVE_TYPES)
# {'bool', 'signed int', 'int', 'unsigned int', 'float', 'bytearray', 'bytes'}
Quick Example
FieldDef.description is an optional human-readable note that can be attached to each field definition.
decode returns a StructInstance.
encode accepts a StructInstance and a destination bytearray.
from sltcore import InfoSize
from sltcodec import (FieldDef, FieldInstance, StructDef, StructInstance,
decode, encode)
struct_def = [
FieldDef(name="flag",
offset=InfoSize(0, 0),
size=InfoSize(0, 1),
type="bool",
scale=1.0,
description="Whether the feature is enabled"),
FieldDef(name="value",
offset=InfoSize(0, 1),
size=InfoSize(1, 0),
type="unsigned int",
scale=1.0,
description="The encoded numeric payload"),
]
encoded = encode(
StructInstance(
struct_def=StructDef(fields=struct_def),
field_instances=[
FieldInstance(struct_def[0], True),
FieldInstance(struct_def[1], 0xA5),
],
),
bytearray(),
)
decoded = decode(struct_def, encoded)
print(encoded)
print(decoded)
print(decoded.field_instances)
Output:
bytearray(b"\xd2\x80")
StructInstance(struct_def=StructDef(...), field_instances=[...])
[FieldInstance(field_def=FieldDef(name='flag', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=0, bit=1), type='bool', scale=1.0, repeat=None), value=True),
FieldInstance(field_def=FieldDef(name='value', offset=InfoSize(byte=0, bit=1), size=InfoSize(byte=1, bit=0), type='unsigned int', scale=1.0, repeat=None), value=165)]
Nested Field Types
FieldDef.type can be a StructDef, not only a primitive type name.
This allows you to define an inline nested structure and keep encode_field / decode_field behavior symmetrical.
from sltcore import InfoSize
from sltcodec import FieldDef, FieldInstance, StructDef, StructInstance, decode, encode
child_struct_def = [
FieldDef(name="left", offset=InfoSize(0, 0), size=InfoSize(1, 0), type="unsigned int"),
FieldDef(name="right", offset=InfoSize(1, 0), size=InfoSize(1, 0), type="unsigned int"),
]
parent_field_def = FieldDef(
name="pair",
offset=InfoSize(0, 0),
size=InfoSize(2, 0),
type=StructDef(
name="Pair",
description="Two adjacent unsigned ints",
fields=child_struct_def,
),
)
encoded = encode(
StructInstance(
struct_def=StructDef(fields=[parent_field_def]),
field_instances=[
FieldInstance(
parent_field_def,
StructInstance(
struct_def=StructDef(fields=child_struct_def),
field_instances=[
FieldInstance(child_struct_def[0], 3),
FieldInstance(child_struct_def[1], 4),
],
),
)
],
),
bytearray(),
)
decoded = decode([parent_field_def], encoded)
print(encoded)
print(decoded)
print(decoded.field_instances)
Output:
bytearray(b"\x03\x04")
StructInstance(struct_def=StructDef(...), field_instances=[...])
[FieldInstance(field_def=FieldDef(name='pair', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=2, bit=0), type=StructDef(...), scale=1.0, repeat=None),
value=StructInstance(struct_def=StructDef(...), field_instances=[...]))]
Expression-Based Type Selection
FieldDef.type can also be an expression string.
The expression is evaluated with previously processed field values in scope, so you can switch decoding/encoding type dynamically.
from sltcore import InfoSize
from sltcodec import (FieldDef, FieldInstance, StructDef, StructInstance,
decode, encode)
struct_def = [
FieldDef(name="kind", offset=InfoSize(0, 0), size=InfoSize(1, 0), type="unsigned int"),
FieldDef(
name="payload",
offset=InfoSize(1, 0),
size="{1: 1, 2: 4}[kind]",
type="{1: 'int', 2: 'float'}[kind]",
),
]
# kind=1 -> payload is int (1 byte)
encoded_int = encode(
StructInstance(
struct_def=StructDef(fields=struct_def),
field_instances=[
FieldInstance(struct_def[0], 1),
FieldInstance(struct_def[1], 7),
],
),
bytearray(),
)
decoded_int = decode(struct_def, encoded_int)
# kind=2 -> payload is float (4 bytes)
encoded_float = encode(
StructInstance(
struct_def=StructDef(fields=struct_def),
field_instances=[
FieldInstance(struct_def[0], 2),
FieldInstance(struct_def[1], 1.5),
],
),
bytearray(),
)
decoded_float = decode(struct_def, encoded_float)
print(encoded_int, decoded_int)
print(encoded_float, decoded_float)
Saving And Loading Type Dictionaries
You can persist reusable structure and enum definitions together via
TypeDict with save_type_dict / load_type_dict.
InfoSize values in offset and size are saved as typed dictionaries in
JSON. Expression-based offsets and sizes remain strings and are resolved when
the definition is used.
from pathlib import Path
from sltcore import InfoSize
from sltcodec import EnumDef, FieldDef, StructDef, TypeDict, load_type_dict, save_type_dict
type_dict = TypeDict(
struct_dict={
"Header": StructDef(
name="Header",
description="Simple header",
fields=[
FieldDef(name="kind", offset=InfoSize(0, 0), size=InfoSize(1, 0), type="unsigned int"),
FieldDef(name="flags", offset=InfoSize(1, 0), size=InfoSize(1, 0), type="unsigned int"),
],
)
},
enum_dict={
"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1}),
}
)
path = Path("struct_defs.json")
save_type_dict(path, type_dict)
loaded = load_type_dict(path)
print(loaded.struct_dict["Header"].name)
print(loaded.struct_dict["Header"].description)
print(loaded.enum_dict["Status"].values["NG"])
Example output:
Header
Simple header
1
Enum Definitions
FieldDef.enum_def_name holds only the name of an EnumDef, not the definition
itself. In practice, TypeDict is most useful when you pass both
type_dict.struct_dict and type_dict.enum_dict together: one resolves named
layout types and the other resolves enum labels. The example below uses both in
one decode flow.
from sltcore import InfoSize
from sltcodec import EnumDef, EnumDict, FieldDef, StructDef, StructDict, TypeDict, decode
enum_dict = EnumDict({"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1})})
packet_struct = StructDef(
name="Packet",
fields=[
FieldDef(name="status",
offset=InfoSize(0, 0),
size=InfoSize(1, 0),
type="unsigned int",
enum_def_name="Status"),
],
)
struct_dict = StructDict({"Packet": packet_struct})
type_dict = TypeDict(struct_dict=struct_dict.items_dict(),
enum_dict=enum_dict.items_dict())
decoded = decode(type_dict.struct_dict["Packet"],
bytearray(b"\x01"),
type_dict=type_dict)
print(decoded.field_instances[0].value)
print(decoded.field_instances[0].enum_item)
Output:
1
('NG', 1)
You can also persist enum definitions by name through TypeDict using
save_type_dict / load_type_dict.
Development
uv sync --all-extras
uv run pytest
Metadata
Release files for sltcodec 1.3.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| sltcodec-1.3.0.tar.gz | 22.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| sltcodec-1.3.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.6 kB
Release files / sltcodec-1.3.0.tar.gz
| Download URL | sltcodec-1.3.0.tar.gz |
|---|---|
| Size | 22.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f9589a100295657affe6f97bd412403dc3cde7423a7ec5d8f358e83320bbdd56
|
|
BLAKE2b-256 checksum How to use checksums |
0ab78a4a82f610a411104546e42beb9ac80164ba2fbcb19dfb84da23d9a353ef
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / sltcodec-1.3.0-py3-none-any.whl
| Download URL | sltcodec-1.3.0-py3-none-any.whl |
|---|---|
| Size | 13.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
6f4d21c80f7161b6bea5326956a7c4a3ec7742f9ec2f4ecc3af6d60f833147ab
|
|
BLAKE2b-256 checksum How to use checksums |
9a166d15d49348074d7473cfa5a7e48c5e84022110aab55efa34313fa6d5f20d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|