dissect.cstruct 4.4.dev2__py3-none-any.whl → 4.5.dev1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,13 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, BinaryIO
3
+ from typing import TYPE_CHECKING, Any, BinaryIO
4
4
 
5
5
  from dissect.cstruct.types.base import BaseType
6
6
  from dissect.cstruct.utils import ENDIANNESS_MAP
7
7
 
8
+ if TYPE_CHECKING:
9
+ from typing_extensions import Self
10
+
8
11
 
9
12
  class Int(int, BaseType):
10
13
  """Integer type that can span an arbitrary amount of bytes."""
@@ -12,7 +15,7 @@ class Int(int, BaseType):
12
15
  signed: bool
13
16
 
14
17
  @classmethod
15
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Int:
18
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
16
19
  data = stream.read(cls.size)
17
20
 
18
21
  if len(data) != cls.size:
@@ -21,7 +24,7 @@ class Int(int, BaseType):
21
24
  return cls.from_bytes(data, ENDIANNESS_MAP[cls.cs.endian], signed=cls.signed)
22
25
 
23
26
  @classmethod
24
- def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Int:
27
+ def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
25
28
  result = []
26
29
 
27
30
  while True:
@@ -1,9 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, BinaryIO
3
+ from typing import TYPE_CHECKING, Any, BinaryIO
4
4
 
5
5
  from dissect.cstruct.types.base import BaseType
6
6
 
7
+ if TYPE_CHECKING:
8
+ from typing_extensions import Self
9
+
7
10
 
8
11
  class LEB128(int, BaseType):
9
12
  """Variable-length code compression to store an arbitrarily large integer in a small number of bytes.
@@ -14,7 +17,7 @@ class LEB128(int, BaseType):
14
17
  signed: bool
15
18
 
16
19
  @classmethod
17
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> LEB128:
20
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
18
21
  result = 0
19
22
  shift = 0
20
23
  while True:
@@ -34,7 +37,7 @@ class LEB128(int, BaseType):
34
37
  return cls.__new__(cls, result)
35
38
 
36
39
  @classmethod
37
- def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> LEB128:
40
+ def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[Self]:
38
41
  result = []
39
42
 
40
43
  while True:
@@ -2,27 +2,33 @@ from __future__ import annotations
2
2
 
3
3
  from functools import lru_cache
4
4
  from struct import Struct
5
- from typing import Any, BinaryIO
5
+ from typing import TYPE_CHECKING, Any, BinaryIO, Generic, TypeVar
6
6
 
7
7
  from dissect.cstruct.types.base import EOF, BaseType
8
8
 
9
+ if TYPE_CHECKING:
10
+ from typing_extensions import Self
11
+
9
12
 
10
13
  @lru_cache(1024)
11
14
  def _struct(endian: str, packchar: str) -> Struct:
12
15
  return Struct(f"{endian}{packchar}")
13
16
 
14
17
 
15
- class Packed(BaseType):
18
+ T = TypeVar("T", int, float)
19
+
20
+
21
+ class Packed(BaseType, Generic[T]):
16
22
  """Packed type for Python struct (un)packing."""
17
23
 
18
24
  packchar: str
19
25
 
20
26
  @classmethod
21
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Packed:
27
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
22
28
  return cls._read_array(stream, 1, context)[0]
23
29
 
24
30
  @classmethod
25
- def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> list[Packed]:
31
+ def _read_array(cls, stream: BinaryIO, count: int, context: dict[str, Any] | None = None) -> list[Self]:
26
32
  if count == EOF:
27
33
  data = stream.read()
28
34
  length = len(data)
@@ -39,7 +45,7 @@ class Packed(BaseType):
39
45
  return [cls.__new__(cls, value) for value in fmt.unpack(data)]
40
46
 
41
47
  @classmethod
42
- def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Packed:
48
+ def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
43
49
  result = []
44
50
 
45
51
  fmt = _struct(cls.cs.endian, cls.packchar)
@@ -57,9 +63,9 @@ class Packed(BaseType):
57
63
  return result
58
64
 
59
65
  @classmethod
60
- def _write(cls, stream: BinaryIO, data: Packed) -> int:
66
+ def _write(cls, stream: BinaryIO, data: Packed[T]) -> int:
61
67
  return stream.write(_struct(cls.cs.endian, cls.packchar).pack(data))
62
68
 
63
69
  @classmethod
64
- def _write_array(cls, stream: BinaryIO, data: list[Packed]) -> int:
70
+ def _write_array(cls, stream: BinaryIO, data: list[Packed[T]]) -> int:
65
71
  return stream.write(_struct(cls.cs.endian, f"{len(data)}{cls.packchar}").pack(*data))
@@ -1,22 +1,27 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import Any, BinaryIO
3
+ from typing import TYPE_CHECKING, Any, BinaryIO, Generic, TypeVar
4
4
 
5
5
  from dissect.cstruct.exceptions import NullPointerDereference
6
- from dissect.cstruct.types.base import BaseType, MetaType
6
+ from dissect.cstruct.types.base import BaseType
7
7
  from dissect.cstruct.types.char import Char
8
8
  from dissect.cstruct.types.void import Void
9
9
 
10
+ if TYPE_CHECKING:
11
+ from typing_extensions import Self
10
12
 
11
- class Pointer(int, BaseType):
13
+ T = TypeVar("T", bound=BaseType)
14
+
15
+
16
+ class Pointer(int, BaseType, Generic[T]):
12
17
  """Pointer to some other type."""
13
18
 
14
- type: MetaType
19
+ type: type[T]
15
20
  _stream: BinaryIO | None
16
21
  _context: dict[str, Any] | None
17
- _value: BaseType
22
+ _value: T | None
18
23
 
19
- def __new__(cls, value: int, stream: BinaryIO | None, context: dict[str, Any] | None = None) -> Pointer: # noqa: PYI034
24
+ def __new__(cls, value: int, stream: BinaryIO | None, context: dict[str, Any] | None = None) -> Self:
20
25
  obj = super().__new__(cls, value)
21
26
  obj._stream = stream
22
27
  obj._context = context
@@ -32,52 +37,52 @@ class Pointer(int, BaseType):
32
37
  def __getattr__(self, attr: str) -> Any:
33
38
  return getattr(self.dereference(), attr)
34
39
 
35
- def __add__(self, other: int) -> Pointer:
40
+ def __add__(self, other: int) -> Self:
36
41
  return type.__call__(self.__class__, int.__add__(self, other), self._stream, self._context)
37
42
 
38
- def __sub__(self, other: int) -> Pointer:
43
+ def __sub__(self, other: int) -> Self:
39
44
  return type.__call__(self.__class__, int.__sub__(self, other), self._stream, self._context)
40
45
 
41
- def __mul__(self, other: int) -> Pointer:
46
+ def __mul__(self, other: int) -> Self:
42
47
  return type.__call__(self.__class__, int.__mul__(self, other), self._stream, self._context)
43
48
 
44
- def __floordiv__(self, other: int) -> Pointer:
49
+ def __floordiv__(self, other: int) -> Self:
45
50
  return type.__call__(self.__class__, int.__floordiv__(self, other), self._stream, self._context)
46
51
 
47
- def __mod__(self, other: int) -> Pointer:
52
+ def __mod__(self, other: int) -> Self:
48
53
  return type.__call__(self.__class__, int.__mod__(self, other), self._stream, self._context)
49
54
 
50
- def __pow__(self, other: int) -> Pointer:
55
+ def __pow__(self, other: int) -> Self:
51
56
  return type.__call__(self.__class__, int.__pow__(self, other), self._stream, self._context)
52
57
 
53
- def __lshift__(self, other: int) -> Pointer:
58
+ def __lshift__(self, other: int) -> Self:
54
59
  return type.__call__(self.__class__, int.__lshift__(self, other), self._stream, self._context)
55
60
 
56
- def __rshift__(self, other: int) -> Pointer:
61
+ def __rshift__(self, other: int) -> Self:
57
62
  return type.__call__(self.__class__, int.__rshift__(self, other), self._stream, self._context)
58
63
 
59
- def __and__(self, other: int) -> Pointer:
64
+ def __and__(self, other: int) -> Self:
60
65
  return type.__call__(self.__class__, int.__and__(self, other), self._stream, self._context)
61
66
 
62
- def __xor__(self, other: int) -> Pointer:
67
+ def __xor__(self, other: int) -> Self:
63
68
  return type.__call__(self.__class__, int.__xor__(self, other), self._stream, self._context)
64
69
 
65
- def __or__(self, other: int) -> Pointer:
70
+ def __or__(self, other: int) -> Self:
66
71
  return type.__call__(self.__class__, int.__or__(self, other), self._stream, self._context)
67
72
 
68
73
  @classmethod
69
- def __default__(cls) -> Pointer:
74
+ def __default__(cls) -> Self:
70
75
  return cls.__new__(cls, cls.cs.pointer.__default__(), None, None)
71
76
 
72
77
  @classmethod
73
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Pointer:
78
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self:
74
79
  return cls.__new__(cls, cls.cs.pointer._read(stream, context), stream, context)
75
80
 
76
81
  @classmethod
77
82
  def _write(cls, stream: BinaryIO, data: int) -> int:
78
83
  return cls.cs.pointer._write(stream, data)
79
84
 
80
- def dereference(self) -> Any:
85
+ def dereference(self) -> T:
81
86
  if self == 0 or self._stream is None:
82
87
  raise NullPointerDereference
83
88
 
@@ -7,6 +7,7 @@ from functools import lru_cache
7
7
  from itertools import chain
8
8
  from operator import attrgetter
9
9
  from textwrap import dedent
10
+ from types import FunctionType
10
11
  from typing import TYPE_CHECKING, Any, BinaryIO, Callable
11
12
 
12
13
  from dissect.cstruct.bitbuffer import BitBuffer
@@ -23,12 +24,15 @@ if TYPE_CHECKING:
23
24
  from collections.abc import Iterator
24
25
  from types import FunctionType
25
26
 
27
+ from typing_extensions import Self
28
+
26
29
 
27
30
  class Field:
28
31
  """Structure field."""
29
32
 
30
- def __init__(self, name: str, type_: MetaType, bits: int | None = None, offset: int | None = None):
31
- self.name = name
33
+ def __init__(self, name: str | None, type_: type[BaseType], bits: int | None = None, offset: int | None = None):
34
+ self.name = name # The name of the field, or None if anonymous
35
+ self._name = name or type_.__name__ # The name of the field, or the type name if anonymous
32
36
  self.type = type_
33
37
  self.bits = bits
34
38
  self.offset = offset
@@ -57,13 +61,13 @@ class StructureMetaType(MetaType):
57
61
  __updating__ = False
58
62
  __compiled__ = False
59
63
 
60
- def __new__(metacls, name: str, bases: tuple[type, ...], classdict: dict[str, Any]) -> MetaType:
64
+ def __new__(metacls, name: str, bases: tuple[type, ...], classdict: dict[str, Any]) -> Self: # type: ignore
61
65
  if (fields := classdict.pop("fields", None)) is not None:
62
66
  metacls._update_fields(metacls, fields, align=classdict.get("__align__", False), classdict=classdict)
63
67
 
64
68
  return super().__new__(metacls, name, bases, classdict)
65
69
 
66
- def __call__(cls, *args, **kwargs) -> Structure:
70
+ def __call__(cls, *args, **kwargs) -> Self: # type: ignore
67
71
  if (
68
72
  cls.__fields__
69
73
  and len(args) == len(cls.__fields__) == 1
@@ -81,26 +85,28 @@ class StructureMetaType(MetaType):
81
85
 
82
86
  return super().__call__(*args, **kwargs)
83
87
 
84
- def _update_fields(cls, fields: list[Field], align: bool = False, classdict: dict[str, Any] | None = None) -> None:
88
+ def _update_fields(
89
+ cls, fields: list[Field], align: bool = False, classdict: dict[str, Any] | None = None
90
+ ) -> dict[str, Any]:
85
91
  classdict = classdict or {}
86
92
 
87
93
  lookup = {}
88
94
  raw_lookup = {}
89
95
  field_names = []
90
96
  for field in fields:
91
- if field.name in lookup and field.name != "_":
92
- raise ValueError(f"Duplicate field name: {field.name}")
97
+ if field._name in lookup and field._name != "_":
98
+ raise ValueError(f"Duplicate field name: {field._name}")
93
99
 
94
- if isinstance(field.type, StructureMetaType) and field.type.__anonymous__:
100
+ if isinstance(field.type, StructureMetaType) and field.name is None:
95
101
  for anon_field in field.type.fields.values():
96
- attr = f"{field.name}.{anon_field.name}"
102
+ attr = f"{field._name}.{anon_field.name}"
97
103
  classdict[anon_field.name] = property(attrgetter(attr), attrsetter(attr))
98
104
 
99
105
  lookup.update(field.type.fields)
100
106
  else:
101
- lookup[field.name] = field
107
+ lookup[field._name] = field
102
108
 
103
- raw_lookup[field.name] = field
109
+ raw_lookup[field._name] = field
104
110
 
105
111
  field_names = lookup.keys()
106
112
  classdict["fields"] = lookup
@@ -133,7 +139,7 @@ class StructureMetaType(MetaType):
133
139
  classdict["__compiled__"] = True
134
140
  except Exception:
135
141
  # Revert _read to the slower loop based method
136
- classdict["_read"] = classmethod(StructureMetaType._read)
142
+ classdict["_read"] = classmethod(Structure._read.__func__)
137
143
  classdict["__compiled__"] = False
138
144
 
139
145
  # TODO: compile _write
@@ -233,7 +239,7 @@ class StructureMetaType(MetaType):
233
239
  # The structure size is whatever the currently calculated offset is
234
240
  return offset, alignment
235
241
 
236
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Structure:
242
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self: # type: ignore
237
243
  bit_buffer = BitBuffer(stream, cls.cs.endian)
238
244
  struct_start = stream.tell()
239
245
 
@@ -241,7 +247,6 @@ class StructureMetaType(MetaType):
241
247
  sizes = {}
242
248
  for field in cls.__fields__:
243
249
  offset = stream.tell()
244
- field_type = cls.cs.resolve(field.type)
245
250
 
246
251
  if field.offset is not None and offset != struct_start + field.offset:
247
252
  # Field is at a specific offset, either alligned or added that way
@@ -254,21 +259,20 @@ class StructureMetaType(MetaType):
254
259
  stream.seek(offset)
255
260
 
256
261
  if field.bits:
257
- if isinstance(field_type, EnumMetaType):
258
- value = field_type(bit_buffer.read(field_type.type, field.bits))
262
+ if isinstance(field.type, EnumMetaType):
263
+ value = field.type(bit_buffer.read(field.type.type, field.bits))
259
264
  else:
260
- value = bit_buffer.read(field_type, field.bits)
265
+ value = bit_buffer.read(field.type, field.bits)
261
266
 
262
- if field.name:
263
- result[field.name] = value
267
+ result[field._name] = value
264
268
  continue
269
+
265
270
  bit_buffer.reset()
266
271
 
267
- value = field_type._read(stream, result)
272
+ value = field.type._read(stream, result)
268
273
 
269
- if field.name:
270
- sizes[field.name] = stream.tell() - offset
271
- result[field.name] = value
274
+ sizes[field._name] = stream.tell() - offset
275
+ result[field._name] = value
272
276
 
273
277
  if cls.__align__:
274
278
  # Align the stream
@@ -281,7 +285,7 @@ class StructureMetaType(MetaType):
281
285
  obj._values = result
282
286
  return obj
283
287
 
284
- def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[Structure]:
288
+ def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> list[Self]: # type: ignore
285
289
  result = []
286
290
 
287
291
  while obj := cls._read(stream, context):
@@ -325,7 +329,7 @@ class StructureMetaType(MetaType):
325
329
  stream.write(b"\x00" * align_pad)
326
330
  offset += align_pad
327
331
 
328
- value = getattr(data, field.name, None)
332
+ value = getattr(data, field._name, None)
329
333
  if value is None:
330
334
  value = field_type.__default__()
331
335
 
@@ -347,7 +351,7 @@ class StructureMetaType(MetaType):
347
351
 
348
352
  return num
349
353
 
350
- def add_field(cls, name: str, type_: BaseType, bits: int | None = None, offset: int | None = None) -> None:
354
+ def add_field(cls, name: str, type_: type[BaseType], bits: int | None = None, offset: int | None = None) -> None:
351
355
  field = Field(name, type_, bits=bits, offset=offset)
352
356
  cls.__fields__.append(field)
353
357
 
@@ -401,7 +405,7 @@ class Structure(BaseType, metaclass=StructureMetaType):
401
405
  class UnionMetaType(StructureMetaType):
402
406
  """Base metaclass for cstruct union type classes."""
403
407
 
404
- def __call__(cls, *args, **kwargs) -> Union:
408
+ def __call__(cls, *args, **kwargs) -> Self: # type: ignore
405
409
  obj: Union = super().__call__(*args, **kwargs)
406
410
 
407
411
  # Calling with non-stream args or kwargs means we are initializing with values
@@ -412,7 +416,7 @@ class UnionMetaType(StructureMetaType):
412
416
 
413
417
  # User (partial) initialization, rebuild the union
414
418
  # First user-provided field is the one used to rebuild the union
415
- arg_fields = (field.name for _, field in zip(args, cls.__fields__))
419
+ arg_fields = (field._name for _, field in zip(args, cls.__fields__))
416
420
  kwarg_fields = (name for name in kwargs if name in cls.lookup)
417
421
  if (first_field := next(chain(arg_fields, kwarg_fields), None)) is not None:
418
422
  obj._rebuild(first_field)
@@ -469,12 +473,12 @@ class UnionMetaType(StructureMetaType):
469
473
  buf.seek(offset + start)
470
474
  value = field_type._read(buf, result)
471
475
 
472
- sizes[field.name] = buf.tell() - start
473
- result[field.name] = value
476
+ sizes[field._name] = buf.tell() - start
477
+ result[field._name] = value
474
478
 
475
479
  return result, sizes
476
480
 
477
- def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Union:
481
+ def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self: # type: ignore
478
482
  if cls.size is None:
479
483
  start = stream.tell()
480
484
  result, sizes = cls._read_fields(stream, context)
@@ -517,13 +521,13 @@ class UnionMetaType(StructureMetaType):
517
521
 
518
522
  # Try to write by largest field
519
523
  for field in fields:
520
- if isinstance(field.type, StructureMetaType) and field.type.__anonymous__:
524
+ if isinstance(field.type, StructureMetaType) and field.name is None:
521
525
  # Prefer to write regular fields initially
522
526
  anonymous_struct = field.type
523
527
  continue
524
528
 
525
529
  # Write the value
526
- field.type._write(stream, getattr(data, field.name))
530
+ field.type._write(stream, getattr(data, field._name))
527
531
  break
528
532
 
529
533
  # If we haven't written anything yet and we initially skipped an anonymous struct, write it now
@@ -582,9 +586,9 @@ class Union(Structure, metaclass=UnionMetaType):
582
586
  def _proxy_structure(value: Structure) -> None:
583
587
  for field in value.__class__.__fields__:
584
588
  if issubclass(field.type, Structure):
585
- nested_value = getattr(value, field.name)
586
- proxy = UnionProxy(self, field.name, nested_value)
587
- object.__setattr__(value, field.name, proxy)
589
+ nested_value = getattr(value, field._name)
590
+ proxy = UnionProxy(self, field._name, nested_value)
591
+ object.__setattr__(value, field._name, proxy)
588
592
  _proxy_structure(nested_value)
589
593
 
590
594
  _proxy_structure(self)
@@ -771,7 +775,7 @@ def _generate_structure__init__(fields: list[Field]) -> FunctionType:
771
775
  Args:
772
776
  fields: List of field names.
773
777
  """
774
- field_names = [field.name for field in fields]
778
+ field_names = [field._name for field in fields]
775
779
 
776
780
  template: FunctionType = _make_structure__init__(len(field_names))
777
781
  return type(template)(
@@ -791,12 +795,15 @@ def _generate_union__init__(fields: list[Field]) -> FunctionType:
791
795
  Args:
792
796
  fields: List of field names.
793
797
  """
794
- field_names = [field.name for field in fields]
798
+ field_names = [field._name for field in fields]
795
799
 
796
800
  template: FunctionType = _make_union__init__(len(field_names))
797
801
  return type(template)(
798
802
  template.__code__.replace(
799
- co_consts=(None, *sum([(field.name, field.type.__default__()) for field in fields], ())),
803
+ co_consts=(
804
+ None,
805
+ *sum([(field._name, field.type.__default__()) for field in fields], ()),
806
+ ),
800
807
  co_varnames=("self", *field_names),
801
808
  ),
802
809
  template.__globals__,
@@ -3,17 +3,21 @@ from __future__ import annotations
3
3
  import sys
4
4
  from typing import Any, BinaryIO, ClassVar
5
5
 
6
- from dissect.cstruct.types.base import EOF, ArrayMetaType, BaseType
6
+ from dissect.cstruct.types.base import EOF, BaseArray, BaseType
7
7
 
8
8
 
9
- class WcharArray(str, BaseType, metaclass=ArrayMetaType):
9
+ class WcharArray(str, BaseArray):
10
10
  """Wide-character array type for reading and writing UTF-16 strings."""
11
11
 
12
12
  __slots__ = ()
13
13
 
14
+ @classmethod
15
+ def __default__(cls) -> WcharArray:
16
+ return type.__call__(cls, "\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
17
+
14
18
  @classmethod
15
19
  def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> WcharArray:
16
- return type.__call__(cls, ArrayMetaType._read(cls, stream, context))
20
+ return type.__call__(cls, super()._read(stream, context))
17
21
 
18
22
  @classmethod
19
23
  def _write(cls, stream: BinaryIO, data: str) -> int:
@@ -21,10 +25,6 @@ class WcharArray(str, BaseType, metaclass=ArrayMetaType):
21
25
  data += "\x00"
22
26
  return stream.write(data.encode(Wchar.__encoding_map__[cls.cs.endian]))
23
27
 
24
- @classmethod
25
- def __default__(cls) -> WcharArray:
26
- return type.__call__(cls, "\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
27
-
28
28
 
29
29
  class Wchar(str, BaseType):
30
30
  """Wide-character type for reading and writing UTF-16 characters."""
@@ -40,6 +40,10 @@ class Wchar(str, BaseType):
40
40
  "!": "utf-16-be",
41
41
  }
42
42
 
43
+ @classmethod
44
+ def __default__(cls) -> Wchar:
45
+ return type.__call__(cls, "\x00")
46
+
43
47
  @classmethod
44
48
  def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Wchar:
45
49
  return cls._read_array(stream, 1, context)
@@ -76,7 +80,3 @@ class Wchar(str, BaseType):
76
80
  @classmethod
77
81
  def _write(cls, stream: BinaryIO, data: str) -> int:
78
82
  return stream.write(data.encode(cls.__encoding_map__[cls.cs.endian]))
79
-
80
- @classmethod
81
- def __default__(cls) -> Wchar:
82
- return type.__call__(cls, "\x00")
dissect/cstruct/utils.py CHANGED
@@ -11,6 +11,7 @@ from dissect.cstruct.types.structure import Structure
11
11
 
12
12
  if TYPE_CHECKING:
13
13
  from collections.abc import Iterator
14
+ from typing import Literal
14
15
 
15
16
  COLOR_RED = "\033[1;31m"
16
17
  COLOR_GREEN = "\033[1;32m"
@@ -31,7 +32,7 @@ COLOR_BG_WHITE = "\033[1;47m\033[1;30m"
31
32
 
32
33
  PRINTABLE = string.digits + string.ascii_letters + string.punctuation + " "
33
34
 
34
- ENDIANNESS_MAP = {
35
+ ENDIANNESS_MAP: dict[str, Literal["big", "little"]] = {
35
36
  "@": sys.byteorder,
36
37
  "=": sys.byteorder,
37
38
  "<": "little",
@@ -40,7 +41,7 @@ ENDIANNESS_MAP = {
40
41
  "network": "big",
41
42
  }
42
43
 
43
- Palette = list[tuple[str, str]]
44
+ Palette = list[tuple[int, str]]
44
45
 
45
46
 
46
47
  def _hexdump(data: bytes, palette: Palette | None = None, offset: int = 0, prefix: str = "") -> Iterator[str]:
@@ -158,10 +159,10 @@ def _dumpstruct(
158
159
 
159
160
  if color:
160
161
  foreground, background = colors[ci % len(colors)]
161
- palette.append((structure._sizes[field.name], background))
162
+ palette.append((structure._sizes[field._name], background))
162
163
  ci += 1
163
164
 
164
- value = getattr(structure, field.name)
165
+ value = getattr(structure, field._name)
165
166
  if isinstance(value, (str, Pointer, Enum)):
166
167
  value = repr(value)
167
168
  elif isinstance(value, int):
@@ -169,12 +170,12 @@ def _dumpstruct(
169
170
  elif isinstance(value, list):
170
171
  value = pprint.pformat(value)
171
172
  if "\n" in value:
172
- value = value.replace("\n", f"\n{' ' * (len(field.name) + 4)}")
173
+ value = value.replace("\n", f"\n{' ' * (len(field._name) + 4)}")
173
174
 
174
175
  if color:
175
- out.append(f"- {foreground}{field.name}{COLOR_NORMAL}: {value}")
176
+ out.append(f"- {foreground}{field._name}{COLOR_NORMAL}: {value}")
176
177
  else:
177
- out.append(f"- {field.name}: {value}")
178
+ out.append(f"- {field._name}: {value}")
178
179
 
179
180
  out = "\n".join(out)
180
181
 
@@ -184,7 +185,7 @@ def _dumpstruct(
184
185
  print()
185
186
  print(out)
186
187
  elif output == "string":
187
- return "\n".join(["", hexdump(data, palette, offset=offset, output="string"), "", out])
188
+ return f"\n{hexdump(data, palette, offset=offset, output='string')}\n\n{out}"
188
189
  return None
189
190
 
190
191
 
@@ -210,7 +211,7 @@ def dumpstruct(
210
211
 
211
212
  if isinstance(obj, Structure):
212
213
  return _dumpstruct(obj, obj.dumps(), offset, color, output)
213
- if issubclass(obj, Structure) and data:
214
+ if issubclass(obj, Structure) and data is not None:
214
215
  return _dumpstruct(obj(data), data, offset, color, output)
215
216
  raise ValueError("Invalid arguments")
216
217
 
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 4.4.dev2
3
+ Version: 4.5.dev1
4
4
  Summary: A Dissect module implementing a parser for C-like structures: structure parsing in Python made easy
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Apache License 2.0
@@ -22,6 +22,9 @@ Requires-Python: ~=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  License-File: LICENSE
24
24
  License-File: COPYRIGHT
25
+ Provides-Extra: dev
26
+ Requires-Dist: typing_extensions; extra == "dev"
27
+ Dynamic: license-file
25
28
 
26
29
  # dissect.cstruct
27
30
 
@@ -0,0 +1,29 @@
1
+ dissect/cstruct/__init__.py,sha256=mILXFvtajLG5EqAPSeCJ2g5klZSnRbu7uqyGcH_DF2k,1344
2
+ dissect/cstruct/bitbuffer.py,sha256=bid_N4ZsaTeC3x5Fzs8viUrldT863dMPtEDih20Nt6k,2644
3
+ dissect/cstruct/compiler.py,sha256=luyUXwPlOXFKRoqztNFY7YeENk_WEjsponvNfnjMKT4,14053
4
+ dissect/cstruct/cstruct.py,sha256=IVvue9unr0T-ZZXSVM9mjIovwG4YdopKOqktmB8vIDk,19379
5
+ dissect/cstruct/exceptions.py,sha256=WqsUW4OiIpRLQLvixfLrfKl0rtvU1qx7pvfBrz9Sz-I,293
6
+ dissect/cstruct/expression.py,sha256=BJeQJYKwTvoDOIuv48rtfDp1kvyAjIkxpy2-VZMZ7Gc,10870
7
+ dissect/cstruct/parser.py,sha256=v_D6x46J1EB3EJF5OssLf8nedYcShpQke4FJK4G7AyU,20910
8
+ dissect/cstruct/utils.py,sha256=yItxcZ-IG9EZGABXap0456Y3LjLjh8zNEd5KOBD7YmU,10525
9
+ dissect/cstruct/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ dissect/cstruct/tools/stubgen.py,sha256=Cg_nHTp-KMm7eI0kCCeWflaYXeCGDFn728IdX60o508,7332
11
+ dissect/cstruct/types/__init__.py,sha256=e9Z6BhhAPquT4MJoK3i_SjC9AgIGWFH_-tAVFCFBiqE,847
12
+ dissect/cstruct/types/base.py,sha256=UFDolAxpVW1W9JoVlykEf96wBzt_suXvuYy08iB54Sw,9677
13
+ dissect/cstruct/types/char.py,sha256=9XYCjCF0D_5o3EYnmIcjev-5GI2NzxOhd1ct1xF6SAM,2457
14
+ dissect/cstruct/types/enum.py,sha256=9mzlIeHYLHOdNtTmiPgGNvGBYe71HeDX5qQDspX5O80,7409
15
+ dissect/cstruct/types/flag.py,sha256=7xxQjFEDPHSQOxBHgcc75WzPiAxflOquwr7Z7c3erLE,2347
16
+ dissect/cstruct/types/int.py,sha256=MGsdUxJt-lj3nl9wzAgGX8cb_vJOtGLiCHwYZs1j_IA,1156
17
+ dissect/cstruct/types/leb128.py,sha256=kDmsWGXX6vr1bm4uJcilsrQ7JABdQRio16rBZV21pno,2243
18
+ dissect/cstruct/types/packed.py,sha256=tpZpb8tiSMXr1np6p0-nZqT4sY7zieO-4E16lmMmqJA,2128
19
+ dissect/cstruct/types/pointer.py,sha256=PqeJOoNBUfMd5uO2kpF6OHeW7Q6R1N1W1V4q1MakN4I,3912
20
+ dissect/cstruct/types/structure.py,sha256=6OBxXr7hMYhufe4GvHkBHpME1fvJQYlfbFxchouHIIc,29814
21
+ dissect/cstruct/types/void.py,sha256=VL2qJ86rq-oBUnkBprNsPPgPGgHV6UENRJTQ2jw0rxc,669
22
+ dissect/cstruct/types/wchar.py,sha256=N9Y_XX2_hZEe2DwepJjxsB6xuRJ6zINRalcUofR59kY,2608
23
+ dissect_cstruct-4.5.dev1.dist-info/licenses/COPYRIGHT,sha256=H-18RXfshdH9AdHwW2eO1Xa-5s6tY5eipHh5c0whDu4,316
24
+ dissect_cstruct-4.5.dev1.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
25
+ dissect_cstruct-4.5.dev1.dist-info/METADATA,sha256=eeA43wdx2j8YrTB3tP3fda3_Iv1NsMkIbbhW1ThT53g,8649
26
+ dissect_cstruct-4.5.dev1.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
27
+ dissect_cstruct-4.5.dev1.dist-info/entry_points.txt,sha256=z53zqZqwD2OLrAkRwrP4wTeiU9CQe7xrMly0T2c0_wQ,71
28
+ dissect_cstruct-4.5.dev1.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
29
+ dissect_cstruct-4.5.dev1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.8.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cstruct-stubgen = dissect.cstruct.tools.stubgen:main