Skip to content

Commit cd2ad25

Browse files
committed
Fix default initialization for various types
1 parent 09830d2 commit cd2ad25

25 files changed

Lines changed: 860 additions & 251 deletions

dissect/cstruct/cstruct.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ def _make_type(
321321
size: int | None,
322322
*,
323323
alignment: int | None = None,
324-
attrs: dict[str, Any] = None,
324+
attrs: dict[str, Any] | None = None,
325325
) -> type[BaseType]:
326326
"""Create a new type class bound to this cstruct instance.
327327

dissect/cstruct/types/base.py

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,14 @@ def __call__(cls, *args, **kwargs) -> MetaType | BaseType:
3636
if len(args) == 1 and not isinstance(args[0], cls):
3737
stream = args[0]
3838

39-
if hasattr(stream, "read"):
39+
if _is_readable_type(stream):
4040
return cls._read(stream)
4141

4242
if issubclass(cls, bytes) and isinstance(stream, bytes) and len(stream) == cls.size:
4343
# Shortcut for char/bytes type
4444
return type.__call__(cls, *args, **kwargs)
4545

46-
if isinstance(stream, (bytes, memoryview, bytearray)):
46+
if _is_buffer_type(stream):
4747
return cls.reads(stream)
4848

4949
return type.__call__(cls, *args, **kwargs)
@@ -83,7 +83,7 @@ def read(cls, obj: BinaryIO | bytes) -> BaseType:
8383
Returns:
8484
The parsed value of this type.
8585
"""
86-
if isinstance(obj, (bytes, memoryview, bytearray)):
86+
if _is_buffer_type(obj):
8787
return cls.reads(obj)
8888

8989
return cls._read(obj)
@@ -113,7 +113,7 @@ def dumps(cls, value: Any) -> bytes:
113113
cls._write(out, value)
114114
return out.getvalue()
115115

116-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> BaseType:
116+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> BaseType:
117117
"""Internal function for reading value.
118118
119119
Must be implemented per type.
@@ -124,7 +124,7 @@ def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> BaseType:
124124
"""
125125
raise NotImplementedError()
126126

127-
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = None) -> list[BaseType]:
127+
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> list[BaseType]:
128128
"""Internal function for reading array values.
129129
130130
Allows type implementations to do optimized reading for their type.
@@ -145,7 +145,7 @@ def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = Non
145145

146146
return [cls._read(stream, context) for _ in range(count)]
147147

148-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> list[BaseType]:
148+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[BaseType]:
149149
"""Internal function for reading null-terminated data.
150150
151151
"Null" is type specific, so must be implemented per type.
@@ -179,7 +179,7 @@ def _write_0(cls, stream: BinaryIO, array: list[BaseType]) -> int:
179179
stream: The stream to read from.
180180
array: The array to write.
181181
"""
182-
return cls._write_array(stream, array + [cls()])
182+
return cls._write_array(stream, array + [cls.default()])
183183

184184

185185
class _overload:
@@ -225,7 +225,10 @@ class ArrayMetaType(MetaType):
225225
num_entries: int | Expression | None
226226
null_terminated: bool
227227

228-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Array:
228+
def default(cls) -> BaseType:
229+
return type.__call__(cls, [cls.type.default()] * (cls.num_entries if isinstance(cls.num_entries, int) else 0))
230+
231+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Array:
229232
if cls.null_terminated:
230233
return cls.type._read_0(stream, context)
231234

@@ -243,11 +246,6 @@ def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Array:
243246

244247
return cls.type._read_array(stream, num, context)
245248

246-
def default(cls) -> BaseType:
247-
return type.__call__(
248-
cls, [cls.type.default() for _ in range(0 if cls.dynamic or cls.null_terminated else cls.num_entries)]
249-
)
250-
251249

252250
class Array(list, BaseType, metaclass=ArrayMetaType):
253251
"""Implements a fixed or dynamically sized array type.
@@ -261,7 +259,7 @@ class Array(list, BaseType, metaclass=ArrayMetaType):
261259
"""
262260

263261
@classmethod
264-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Array:
262+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Array:
265263
return cls(ArrayMetaType._read(cls, stream, context))
266264

267265
@classmethod
@@ -275,5 +273,13 @@ def _write(cls, stream: BinaryIO, data: list[Any]) -> int:
275273
return cls.type._write_array(stream, data)
276274

277275

276+
def _is_readable_type(value: Any) -> bool:
277+
return hasattr(value, "read")
278+
279+
280+
def _is_buffer_type(value: Any) -> bool:
281+
return isinstance(value, (bytes, memoryview, bytearray))
282+
283+
278284
# As mentioned in the BaseType class, we correctly set the type here
279285
MetaType.ArrayType = Array

dissect/cstruct/types/char.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ class CharArray(bytes, BaseType, metaclass=ArrayMetaType):
99
"""Character array type for reading and writing byte strings."""
1010

1111
@classmethod
12-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> CharArray:
12+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> CharArray:
1313
return type.__call__(cls, ArrayMetaType._read(cls, stream, context))
1414

1515
@classmethod
@@ -35,11 +35,11 @@ class Char(bytes, BaseType):
3535
ArrayType = CharArray
3636

3737
@classmethod
38-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Char:
38+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Char:
3939
return cls._read_array(stream, 1, context)
4040

4141
@classmethod
42-
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = None) -> Char:
42+
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> Char:
4343
if count == 0:
4444
return type.__call__(cls, b"")
4545

@@ -50,7 +50,7 @@ def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = Non
5050
return type.__call__(cls, data)
5151

5252
@classmethod
53-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Char:
53+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Char:
5454
buf = []
5555
while True:
5656
byte = stream.read(1)

dissect/cstruct/types/enum.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __call__(
2727
) -> EnumMetaType:
2828
if name is None:
2929
if value is None:
30-
value = cls.type()
30+
value = cls.type.default()
3131

3232
if not isinstance(value, int):
3333
# value is a parsable value
@@ -64,13 +64,13 @@ def __contains__(cls, value: Any) -> bool:
6464
return True
6565
return value in cls._value2member_map_
6666

67-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Enum:
67+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Enum:
6868
return cls(cls.type._read(stream, context))
6969

70-
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = None) -> list[Enum]:
70+
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> list[Enum]:
7171
return list(map(cls, cls.type._read_array(stream, count, context)))
7272

73-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> list[Enum]:
73+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[Enum]:
7474
return list(map(cls, cls.type._read_0(stream, context)))
7575

7676
def _write(cls, stream: BinaryIO, data: Enum) -> int:
@@ -82,7 +82,7 @@ def _write_array(cls, stream: BinaryIO, array: list[Enum]) -> int:
8282

8383
def _write_0(cls, stream: BinaryIO, array: list[BaseType]) -> int:
8484
data = [entry.value if isinstance(entry, Enum) else entry for entry in array]
85-
return cls._write_array(stream, data + [cls.type()])
85+
return cls._write_array(stream, data + [cls.type.default()])
8686

8787

8888
def _fix_alias_members(cls: type[Enum]) -> None:

dissect/cstruct/types/int.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class Int(int, BaseType):
1212
signed: bool
1313

1414
@classmethod
15-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Int:
15+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Int:
1616
data = stream.read(cls.size)
1717

1818
if len(data) != cls.size:
@@ -21,7 +21,7 @@ def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Int:
2121
return cls.from_bytes(data, ENDIANNESS_MAP[cls.cs.endian], signed=cls.signed)
2222

2323
@classmethod
24-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Int:
24+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Int:
2525
result = []
2626

2727
while True:

dissect/cstruct/types/leb128.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class LEB128(int, BaseType):
1414
signed: bool
1515

1616
@classmethod
17-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> LEB128:
17+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> LEB128:
1818
result = 0
1919
shift = 0
2020
while True:
@@ -35,7 +35,7 @@ def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> LEB128:
3535
return cls.__new__(cls, result)
3636

3737
@classmethod
38-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> LEB128:
38+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> LEB128:
3939
result = []
4040

4141
while True:

dissect/cstruct/types/packed.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,11 @@ class Packed(BaseType):
1818
packchar: str
1919

2020
@classmethod
21-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Packed:
21+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Packed:
2222
return cls._read_array(stream, 1, context)[0]
2323

2424
@classmethod
25-
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = None) -> list[Packed]:
25+
def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> list[Packed]:
2626
if count == EOF:
2727
data = stream.read()
2828
length = len(data)
@@ -39,7 +39,7 @@ def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] = Non
3939
return [cls.__new__(cls, value) for value in fmt.unpack(data)]
4040

4141
@classmethod
42-
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Packed:
42+
def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Packed:
4343
result = []
4444

4545
fmt = _struct(cls.cs.endian, cls.packchar)

dissect/cstruct/types/pointer.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ class Pointer(int, BaseType):
1212
"""Pointer to some other type."""
1313

1414
type: MetaType
15-
_stream: BinaryIO
16-
_context: dict[str, Any]
15+
_stream: BinaryIO | None
16+
_context: dict[str, Any] | None
1717
_value: BaseType
1818

19-
def __new__(cls, value: int, stream: BinaryIO, context: dict[str, Any] = None) -> Pointer:
19+
def __new__(cls, value: int, stream: BinaryIO | None, context: dict[str, Any] | None = None) -> Pointer:
2020
obj = super().__new__(cls, value)
2121
obj._stream = stream
2222
obj._context = context
@@ -66,15 +66,19 @@ def __or__(self, other: int) -> Pointer:
6666
return type.__call__(self.__class__, int.__or__(self, other), self._stream, self._context)
6767

6868
@classmethod
69-
def _read(cls, stream: BinaryIO, context: dict[str, Any] = None) -> Pointer:
69+
def default(cls) -> Pointer:
70+
return cls.__new__(cls, cls.cs.pointer.default(), None, None)
71+
72+
@classmethod
73+
def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Pointer:
7074
return cls.__new__(cls, cls.cs.pointer._read(stream, context), stream, context)
7175

7276
@classmethod
7377
def _write(cls, stream: BinaryIO, data: int) -> int:
7478
return cls.cs.pointer._write(stream, data)
7579

7680
def dereference(self) -> Any:
77-
if self == 0:
81+
if self == 0 or self._stream is None:
7882
raise NullPointerDereference()
7983

8084
if self._value is None and not issubclass(self.type, Void):

0 commit comments

Comments
 (0)