From ff48a801bf7135ec21aeae56fb63ff9fe66b1f8b Mon Sep 17 00:00:00 2001 From: Peter Birch Date: Wed, 1 Oct 2025 11:06:20 +0100 Subject: [PATCH 1/2] Fixing 'utils.unpack(...)' when used against an ArraySpec --- packtype/types/array.py | 12 ++++---- packtype/utils/basic.py | 3 ++ tests/grammar/test_struct.py | 57 +++++++++++++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 7 deletions(-) diff --git a/packtype/types/array.py b/packtype/types/array.py index 834ff2d..7201639 100644 --- a/packtype/types/array.py +++ b/packtype/types/array.py @@ -3,6 +3,7 @@ # import functools +import inspect import math from collections.abc import Callable, Iterable from typing import Any, Self @@ -69,6 +70,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 +177,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 From 89357ec675297a57f30c849ded6843bccb0c9f91 Mon Sep 17 00:00:00 2001 From: Peter Birch Date: Wed, 1 Oct 2025 11:07:52 +0100 Subject: [PATCH 2/2] Lint fix --- packtype/types/array.py | 1 - 1 file changed, 1 deletion(-) diff --git a/packtype/types/array.py b/packtype/types/array.py index 7201639..6b476d2 100644 --- a/packtype/types/array.py +++ b/packtype/types/array.py @@ -3,7 +3,6 @@ # import functools -import inspect import math from collections.abc import Callable, Iterable from typing import Any, Self