Skip to content

Commit 44a5689

Browse files
committed
fix: size attribute of VLA structs is now computed properly
1 parent db089a1 commit 44a5689

2 files changed

Lines changed: 41 additions & 4 deletions

File tree

libdestruct/common/struct/struct_impl.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,22 @@ def __init__(self: struct_impl, resolver: Resolver | None = None, **kwargs: ...)
5959

6060
def __getattribute__(self: struct_impl, name: str) -> object:
6161
"""Return the attribute, checking struct members first to avoid collisions with obj properties."""
62-
# Check _members dict directly to avoid infinite recursion
6362
try:
6463
members = object.__getattribute__(self, "_members")
65-
if name in members:
66-
return members[name]
6764
except AttributeError:
68-
pass
65+
return super().__getattribute__(name)
66+
if name in members:
67+
return members[name]
68+
if name == "size":
69+
# VLA structs store _vla_fixed_offset instead of an instance size attr;
70+
# without this, `instance.size` would fall back to the static class size
71+
# (set by compute_own_size), missing the dynamic VLA contribution.
72+
try:
73+
vla_offset = object.__getattribute__(self, "_vla_fixed_offset")
74+
except AttributeError:
75+
pass
76+
else:
77+
return vla_offset + next(reversed(members.values())).size
6978
return super().__getattribute__(name)
7079

7180
def __setattr__(self: struct_impl, name: str, value: object) -> None:

test/scripts/vla_test.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,5 +313,33 @@ class packet_t(struct):
313313
lib.inflate(packet_t, 0)
314314

315315

316+
class VLAInstanceSizeTest(unittest.TestCase):
317+
"""`instance.size` on a VLA struct must report the dynamic size, not the class static size."""
318+
319+
def test_vla_instance_size_matches_size_of(self):
320+
class packet_t(struct):
321+
length: c_int
322+
data: array[c_int, "length"]
323+
324+
data = pystruct.pack("<i", 3) + pystruct.pack("<iii", 10, 20, 30)
325+
memory = bytearray(data)
326+
pkt = packet_t.from_bytes(memory)
327+
self.assertEqual(pkt.size, size_of(pkt))
328+
self.assertEqual(pkt.size, 16)
329+
330+
def test_vla_instance_size_updates_when_count_changes(self):
331+
class packet_t(struct):
332+
length: c_int
333+
data: array[c_int, "length"]
334+
335+
memory = bytearray(pystruct.pack("<i", 2) + pystruct.pack("<ii", 10, 20) + b"\x00" * 16)
336+
lib = inflater(memory)
337+
pkt = lib.inflate(packet_t, 0)
338+
self.assertEqual(pkt.size, 12)
339+
340+
memory[0:4] = pystruct.pack("<i", 4)
341+
self.assertEqual(pkt.size, 20)
342+
343+
316344
if __name__ == "__main__":
317345
unittest.main()

0 commit comments

Comments
 (0)