Skip to content

Commit 54ef86a

Browse files
committed
docs: add bitfield documentation
1 parent 570dea3 commit 54ef86a

2 files changed

Lines changed: 111 additions & 0 deletions

File tree

docs/advanced/bitfields.md

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# Bitfields
2+
3+
Bitfields let you pack multiple values into a single integer, just like C bitfields. This is common in hardware registers, protocol headers, and OS structures.
4+
5+
## Defining Bitfields
6+
7+
Use `bitfield_of(backing_type, bit_width)` as a struct field descriptor:
8+
9+
```python
10+
from libdestruct import struct, c_uint, bitfield_of
11+
12+
class flags_t(struct):
13+
read: c_uint = bitfield_of(c_uint, 1)
14+
write: c_uint = bitfield_of(c_uint, 1)
15+
execute: c_uint = bitfield_of(c_uint, 1)
16+
reserved: c_uint = bitfield_of(c_uint, 29)
17+
```
18+
19+
All four fields share a single `c_uint` (4 bytes). The bits are allocated left-to-right:
20+
21+
- `read` occupies bit 0
22+
- `write` occupies bit 1
23+
- `execute` occupies bit 2
24+
- `reserved` occupies bits 3-31
25+
26+
## Reading Bitfields
27+
28+
```python
29+
# Bit pattern: 0b101 = read=1, write=0, execute=1
30+
memory = (0b101).to_bytes(4, "little")
31+
flags = flags_t.from_bytes(memory)
32+
33+
print(flags.read.value) # 1
34+
print(flags.write.value) # 0
35+
print(flags.execute.value) # 1
36+
```
37+
38+
## Writing Bitfields
39+
40+
Writes only affect the relevant bits — other bits are preserved:
41+
42+
```python
43+
from libdestruct import inflater
44+
45+
memory = bytearray(4)
46+
lib = inflater(memory)
47+
flags = lib.inflate(flags_t, 0)
48+
49+
flags.read.value = 1
50+
flags.execute.value = 1
51+
52+
print(flags.read.value) # 1
53+
print(flags.write.value) # 0 (untouched)
54+
print(flags.execute.value) # 1
55+
```
56+
57+
## Signed Bitfields
58+
59+
Use a signed backing type (e.g., `c_int`) for sign-extended extraction:
60+
61+
```python
62+
from libdestruct import struct, c_int, bitfield_of
63+
64+
class example_t(struct):
65+
val: c_int = bitfield_of(c_int, 4)
66+
67+
# 4-bit signed: 0b1111 = -1
68+
memory = (0b1111).to_bytes(4, "little")
69+
test = example_t.from_bytes(memory)
70+
print(test.val.value) # -1
71+
```
72+
73+
## Multiple Backing Types
74+
75+
When consecutive bitfields use different backing types, a new group starts automatically:
76+
77+
```python
78+
class mixed_t(struct):
79+
a: c_uint = bitfield_of(c_uint, 3) # bits 0-2 of a c_uint
80+
b: c_uint = bitfield_of(c_uint, 5) # bits 3-7 of the same c_uint
81+
c: c_long = bitfield_of(c_long, 16) # bits 0-15 of a new c_long
82+
```
83+
84+
`a` and `b` share 4 bytes, `c` starts a new 8-byte group. Total struct size: 12 bytes.
85+
86+
## C Parser Support
87+
88+
The C struct parser handles bitfield syntax:
89+
90+
```python
91+
from libdestruct.c.struct_parser import definition_to_type
92+
93+
flags_t = definition_to_type("""
94+
struct flags_t {
95+
unsigned int read:1;
96+
unsigned int write:1;
97+
unsigned int execute:1;
98+
unsigned int reserved:29;
99+
};
100+
""")
101+
```
102+
103+
## Serialization
104+
105+
Bitfield structs serialize correctly — shared backing bytes are emitted once:
106+
107+
```python
108+
data = flags.to_bytes()
109+
assert len(data) == 4 # one c_uint
110+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ nav:
6161
- The Inflater: memory/inflater.md
6262
- Resolvers: memory/resolvers.md
6363
- Advanced:
64+
- Bitfields: advanced/bitfields.md
6465
- Freeze, Diff & Reset: advanced/freeze_diff.md
6566
- C Struct Parser: advanced/c_parser.md
6667
- Forward References: advanced/forward_refs.md

0 commit comments

Comments
 (0)