diff --git a/packtype/types/array.py b/packtype/types/array.py index 834ff2d..6b476d2 100644 --- a/packtype/types/array.py +++ b/packtype/types/array.py @@ -69,6 +69,11 @@ def as_packed(self, **kwds) -> "PackedArray": def as_unpacked(self, **kwds) -> "PackedArray": return UnpackedArray(self, **kwds) + def _pt_unpack(self, packed: int) -> "PackedArray": + inst = PackedArray(self) + inst._pt_set(packed) + return inst + def __call__(self, **kwds) -> "PackedArray": return self.as_packed(**kwds) @@ -171,12 +176,6 @@ def _pt_width(self) -> int: def _pt_pack(self) -> int: return int(self._pt_bv) - @classmethod - def _pt_unpack(cls, packed: int) -> "PackedArray": - inst = cls() - inst._pt_set(packed) - return inst - def __int__(self) -> int: return self._pt_pack() diff --git a/packtype/utils/basic.py b/packtype/utils/basic.py index 1437ec8..22d0c71 100644 --- a/packtype/utils/basic.py +++ b/packtype/utils/basic.py @@ -6,6 +6,7 @@ import math from ..types.alias import Alias +from ..types.array import ArraySpec from ..types.assembly import PackedAssembly from ..types.base import Base from ..types.enum import Enum @@ -120,6 +121,8 @@ def unpack(ptype: type[Base], value: int) -> Base: :param value: The value to unpack :return: An instance of the Packtype definition with the unpacked value """ + if isinstance(ptype, ArraySpec): + return ptype._pt_unpack(value) if not inspect.isclass(ptype): raise TypeError(f"{ptype} is an instance of a Packtype definition") if not issubclass(ptype, Base): diff --git a/tests/grammar/test_struct.py b/tests/grammar/test_struct.py index 1dc7748..0fbb18b 100644 --- a/tests/grammar/test_struct.py +++ b/tests/grammar/test_struct.py @@ -2,12 +2,14 @@ # SPDX-License-Identifier: Apache-2.0 # +import random + import pytest from packtype.grammar import ParseError, UnknownEntityError, parse_string from packtype.types.assembly import Packing, WidthError from packtype.types.struct import Struct -from packtype.utils import get_width +from packtype.utils import get_width, unpack from ..fixtures import reset_registry @@ -227,3 +229,56 @@ def test_parse_struct_bad_field_ref(): """ ) ) + + +def test_parse_struct_nested(): + """Parse a multiply-nested arrayed structure""" + # Declare a nested and arrayed structure + pkg = next( + parse_string( + """ + package the_package { + // Implicit width, implicitly packed from LSB + struct lowest { + a : scalar[2] + b : scalar[2] + } + struct middle { + c : lowest[2] + d : scalar[2] + } + struct top { + e : middle[2] + f : scalar[2] + } + an_array : top[2] + } + """ + ) + ) + # Unpack a value + the_value = random.getrandbits(44) + inst = unpack(pkg.an_array, the_value) + # Check values + assert inst[0].e[0].c[0].a == (the_value >> 0) & 0b11 + assert inst[0].e[0].c[0].b == (the_value >> 2) & 0b11 + assert inst[0].e[0].c[1].a == (the_value >> 4) & 0b11 + assert inst[0].e[0].c[1].b == (the_value >> 6) & 0b11 + assert inst[0].e[0].d == (the_value >> 8) & 0b11 + assert inst[0].e[1].c[0].a == (the_value >> 10) & 0b11 + assert inst[0].e[1].c[0].b == (the_value >> 12) & 0b11 + assert inst[0].e[1].c[1].a == (the_value >> 14) & 0b11 + assert inst[0].e[1].c[1].b == (the_value >> 16) & 0b11 + assert inst[0].e[1].d == (the_value >> 18) & 0b11 + assert inst[0].f == (the_value >> 20) & 0b11 + assert inst[1].e[0].c[0].a == (the_value >> 22) & 0b11 + assert inst[1].e[0].c[0].b == (the_value >> 24) & 0b11 + assert inst[1].e[0].c[1].a == (the_value >> 26) & 0b11 + assert inst[1].e[0].c[1].b == (the_value >> 28) & 0b11 + assert inst[1].e[0].d == (the_value >> 30) & 0b11 + assert inst[1].e[1].c[0].a == (the_value >> 32) & 0b11 + assert inst[1].e[1].c[0].b == (the_value >> 34) & 0b11 + assert inst[1].e[1].c[1].a == (the_value >> 36) & 0b11 + assert inst[1].e[1].c[1].b == (the_value >> 38) & 0b11 + assert inst[1].e[1].d == (the_value >> 40) & 0b11 + assert inst[1].f == (the_value >> 42) & 0b11