dissect.cstruct 5.0.dev7__py3-none-any.whl → 5.0.dev9__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,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import io
4
+ import warnings
4
5
  from collections import ChainMap
5
6
  from collections.abc import MutableMapping
6
7
  from contextlib import contextmanager
@@ -43,7 +44,7 @@ class Field:
43
44
  self.type = type_
44
45
  self.bits = bits
45
46
  self.offset = offset
46
- self.alignment = type_.alignment or 1
47
+ self.alignment = type_.__alignment__ or 1
47
48
 
48
49
  def __repr__(self) -> str:
49
50
  bits_str = f" : {self.bits}" if self.bits else ""
@@ -53,11 +54,9 @@ class Field:
53
54
  class StructureMetaType(MetaType):
54
55
  """Base metaclass for cstruct structure type classes."""
55
56
 
56
- # TODO: resolve field types in _update_fields, remove resolves elsewhere?
57
-
58
- fields: dict[str, Field]
57
+ __members__: dict[str, Field]
59
58
  """Mapping of field names to :class:`Field` objects, including "folded" fields from anonymous structures."""
60
- lookup: dict[str, Field]
59
+ __lookup__: dict[str, Field]
61
60
  """Mapping of "raw" field names to :class:`Field` objects. E.g. holds the anonymous struct and not its fields."""
62
61
  __fields__: list[Field]
63
62
  """List of :class:`Field` objects for this structure. This is the structures' Single Source Of Truth."""
@@ -70,7 +69,7 @@ class StructureMetaType(MetaType):
70
69
  __static_sizes__: dict[str, int] # Cache of static sizes by field name
71
70
 
72
71
  def __new__(metacls, name: str, bases: tuple[type, ...], classdict: dict[str, Any]) -> Self: # type: ignore
73
- if (fields := classdict.pop("fields", None)) is not None:
72
+ if (fields := classdict.pop("__members__", None)) is not None:
74
73
  metacls._update_fields(metacls, fields, align=classdict.get("__align__", False), classdict=classdict)
75
74
 
76
75
  return super().__new__(metacls, name, bases, classdict)
@@ -96,23 +95,23 @@ class StructureMetaType(MetaType):
96
95
  if field._name in lookup and field._name != "_":
97
96
  raise ValueError(f"Duplicate field name: {field._name}")
98
97
 
99
- if not field.type.dynamic:
100
- static_sizes[field._name] = field.type.size
98
+ if not field.type.__dynamic__:
99
+ static_sizes[field._name] = field.type.__size__
101
100
 
102
101
  if isinstance(field.type, StructureMetaType) and field.name is None:
103
- for anon_field in field.type.fields.values():
102
+ for anon_field in field.type.__members__.values():
104
103
  attr = f"{field._name}.{anon_field.name}"
105
104
  classdict[anon_field.name] = property(attrgetter(attr), attrsetter(attr))
106
105
 
107
- lookup.update(field.type.fields)
106
+ lookup.update(field.type.__members__)
108
107
  else:
109
108
  lookup[field._name] = field
110
109
 
111
110
  raw_lookup[field._name] = field
112
111
 
113
112
  field_names = lookup.keys()
114
- classdict["fields"] = lookup
115
- classdict["lookup"] = raw_lookup
113
+ classdict["__members__"] = lookup
114
+ classdict["__lookup__"] = raw_lookup
116
115
  classdict["__fields__"] = fields
117
116
  classdict["__static_sizes__"] = static_sizes
118
117
  classdict["__bool__"] = _generate__bool__(field_names)
@@ -138,7 +137,9 @@ class StructureMetaType(MetaType):
138
137
  from dissect.cstruct import compiler
139
138
 
140
139
  try:
141
- classdict["_read"] = compiler.Compiler(cls.cs).compile_read(fields, cls.__name__, align=cls.__align__)
140
+ classdict["_read"] = compiler.Compiler(cls.__cs__).compile_read(
141
+ fields, cls.__name__, align=cls.__align__
142
+ )
142
143
  classdict["__compiled__"] = True
143
144
  except Exception:
144
145
  # Revert _read to the slower loop based method
@@ -148,17 +149,17 @@ class StructureMetaType(MetaType):
148
149
  # TODO: compile _write
149
150
  # TODO: generate cached_property for lazy reading
150
151
 
151
- classdict["size"] = size
152
- classdict["alignment"] = alignment
153
- classdict["dynamic"] = size is None
152
+ classdict["__size__"] = size
153
+ classdict["__alignment__"] = alignment
154
+ classdict["__dynamic__"] = size is None
154
155
 
155
156
  return classdict
156
157
 
157
158
  def _calculate_size_and_offsets(cls, fields: list[Field], align: bool = False) -> tuple[int | None, int]:
158
159
  """Iterate all fields in this structure to calculate the field offsets and total structure size.
159
160
 
160
- If a structure has a dynamic field, further field offsets will be set to None and self.dynamic
161
- will be set to True.
161
+ If a structure has a dynamic field, further field offsets will be set to None and ``self.__dynamic__``
162
+ will be set to ``True``.
162
163
  """
163
164
  # The current offset, set to None if we become dynamic
164
165
  offset = 0
@@ -188,7 +189,7 @@ class StructureMetaType(MetaType):
188
189
  field_type = field.type
189
190
 
190
191
  if isinstance(field_type, EnumMetaType):
191
- field_type = field_type.type
192
+ field_type = field_type.__type__
192
193
 
193
194
  # Bit fields have special logic
194
195
  if (
@@ -197,17 +198,17 @@ class StructureMetaType(MetaType):
197
198
  # Moved to a bit field of another type, e.g. uint16 f1 : 8, uint32 f2 : 8;
198
199
  or field_type != bits_type
199
200
  # Still processing a bit field, but it's at a different offset due to alignment or a manual offset
200
- or (bits_type is not None and offset > bits_field_offset + bits_type.size)
201
+ or (bits_type is not None and offset > bits_field_offset + bits_type.__size__)
201
202
  ):
202
203
  # ... if any of this is true, we have to move to the next field
203
204
  bits_type = field_type
204
- bits_count = bits_type.size * 8
205
+ bits_count = bits_type.__size__ * 8
205
206
  bits_remaining = bits_count
206
207
  bits_field_offset = offset
207
208
 
208
209
  if offset is not None:
209
210
  # We're not dynamic, update the structure size and current offset
210
- offset += bits_type.size
211
+ offset += bits_type.__size__
211
212
 
212
213
  field.offset = bits_field_offset
213
214
 
@@ -263,7 +264,7 @@ class StructureMetaType(MetaType):
263
264
 
264
265
  if field.bits:
265
266
  if isinstance(field.type, EnumMetaType):
266
- value = field.type(bit_buffer.read(field.type.type, field.bits))
267
+ value = field.type(bit_buffer.read(field.type.__type__, field.bits))
267
268
  else:
268
269
  value = bit_buffer.read(field.type, field.bits)
269
270
 
@@ -275,12 +276,12 @@ class StructureMetaType(MetaType):
275
276
  value = field.type._read(stream, context=result, endian=endian)
276
277
 
277
278
  result[field._name] = value
278
- if field.type.dynamic:
279
+ if field.type.__dynamic__:
279
280
  sizes[field._name] = stream.tell() - offset
280
281
 
281
282
  if cls.__align__:
282
283
  # Align the stream
283
- stream.seek(-stream.tell() & (cls.alignment - 1), io.SEEK_CUR)
284
+ stream.seek(-stream.tell() & (cls.__alignment__ - 1), io.SEEK_CUR)
284
285
 
285
286
  # Using type.__call__ directly calls the __init__ method of the class
286
287
  # This is faster than calling cls() and bypasses the metaclass __call__ method
@@ -305,7 +306,7 @@ class StructureMetaType(MetaType):
305
306
  field_type = field.type
306
307
 
307
308
  bit_field_type = (
308
- (field_type.type if isinstance(field_type, EnumMetaType) else field_type) if field.bits else None
309
+ (field_type.__type__ if isinstance(field_type, EnumMetaType) else field_type) if field.bits else None
309
310
  )
310
311
  # Current field is not a bit field, but previous was
311
312
  # Or, moved to a bit field of another type, e.g. uint16 f1 : 8, uint32 f2 : 8;
@@ -338,7 +339,7 @@ class StructureMetaType(MetaType):
338
339
 
339
340
  if field.bits:
340
341
  if isinstance(field_type, EnumMetaType):
341
- bit_buffer.write(field_type.type, value.value, field.bits)
342
+ bit_buffer.write(field_type.__type__, value.value, field.bits)
342
343
  else:
343
344
  bit_buffer.write(field_type, value, field.bits)
344
345
  else:
@@ -350,7 +351,7 @@ class StructureMetaType(MetaType):
350
351
 
351
352
  if cls.__align__:
352
353
  # Align the stream
353
- stream.write(b"\x00" * (-stream.tell() & (cls.alignment - 1)))
354
+ stream.write(b"\x00" * (-stream.tell() & (cls.__alignment__ - 1)))
354
355
 
355
356
  return num
356
357
 
@@ -393,13 +394,13 @@ class StructureMetaType(MetaType):
393
394
  def _decompose(type_: MetaType) -> tuple[MetaType, int, list[int | Expression | None]]:
394
395
  dimensions = []
395
396
  while issubclass(type_, BaseArray):
396
- dimensions.append(type_.num_entries)
397
- type_ = type_.type
397
+ dimensions.append(type_.__num_entries__)
398
+ type_ = type_.__type__
398
399
 
399
400
  stars = 0
400
401
  while issubclass(type_, Pointer):
401
402
  stars += 1
402
- type_ = type_.type
403
+ type_ = type_.__type__
403
404
 
404
405
  return type_, stars, dimensions
405
406
 
@@ -413,7 +414,7 @@ class StructureMetaType(MetaType):
413
414
  for token in dim._tokens:
414
415
  if (
415
416
  token.type in IDENTIFIER_TYPES
416
- and token.value in cls.cs.consts
417
+ and token.value in cls.__cs__.consts
417
418
  and token.value not in seen
418
419
  ):
419
420
  deps.append(token.value)
@@ -464,7 +465,7 @@ class StructureMetaType(MetaType):
464
465
  blocks = []
465
466
  for type_ in deps:
466
467
  if isinstance(type_, str):
467
- consts.append(f"#define {type_} {cls.cs.consts[type_]!r}")
468
+ consts.append(f"#define {type_} {cls.__cs__.consts[type_]!r}")
468
469
  elif isinstance(type_, EnumMetaType):
469
470
  blocks.append(type_.cdef())
470
471
  else:
@@ -472,6 +473,26 @@ class StructureMetaType(MetaType):
472
473
 
473
474
  return ("\n".join(consts) + "\n\n" if consts else "") + ("\n\n".join(blocks) if blocks else "")
474
475
 
476
+ @property
477
+ def fields(cls) -> dict[str, Field]:
478
+ """List of :class:`Field` objects for this structure."""
479
+ warnings.warn(
480
+ "The 'fields' property is deprecated, use '__members__' instead.",
481
+ DeprecationWarning,
482
+ stacklevel=2,
483
+ )
484
+ return cls.__members__
485
+
486
+ @property
487
+ def lookup(cls) -> dict[str, Field]:
488
+ """Mapping of "raw" field names to :class:`Field` objects."""
489
+ warnings.warn(
490
+ "The 'lookup' property is deprecated, use '__lookup__' instead.",
491
+ DeprecationWarning,
492
+ stacklevel=2,
493
+ )
494
+ return cls.__lookup__
495
+
475
496
 
476
497
  class Structure(BaseType, metaclass=StructureMetaType):
477
498
  """Base class for cstruct structure type classes.
@@ -493,7 +514,7 @@ class Structure(BaseType, metaclass=StructureMetaType):
493
514
 
494
515
  def __repr__(self) -> str:
495
516
  values = []
496
- for name, field in self.__class__.fields.items():
517
+ for name, field in self.__class__.__members__.items():
497
518
  value = self[name]
498
519
  if issubclass(field.type, int) and not issubclass(field.type, (Pointer, Enum)):
499
520
  value = hex(value)
@@ -529,13 +550,13 @@ class StructureValuesProxy(MutableMapping):
529
550
  raise KeyError(key)
530
551
 
531
552
  def __contains__(self, key: str) -> bool:
532
- return key in self._struct.__class__.fields
553
+ return key in self._struct.__class__.__members__
533
554
 
534
555
  def __iter__(self) -> Iterator[str]:
535
- return iter(self._struct.__class__.fields)
556
+ return iter(self._struct.__class__.__members__)
536
557
 
537
558
  def __len__(self) -> int:
538
- return len(self._struct.__class__.fields)
559
+ return len(self._struct.__class__.__members__)
539
560
 
540
561
  def __repr__(self) -> str:
541
562
  return repr(dict(self))
@@ -555,18 +576,18 @@ class UnionMetaType(StructureMetaType):
555
576
  # If we don't have a _buf attribute, we haven't read from a stream and are initializing with values
556
577
  # Set default internal attributes
557
578
  object.__setattr__(obj, "_buf", None)
558
- object.__setattr__(obj, "_endian", cls.cs.endian)
579
+ object.__setattr__(obj, "_endian", cls.__cs__.endian)
559
580
 
560
581
  # Calling with non-stream args or kwargs means we are initializing with values
561
582
  if (args and not (len(args) == 1 and (_is_readable_type(args[0]) or _is_buffer_type(args[0])))) or kwargs:
562
583
  # We don't support user initialization of dynamic unions yet
563
- if cls.dynamic:
584
+ if cls.__dynamic__:
564
585
  raise NotImplementedError("Initializing a dynamic union is not yet supported")
565
586
 
566
587
  # User (partial) initialization, rebuild the union
567
588
  # First user-provided field is the one used to rebuild the union
568
589
  arg_fields = (field._name for _, field in zip(args, cls.__fields__, strict=False))
569
- kwarg_fields = (name for name in kwargs if name in cls.lookup)
590
+ kwarg_fields = (name for name in kwargs if name in cls.__lookup__)
570
591
  if (first_field := next(chain(arg_fields, kwarg_fields), None)) is not None:
571
592
  obj._rebuild(first_field)
572
593
  elif not args and not kwargs:
@@ -605,12 +626,12 @@ class UnionMetaType(StructureMetaType):
605
626
  result = {}
606
627
  sizes = {}
607
628
 
608
- if cls.size is None:
629
+ if cls.__size__ is None:
609
630
  offset = stream.tell()
610
631
  buf = stream
611
632
  else:
612
633
  offset = 0
613
- buf = io.BytesIO(stream.read(cls.size))
634
+ buf = io.BytesIO(stream.read(cls.__size__))
614
635
 
615
636
  for field in cls.__fields__:
616
637
  field_type = field.type
@@ -623,13 +644,13 @@ class UnionMetaType(StructureMetaType):
623
644
  value = field_type._read(buf, context=result, endian=endian)
624
645
 
625
646
  result[field._name] = value
626
- if field.type.dynamic:
647
+ if field.type.__dynamic__:
627
648
  sizes[field._name] = buf.tell() - start
628
649
 
629
650
  return result, sizes
630
651
 
631
652
  def _read(cls, stream: BinaryIO, *, context: dict[str, Any] | None = None, endian: Endianness) -> Self: # type: ignore
632
- if cls.size is None:
653
+ if cls.__size__ is None:
633
654
  start = stream.tell()
634
655
  result, sizes = cls._read_fields(stream, context=context, endian=endian)
635
656
  size = stream.tell() - start
@@ -638,7 +659,7 @@ class UnionMetaType(StructureMetaType):
638
659
  else:
639
660
  result = {}
640
661
  sizes = {}
641
- buf = stream.read(cls.size)
662
+ buf = stream.read(cls.__size__)
642
663
 
643
664
  # Create the object and set the values
644
665
  # Using type.__call__ directly calls the __init__ method of the class
@@ -650,7 +671,7 @@ class UnionMetaType(StructureMetaType):
650
671
  object.__setattr__(obj, "_buf", buf)
651
672
  object.__setattr__(obj, "_endian", endian)
652
673
 
653
- if cls.size is not None:
674
+ if cls.__size__ is not None:
654
675
  obj._update()
655
676
 
656
677
  # Proxify any nested structures
@@ -659,14 +680,14 @@ class UnionMetaType(StructureMetaType):
659
680
  return obj
660
681
 
661
682
  def _write(cls, stream: BinaryIO, data: Union, *, endian: Endianness) -> int:
662
- if cls.dynamic:
683
+ if cls.__dynamic__:
663
684
  raise NotImplementedError("Writing dynamic unions is not yet supported")
664
685
 
665
686
  offset = stream.tell()
666
687
  expected_offset = offset + len(cls)
667
688
 
668
689
  # Sort by largest field
669
- fields = sorted(cls.__fields__, key=lambda e: e.type.size or 0, reverse=True)
690
+ fields = sorted(cls.__fields__, key=lambda e: e.type.__size__ or 0, reverse=True)
670
691
  anonymous_struct = False
671
692
 
672
693
  # Try to write by largest field
@@ -701,7 +722,7 @@ class Union(Structure, metaclass=UnionMetaType):
701
722
  return self.__class__ is other.__class__ and bytes(self) == bytes(other)
702
723
 
703
724
  def __setattr__(self, attr: str, value: Any) -> None:
704
- if self.__class__.dynamic:
725
+ if self.__class__.__dynamic__:
705
726
  raise NotImplementedError("Modifying a dynamic union is not yet supported")
706
727
 
707
728
  super().__setattr__(attr, value)
@@ -709,10 +730,10 @@ class Union(Structure, metaclass=UnionMetaType):
709
730
 
710
731
  def _rebuild(self, attr: str) -> None:
711
732
  if (cur_buf := getattr(self, "_buf", None)) is None:
712
- cur_buf = b"\x00" * self.__class__.size
733
+ cur_buf = b"\x00" * self.__class__.__size__
713
734
 
714
735
  buf = io.BytesIO(cur_buf)
715
- field = self.__class__.lookup[attr]
736
+ field = self.__class__.__lookup__[attr]
716
737
  if field.offset:
717
738
  buf.seek(field.offset)
718
739
 
@@ -29,7 +29,7 @@ class VoidArray(list, BaseArray):
29
29
  class Void(BaseType):
30
30
  """Void type."""
31
31
 
32
- ArrayType = VoidArray
32
+ __ArrayType__ = VoidArray
33
33
 
34
34
  def __bool__(self) -> bool:
35
35
  return False
@@ -17,7 +17,7 @@ class WcharArray(str, BaseArray):
17
17
 
18
18
  @classmethod
19
19
  def __default__(cls) -> WcharArray:
20
- return type.__call__(cls, "\x00" * (0 if cls.dynamic or cls.null_terminated else cls.num_entries))
20
+ return type.__call__(cls, "\x00" * (0 if cls.__dynamic__ or cls.__null_terminated__ else cls.__num_entries__))
21
21
 
22
22
  @classmethod
23
23
  def _read(cls, stream: BinaryIO, *, context: dict[str, Any] | None = None, endian: Endianness) -> WcharArray:
@@ -25,12 +25,12 @@ class WcharArray(str, BaseArray):
25
25
 
26
26
  @classmethod
27
27
  def _write(cls, stream: BinaryIO, data: str, *, endian: Endianness) -> int:
28
- if cls.null_terminated:
28
+ if cls.__null_terminated__:
29
29
  data += "\x00"
30
30
 
31
- if not cls.dynamic and (remaining := cls.num_entries - (actual_size := len(data))):
31
+ if not cls.__dynamic__ and (remaining := cls.__num_entries__ - (actual_size := len(data))):
32
32
  if remaining < 0:
33
- raise ArraySizeError(f"Expected static array size {cls.num_entries}, got {actual_size} instead.")
33
+ raise ArraySizeError(f"Expected static array size {cls.__num_entries__}, got {actual_size} instead.")
34
34
  data += "\x00" * remaining
35
35
 
36
36
  return stream.write(data.encode(Wchar.__encoding_map__[endian]))
@@ -39,7 +39,7 @@ class WcharArray(str, BaseArray):
39
39
  class Wchar(str, BaseType):
40
40
  """Wide-character type for reading and writing UTF-16 characters."""
41
41
 
42
- ArrayType = WcharArray
42
+ __ArrayType__ = WcharArray
43
43
 
44
44
  __slots__ = ()
45
45
  __encoding_map__: ClassVar[dict[Endianness, str]] = {
dissect/cstruct/util.py CHANGED
@@ -3,15 +3,17 @@ from __future__ import annotations
3
3
  import os
4
4
  import pprint
5
5
  import string
6
+ import struct
6
7
  import sys
7
8
  from enum import Enum
8
- from typing import TYPE_CHECKING
9
+ from functools import lru_cache
10
+ from typing import TYPE_CHECKING, Any
9
11
 
10
12
  if TYPE_CHECKING:
11
13
  from collections.abc import Iterator
12
14
  from typing import Literal
13
15
 
14
- from dissect.cstruct.cstruct import AllowedEndianness
16
+ from dissect.cstruct.cstruct import AllowedEndianness, Endianness
15
17
  from dissect.cstruct.types.base import BaseType
16
18
  from dissect.cstruct.types.structure import Structure
17
19
 
@@ -64,6 +66,36 @@ ENDIANNESS_TO_BYTEORDER_MAP: dict[AllowedEndianness, Literal["big", "little"]] =
64
66
  "big": "big",
65
67
  }
66
68
 
69
+ ENDIANNESS_MAP: dict[AllowedEndianness, Endianness] = {
70
+ "<": "<",
71
+ ">": ">",
72
+ "!": "!",
73
+ "@": "@",
74
+ "=": "=",
75
+ "network": "!",
76
+ "little": "<",
77
+ "big": ">",
78
+ }
79
+
80
+
81
+ def normalize_endianness(endian: AllowedEndianness) -> Endianness:
82
+ """Normalize an endianness string to one of the standard format characters."""
83
+ try:
84
+ return ENDIANNESS_MAP[endian]
85
+ except KeyError:
86
+ raise ValueError(
87
+ f"Invalid endianness: {endian!r}, expected one of {', '.join(ENDIANNESS_MAP.keys())}"
88
+ ) from None
89
+
90
+
91
+ class classproperty:
92
+ def __init__(self, fget: classmethod) -> None:
93
+ self.fget = fget
94
+
95
+ def __get__(self, obj: object | None, owner: type) -> Any:
96
+ return self.fget(owner)
97
+
98
+
67
99
  Palette = list[tuple[int, str]]
68
100
 
69
101
 
@@ -327,6 +359,11 @@ def dumpstruct(
327
359
  return _dumpstruct(obj, obj.dumps(), offset, color, output, autoskip)
328
360
 
329
361
 
362
+ @lru_cache(1024)
363
+ def _struct(endian: str, fmt: str) -> struct.Struct:
364
+ return struct.Struct(f"{endian}{fmt}")
365
+
366
+
330
367
  def pack(value: int, size: int | None = None, endian: AllowedEndianness = "little") -> bytes:
331
368
  """Pack an integer value to a given bit size, endianness.
332
369
 
@@ -400,6 +437,36 @@ def p64(value: int, endian: AllowedEndianness = "little") -> bytes:
400
437
  return pack(value, 64, endian)
401
438
 
402
439
 
440
+ def pf16(value: float, endian: AllowedEndianness = "little") -> bytes:
441
+ """Pack a 16 bit float.
442
+
443
+ Arguments:
444
+ value: Value to pack.
445
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
446
+ """
447
+ return _struct(normalize_endianness(endian), "e").pack(value)
448
+
449
+
450
+ def pf32(value: float, endian: AllowedEndianness = "little") -> bytes:
451
+ """Pack a 32 bit float.
452
+
453
+ Arguments:
454
+ value: Value to pack.
455
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
456
+ """
457
+ return _struct(normalize_endianness(endian), "f").pack(value)
458
+
459
+
460
+ def pf64(value: float, endian: AllowedEndianness = "little") -> bytes:
461
+ """Pack a 64 bit float.
462
+
463
+ Arguments:
464
+ value: Value to pack.
465
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
466
+ """
467
+ return _struct(normalize_endianness(endian), "d").pack(value)
468
+
469
+
403
470
  def u8(value: bytes, endian: AllowedEndianness = "little", sign: bool = False) -> int:
404
471
  """Unpack an 8 bit integer.
405
472
 
@@ -444,6 +511,36 @@ def u64(value: bytes, endian: AllowedEndianness = "little", sign: bool = False)
444
511
  return unpack(value, 64, endian, sign)
445
512
 
446
513
 
514
+ def f16(value: bytes, endian: AllowedEndianness = "little") -> float:
515
+ """Unpack a 16 bit float.
516
+
517
+ Arguments:
518
+ value: Value to unpack.
519
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
520
+ """
521
+ return _struct(normalize_endianness(endian), "e").unpack(value)[0]
522
+
523
+
524
+ def f32(value: bytes, endian: AllowedEndianness = "little") -> float:
525
+ """Unpack a 32 bit float.
526
+
527
+ Arguments:
528
+ value: Value to unpack.
529
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
530
+ """
531
+ return _struct(normalize_endianness(endian), "f").unpack(value)[0]
532
+
533
+
534
+ def f64(value: bytes, endian: AllowedEndianness = "little") -> float:
535
+ """Unpack a 64 bit float.
536
+
537
+ Arguments:
538
+ value: Value to unpack.
539
+ endian: Endianness to use (little, big, network, <, >, !, @ or =).
540
+ """
541
+ return _struct(normalize_endianness(endian), "d").unpack(value)[0]
542
+
543
+
447
544
  def swap(value: int, size: int) -> int:
448
545
  """Swap the endianness of an integer with a given bit size.
449
546
 
@@ -488,7 +585,7 @@ def sizeof(type_: type[BaseType] | BaseType) -> int:
488
585
 
489
586
  def offsetof(type_: type[Structure], name: str) -> int:
490
587
  """Get the offset of a field in a structure."""
491
- if (field := type_.fields.get(name)) is None:
588
+ if (field := type_.__members__.get(name)) is None:
492
589
  raise ValueError(f"Structure '{type_.__name__}' does not have a field named '{name}'")
493
590
  if (offset := field.offset) is None:
494
591
  raise ValueError(f"Field '{field._name}' of structure '{type_.__name__}' does not have a known offset")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dissect.cstruct
3
- Version: 5.0.dev7
3
+ Version: 5.0.dev9
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-Expression: Apache-2.0
@@ -0,0 +1,30 @@
1
+ dissect/cstruct/__init__.py,sha256=KOyNeUvu4c0JaAiyOfj7iftGwUIjTmD8MwHtFsLPLSU,1504
2
+ dissect/cstruct/bitbuffer.py,sha256=aNzY4lkWe8ffAc8j7FODXzwZc8gZwbey77UosyoYCsw,2711
3
+ dissect/cstruct/compiler.py,sha256=zcrO5CWCF2vWo2es-pFGn-Mvt-RTMh8W9bntK-hn85k,14343
4
+ dissect/cstruct/cstruct.py,sha256=wC4Xd8MBxTCyl-IC1I2c3etI8M6C6D5yXVfELbfQk6c,25656
5
+ dissect/cstruct/exception.py,sha256=RqD08LuBkbMqa3iQ1A05DNN8XgEmQR_SUnguwe9LGo8,316
6
+ dissect/cstruct/expression.py,sha256=MEwqHGgqA2fUk4kYk4lRLDWNTAMhB4-saJ5BgTxl7OA,7956
7
+ dissect/cstruct/lexer.py,sha256=NDUfAWgyxgRNRuVO8ExXQ51LqQXS2mv9bywVa_M7tFQ,19665
8
+ dissect/cstruct/parser.py,sha256=xCK-1O8c86sVFvjaXXSRgTsJWWd0ncKM6KJf2wbyGG8,21348
9
+ dissect/cstruct/util.py,sha256=vAf-kdOkk3zprW_gD6QmQeN3VIRgQqhPS4B_ha1jEpQ,18040
10
+ dissect/cstruct/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ dissect/cstruct/tools/stubgen.py,sha256=MeZkLmSeNSLNcAEwlAdIGv1lKvlvK3UQ4WN85ovsxTM,7672
12
+ dissect/cstruct/types/__init__.py,sha256=6P71Pu94Dv7RNjZpDZ19pqzx-G0QzmQw4q0-3zWALso,911
13
+ dissect/cstruct/types/base.py,sha256=vJUgrnsPg2ZA9Mel2P9JBBstI9z9oMxqi5Pnx9FlBIA,15023
14
+ dissect/cstruct/types/char.py,sha256=zsRMlcZGW1cHYH_xQBNORDnM_a5CXcuZLGly1N1i_Ms,3029
15
+ dissect/cstruct/types/enum.py,sha256=j2U6oE_80c4H2505YMddHPnF99az2K5IuDhX40Q7OEI,8590
16
+ dissect/cstruct/types/flag.py,sha256=7xxQjFEDPHSQOxBHgcc75WzPiAxflOquwr7Z7c3erLE,2347
17
+ dissect/cstruct/types/int.py,sha256=RCT3YjrajqRqh8cyeM-zaqzCmBfeJrXkD5W1FcRzXag,1340
18
+ dissect/cstruct/types/leb128.py,sha256=V97XTJT_Xbw1LpTwTN1OkjPHuQQAQUxd8OGuA6yEDmE,2387
19
+ dissect/cstruct/types/packed.py,sha256=QF1mAqgxLbgiibRZLhn6cp6SnuH2IxjGr9DsQdXCCSE,2713
20
+ dissect/cstruct/types/pointer.py,sha256=FNuB9bfENWRebbKX-R6Vs6xWPMHepjvOhhomeUhr7I8,3985
21
+ dissect/cstruct/types/structure.py,sha256=QZBVCCERu9eoXLqrumF7Kqy0gudywl12-cSZx6n3tPI,38402
22
+ dissect/cstruct/types/void.py,sha256=fV4hKk50uop_EKbR1nfy1cdtAzvUYrSymXsdakCyFk8,1222
23
+ dissect/cstruct/types/wchar.py,sha256=X-dRu6ujjQIIsBfzKbujwMO5skKdARtptF6P7R5v8Jo,3232
24
+ dissect_cstruct-5.0.dev9.dist-info/licenses/COPYRIGHT,sha256=H-18RXfshdH9AdHwW2eO1Xa-5s6tY5eipHh5c0whDu4,316
25
+ dissect_cstruct-5.0.dev9.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
26
+ dissect_cstruct-5.0.dev9.dist-info/METADATA,sha256=1TPbiY0D5dnQscZ0QDgEJvsSjlguJxBNXgqM3_Xf0V4,9255
27
+ dissect_cstruct-5.0.dev9.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
28
+ dissect_cstruct-5.0.dev9.dist-info/entry_points.txt,sha256=z53zqZqwD2OLrAkRwrP4wTeiU9CQe7xrMly0T2c0_wQ,71
29
+ dissect_cstruct-5.0.dev9.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
30
+ dissect_cstruct-5.0.dev9.dist-info/RECORD,,
@@ -1,32 +0,0 @@
1
- dissect/cstruct/__init__.py,sha256=8VNVQfPH4aH5V_kD-jvtLPwQ-ag9ppKHTy3dbxIR6ik,1378
2
- dissect/cstruct/bitbuffer.py,sha256=lZuLUOluHx4mzFGRp62_FO45_iJwXayyzv9-v8b0Q08,2687
3
- dissect/cstruct/compiler.py,sha256=vVg2H-pi9ugQ8Xvhs1JQ--4BjA5gtO2tW-1W8jr36V4,14283
4
- dissect/cstruct/cstruct.py,sha256=bsCesSpbE0H4MUsHwb2iEbeadOBlOSAVENwyoP-UkP8,24929
5
- dissect/cstruct/exception.py,sha256=RqD08LuBkbMqa3iQ1A05DNN8XgEmQR_SUnguwe9LGo8,316
6
- dissect/cstruct/expression.py,sha256=MEwqHGgqA2fUk4kYk4lRLDWNTAMhB4-saJ5BgTxl7OA,7956
7
- dissect/cstruct/lexer.py,sha256=NDUfAWgyxgRNRuVO8ExXQ51LqQXS2mv9bywVa_M7tFQ,19665
8
- dissect/cstruct/parser.py,sha256=xCK-1O8c86sVFvjaXXSRgTsJWWd0ncKM6KJf2wbyGG8,21348
9
- dissect/cstruct/util.py,sha256=S__kLEb9HKZg_WZR4CGtXDxkIkERLKbzFTHoU8FT8tU,15313
10
- dissect/cstruct/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- dissect/cstruct/tools/stubgen.py,sha256=FLQieYE-MeKK31DcrDzwNUropUYHPjwdfcDb1X2L6j4,7651
12
- dissect/cstruct/types/__init__.py,sha256=6P71Pu94Dv7RNjZpDZ19pqzx-G0QzmQw4q0-3zWALso,911
13
- dissect/cstruct/types/base.py,sha256=rU1S2FQllx7X7BQG7qMOwNa7waxAJyuTHTNwtkiAz0g,12763
14
- dissect/cstruct/types/char.py,sha256=s4zDmnbuOjwHgcsnEihjSqgC6H3t1jsfBTYH2ykwvHU,2997
15
- dissect/cstruct/types/enum.py,sha256=YIWyP3H0tdxmuSI82enoULiN0YevpnU0UaY5ioBCYfI,8518
16
- dissect/cstruct/types/flag.py,sha256=7xxQjFEDPHSQOxBHgcc75WzPiAxflOquwr7Z7c3erLE,2347
17
- dissect/cstruct/types/int.py,sha256=Ewejng5pBYnQHPtzL5W0wQMJVUPYyrY8ALES8mFaabI,1324
18
- dissect/cstruct/types/leb128.py,sha256=V97XTJT_Xbw1LpTwTN1OkjPHuQQAQUxd8OGuA6yEDmE,2387
19
- dissect/cstruct/types/packed.py,sha256=78nQWm-yylgfK3858nN-XZhO5jiqyO-piGNEyPD2ps4,2458
20
- dissect/cstruct/types/pointer.py,sha256=eGMrB_tTnKXKmYrcqyA_12IdukXcxRG3WOeQ1dgt7lk,3927
21
- dissect/cstruct/types/structure.py,sha256=7pBg0moZbszeRomFEp5CbAHZcd1J8IVTmEG9Mo2eKS0,37571
22
- dissect/cstruct/types/void.py,sha256=LCYnTcxsFPaxln36QEUxWuCZVioHRRckMuqtlm6tH54,1218
23
- dissect/cstruct/types/wchar.py,sha256=YCYlu91D9AMVpWOLbVMdpvDDURH-EqyppM5UMDU5UQw,3200
24
- dissect_cstruct-5.0.dev7.dist-info/licenses/COPYRIGHT,sha256=H-18RXfshdH9AdHwW2eO1Xa-5s6tY5eipHh5c0whDu4,316
25
- dissect_cstruct-5.0.dev7.dist-info/licenses/LICENSE,sha256=PhUqiw6jAh2KbBdVRPBq_hfAvfcTBin7nZ3CK7NQbTM,11341
26
- dissect_cstruct-5.0.dev7.dist-info/METADATA,sha256=IUF-_jFf7_GABYqAww23JWUTr-f73OKwmaICtDcOGc8,9255
27
- dissect_cstruct-5.0.dev7.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
28
- dissect_cstruct-5.0.dev7.dist-info/entry_points.txt,sha256=z53zqZqwD2OLrAkRwrP4wTeiU9CQe7xrMly0T2c0_wQ,71
29
- dissect_cstruct-5.0.dev7.dist-info/scm_file_list.json,sha256=nuFWhDGL6VmjcwOB94CSfjNBblnsa9imUy8X4BLyvhw,2306
30
- dissect_cstruct-5.0.dev7.dist-info/scm_version.json,sha256=wMGNV4ui6dgAUOW0hK23797wTuOlVvhVvvLFz_dXXFk,162
31
- dissect_cstruct-5.0.dev7.dist-info/top_level.txt,sha256=Mn-CQzEYsAbkxrUI0TnplHuXnGVKzxpDw_po_sXpvv4,8
32
- dissect_cstruct-5.0.dev7.dist-info/RECORD,,