Skip to content

Commit ab17b03

Browse files
committed
Allow dynamically sized unions
1 parent cc7f3f5 commit ab17b03

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

dissect/cstruct/types/structure.py

+15-5
Original file line numberDiff line numberDiff line change
@@ -373,25 +373,35 @@ def _calculate_size_and_offsets(cls, fields: list[Field], align: bool = False) -
373373
# If a field already has an alignment, it's leading
374374
field.alignment = field.type.alignment
375375

376-
size = max(len(field.type), size)
376+
if size is not None:
377+
try:
378+
size = max(len(field.type), size)
379+
except TypeError:
380+
size = None
381+
377382
alignment = max(field.alignment, alignment)
378383

379384
return size, alignment
380385

381386
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Union:
382-
buf = io.BytesIO(memoryview(stream.read(len(cls))))
383387
result = {}
384388
sizes = {}
385389

390+
if cls.size is None:
391+
offset = stream.tell()
392+
buf = stream
393+
else:
394+
offset = 0
395+
buf = io.BytesIO(stream.read(cls.size))
396+
386397
for field in cls.fields:
387-
start = 0
388-
buf.seek(0)
389398
field_type = cls.cs.resolve(field.type)
390399

400+
start = 0
391401
if field.offset is not None:
392-
buf.seek(field.offset)
393402
start = field.offset
394403

404+
buf.seek(offset + start)
395405
value = field_type._read(buf, result)
396406

397407
if isinstance(field_type, StructureMetaType) and field_type.anonymous:

tests/test_types_union.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ def test_union_definition(cs: cstruct):
187187
assert cs.test().dumps() == b"\x00\x00\x00\x00\x00\x00\x00\x00"
188188

189189

190-
def test_union_definitiont_nested(cs: cstruct, compiled: bool):
190+
def test_union_definition_nested(cs: cstruct, compiled: bool):
191191
cdef = """
192192
struct test {
193193
char magic[4];
@@ -247,3 +247,25 @@ def test_union_definition_anonymous(cs: cstruct, compiled: bool):
247247
assert obj.c == b"\x02"
248248
assert obj.d == 0x04040303
249249
assert obj.dumps() == buf
250+
251+
252+
def test_union_definition_dynamic(cs: cstruct):
253+
cdef = """
254+
struct dynamic {
255+
uint8 size;
256+
char data[size];
257+
};
258+
259+
union test {
260+
dynamic a;
261+
uint64 b;
262+
};
263+
"""
264+
cs.load(cdef, compiled=False)
265+
266+
buf = b"\x09aaaaaaaaa"
267+
obj = cs.test(buf)
268+
269+
assert obj.a.size == 9
270+
assert obj.a.data == b"aaaaaaaaa"
271+
assert obj.b == 0x6161616161616109

0 commit comments

Comments
 (0)