diff --git a/dissect/cstruct/compiler.py b/dissect/cstruct/compiler.py index 1b608a4..6c98c4b 100644 --- a/dissect/cstruct/compiler.py +++ b/dissect/cstruct/compiler.py @@ -21,6 +21,7 @@ Structure, Union, Void, + VoidArray, Wchar, WcharArray, ) @@ -48,6 +49,7 @@ Void, Wchar, WcharArray, + VoidArray, ) log = logging.getLogger(__name__) @@ -365,7 +367,7 @@ def _generate_struct_info(cs: cstruct, fields: list[Field], align: bool = False) read_type = _get_read_type(cs, field.type) # Drop voids - if issubclass(read_type, Void): + if issubclass(read_type, (Void, VoidArray)): continue # Array of more complex types are handled elsewhere diff --git a/dissect/cstruct/types/__init__.py b/dissect/cstruct/types/__init__.py index 0ec9511..4d86d34 100644 --- a/dissect/cstruct/types/__init__.py +++ b/dissect/cstruct/types/__init__.py @@ -7,7 +7,7 @@ from dissect.cstruct.types.packed import Packed from dissect.cstruct.types.pointer import Pointer from dissect.cstruct.types.structure import Field, Structure, Union -from dissect.cstruct.types.void import Void +from dissect.cstruct.types.void import Void, VoidArray from dissect.cstruct.types.wchar import Wchar, WcharArray __all__ = [ @@ -27,6 +27,7 @@ "Structure", "Union", "Void", + "VoidArray", "Wchar", "WcharArray", ] diff --git a/dissect/cstruct/types/base.py b/dissect/cstruct/types/base.py index 0c9e527..5c49302 100644 --- a/dissect/cstruct/types/base.py +++ b/dissect/cstruct/types/base.py @@ -54,6 +54,10 @@ def __getitem__(cls, num_entries: int | Expression | None) -> type[BaseArray]: """Create a new array with the given number of entries.""" return cls.cs._make_array(cls, num_entries) + def __bool__(cls) -> bool: + """Type class is always truthy.""" + return True + def __len__(cls) -> int: """Return the byte size of the type.""" # Python 3.9 compat thing for bound type vars diff --git a/dissect/cstruct/types/void.py b/dissect/cstruct/types/void.py index f3191d0..fc07888 100644 --- a/dissect/cstruct/types/void.py +++ b/dissect/cstruct/types/void.py @@ -1,13 +1,34 @@ from __future__ import annotations -from typing import Any, BinaryIO +from typing import TYPE_CHECKING, Any, BinaryIO -from dissect.cstruct.types.base import BaseType +from dissect.cstruct.types.base import BaseArray, BaseType + +if TYPE_CHECKING: + from typing_extensions import Self + + +class VoidArray(list, BaseArray): + """Array type representing void elements, primarily used for no-op reading and writing operations.""" + + @classmethod + def __default__(cls) -> Self: + return cls() + + @classmethod + def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self: + return cls() + + @classmethod + def _write(cls, stream: BinaryIO, data: bytes) -> int: + return 0 class Void(BaseType): """Void type.""" + ArrayType = VoidArray + def __bool__(self) -> bool: return False @@ -15,13 +36,9 @@ def __eq__(self, value: object) -> bool: return isinstance(value, Void) @classmethod - def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Void: + def _read(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Self: return cls.__new__(cls) - @classmethod - def _read_0(cls, stream: BinaryIO, context: dict[str, Any] | None = None) -> Void: - return [cls.__new__(cls)] - @classmethod def _write(cls, stream: BinaryIO, data: Void) -> int: return 0 diff --git a/tests/test_types_base.py b/tests/test_types_base.py index cfa1144..6e73ceb 100644 --- a/tests/test_types_base.py +++ b/tests/test_types_base.py @@ -137,3 +137,12 @@ def _read(cls, stream: BinaryIO, context: dict | None = None) -> CustomType: assert isinstance(result.b, CustomType) assert result.a.value == b"ASDF" assert result.b.value == b"asdf" + + +def test_truthy_type(cs: cstruct) -> None: + static_type = cs.uint32 + dynamic_type = cs.uint32[None] + + assert static_type + # Should not raise a TypeError: Dynamic size + assert dynamic_type diff --git a/tests/test_types_void.py b/tests/test_types_void.py index b939b5a..c234641 100644 --- a/tests/test_types_void.py +++ b/tests/test_types_void.py @@ -10,7 +10,9 @@ def test_void_read(cs: cstruct) -> None: - assert not cs.void + # The type itself is truthy, but an instance is not + assert cs.void + assert not cs.void() stream = io.BytesIO(b"AAAA") assert not cs.void(stream) @@ -23,11 +25,11 @@ def test_void_write(cs: cstruct) -> None: def test_void_array_read(cs: cstruct) -> None: - assert not cs.void[4] + assert not cs.void[4]() stream = io.BytesIO(b"AAAA") - assert not any(cs.void[4](stream)) - assert not any(cs.void[None](stream)) + assert not cs.void[4](stream) + assert not cs.void[None](stream) assert stream.tell() == 0 @@ -41,7 +43,7 @@ def test_void_default(cs: cstruct) -> None: assert not cs.void() assert not cs.void.__default__() - assert cs.void[1].__default__() == [cs.void()] + assert cs.void[1].__default__() == [] assert cs.void[None].__default__() == [] @@ -61,8 +63,8 @@ def test_void_struct(cs: cstruct, compiled: bool) -> None: obj = cs.test(stream) assert not obj.a - assert not any(obj.b) - assert not any(obj.c) + assert not obj.b + assert not obj.c assert stream.tell() == 0