Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions packtype/types/array.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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()

Expand Down
3 changes: 3 additions & 0 deletions packtype/utils/basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
57 changes: 56 additions & 1 deletion tests/grammar/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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