From 5e7fd1f7d627e92fda12df4fc7e274071d16b5f2 Mon Sep 17 00:00:00 2001 From: Schamper <1254028+Schamper@users.noreply.github.com> Date: Tue, 29 Apr 2025 12:25:17 +0200 Subject: [PATCH 1/2] Fix dynamic size errors for MetaType --- dissect/cstruct/compiler.py | 4 +++- dissect/cstruct/types/__init__.py | 3 ++- dissect/cstruct/types/base.py | 4 ++++ dissect/cstruct/types/void.py | 31 ++++++++++++++++++++++++------- tests/test_types_base.py | 9 +++++++++ tests/test_types_void.py | 16 +++++++++------- 6 files changed, 51 insertions(+), 16 deletions(-) diff --git a/dissect/cstruct/compiler.py b/dissect/cstruct/compiler.py index 1b608a43..6c98c4b2 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 0ec95117..4d86d347 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 0c9e5278..5c493028 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 f3191d07..11473aca 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): + """Character array type for reading and writing byte strings.""" + + @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 cfa1144b..6e73ceb4 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 b939b5a9..c2346410 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 From 35c5e0c42a588a0e56c703eebb80a2413bec7c45 Mon Sep 17 00:00:00 2001 From: Erik Schamper <1254028+Schamper@users.noreply.github.com> Date: Wed, 7 May 2025 10:23:02 +0200 Subject: [PATCH 2/2] Update dissect/cstruct/types/void.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- dissect/cstruct/types/void.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dissect/cstruct/types/void.py b/dissect/cstruct/types/void.py index 11473aca..fc07888c 100644 --- a/dissect/cstruct/types/void.py +++ b/dissect/cstruct/types/void.py @@ -9,7 +9,7 @@ class VoidArray(list, BaseArray): - """Character array type for reading and writing byte strings.""" + """Array type representing void elements, primarily used for no-op reading and writing operations.""" @classmethod def __default__(cls) -> Self: