Skip to content

Commit 91f2347

Browse files
authored
Add copy method to cstruct instance (#140)
1 parent 9f12b1e commit 91f2347

2 files changed

Lines changed: 58 additions & 0 deletions

File tree

dissect/cstruct/cstruct.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import annotations
22

3+
import copy
34
import ctypes as _ctypes
45
import struct
56
import sys
@@ -25,6 +26,7 @@
2526
Void,
2627
Wchar,
2728
)
29+
from dissect.cstruct.types.base import MetaType
2830

2931
if TYPE_CHECKING:
3032
from collections.abc import Iterable
@@ -206,6 +208,25 @@ def __getattr__(self, attr: str) -> Any:
206208

207209
raise AttributeError(f"Invalid attribute: {attr}")
208210

211+
def __copy__(self) -> cstruct:
212+
cs = cstruct(endian=self.endian, pointer=self.pointer.__name__)
213+
cs._anonymous_count = self._anonymous_count
214+
cs.consts = self.consts.copy()
215+
cs.lookups = self.lookups.copy()
216+
cs.includes = self.includes.copy()
217+
218+
# Update typedefs to point to the new cstruct instance
219+
for name, type in self.typedefs.items():
220+
if name in cs.typedefs:
221+
continue
222+
223+
if isinstance(type, MetaType):
224+
new_type = copy.copy(type)
225+
new_type.cs = cs
226+
cs.typedefs[name] = new_type
227+
228+
return cs
229+
209230
def _next_anonymous(self) -> str:
210231
name = f"__anonymous_{self._anonymous_count}__"
211232
self._anonymous_count += 1
@@ -328,6 +349,14 @@ def resolve(self, name: type[BaseType] | str) -> type[BaseType]:
328349

329350
raise ResolveError(f"Recursion limit exceeded while resolving type {name}")
330351

352+
def copy(self) -> cstruct:
353+
"""Create a copy of this cstruct instance.
354+
355+
Returns:
356+
A new cstruct instance with the same types and settings as this one.
357+
"""
358+
return copy.copy(self)
359+
331360
def _make_type(
332361
self,
333362
name: str,

tests/test_basic.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,3 +532,32 @@ def test_linked_list(cs: cstruct) -> None:
532532

533533
assert obj.data == 1
534534
assert obj.next.data == 2
535+
536+
537+
def test_copy(cs: cstruct) -> None:
538+
"""Test that copying a cstruct instance works as expected."""
539+
cdef = """
540+
struct test {
541+
uint32 a;
542+
};
543+
"""
544+
cs.load(cdef)
545+
546+
cs_copy = cs.copy()
547+
548+
# Modify the copy and verify the original is unchanged
549+
cs_copy.load("""
550+
struct test2 {
551+
uint16 b;
552+
};
553+
""")
554+
555+
assert "test" in cs.typedefs
556+
assert "test2" not in cs.typedefs
557+
558+
assert "test" in cs_copy.typedefs
559+
assert "test2" in cs_copy.typedefs
560+
561+
# Verify that types in the copied cstruct reference the copied cstruct
562+
assert cs_copy.resolve("test").cs is cs_copy
563+
assert cs_copy.resolve("test2").cs is cs_copy

0 commit comments

Comments
 (0)