[plum.array] Tutorial: Creating and Using Array Types¶
The Array
type facilitates a series of uniformly typed variables
within a bytes buffer using Python list
behaviors. The
following tutorials demonstrate the creating and using plum
array
types.
Creating an Array Type¶
Without further customization, Array
elements are UInt8
and
during byte unpacking greedily consume bytes (or produces bytes)
until exhaustion:
>>> from plum.array import Array
>>>
>>> # packs as many elements in a list as provided
>>> Array.pack([1, 2, 3])
bytearray(b'\x01\x02\x03')
>>>
>>> # unpacks (consumes) as many bytes as provided
>>> Array.unpack(b'\x01\x02\x03')
[1, 2, 3]
Subclassing the Array
type offers the ability to control the
type of the items in the array as well as the array dimensions.
Specify array dimensions as a tuple with the dims
argument.
Use the item_cls
argument to control the type used to pack
or unpack array elements. Any fixed size plum
type may be
used. For example:
>>> from plum.int.big import UInt16
>>>
>>> class Array2x2(Array, dims=(2, 2), item_cls=UInt16):
... pass
...
>>> Array2x2.pack([[1, 2], [3, 4]])
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
>>>
>>> Array2x2.unpack(b'\x00\x01\x00\x02\x00\x03\x00\x04')
[[1, 2], [3, 4]]
Unpacking Bytes¶
plum
array types convert bytes into lists of elements where each element
is unpacked per the item_cls
specified when subclassing Array
. For
multidimensional arrays, the unpacked element bytes are organized within
nested lists that follow the dimensions. Array types support the various
plum unpacking mechanisms. For example (using the two dimensional array
subclass from the previous section):
>>> from plum import unpack, Buffer
>>>
>>> # utility function
>>> unpack(Array2x2, b'\x00\x01\x00\x02\x00\x03\x00\x04')
[[1, 2], [3, 4]]
>>>
>>> # class method
>>> Array2x2.unpack(b'\x00\x01\x00\x02\x00\x03\x00\x04')
[[1, 2], [3, 4]]
>>>
>>> # bytes buffer
>>> with Buffer(b'\x00\x01\x00\x02\x00\x03\x00\x04') as buffer:
... a = buffer.unpack(Array2x2)
...
>>> a
[[1, 2], [3, 4]]
Undimensioned array types (no dims
argument specified) consume all remaining
bytes and produce a one dimensional list of unpacked elements:
>>> from plum.int.big import UInt16
>>>
>>> class GreedyArray(Array, item_cls=UInt16):
... """Consumes all bytes when unpacking."""
...
>>> GreedyArray.unpack(b'\x00\x01\x00\x02\x00\x03\x00\x04')
[1, 2, 3, 4]
Packing Bytes¶
plum
array types convert a list of elements into bytes where each element is
converted into bytes per the item_cls
specified when subclassing Array
.
The list of elements must match the dimensions specified when the array type
was created. For multidimensional arrays, the elements are expected to be
organized with nested lists that follow the dimensions. Array types support the
various plum packing mechanisms. For example (using the two dimensional array
subclass from the previous section):
>>> from plum import pack
>>>
>>> # utility function
>>> pack(Array2x2, [[1, 2], [3, 4]])
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
>>>
>>> # class method
>>> Array2x2.pack([[1, 2], [3, 4]])
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
>>>
>>> # instance method
>>> Array2x2([[1, 2], [3, 4]]).pack()
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
Undimensioned array types (no dims
argument specified) produce bytes for
all elements specified in a list:
>>> from plum.int.big import UInt16
>>>
>>> class GreedyArray(Array, item_cls=UInt16):
... """Consumes all bytes when unpacking."""
...
>>> GreedyArray.pack([1, 2, 3, 4])
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
Instantiation and Properties¶
Array
types have list behaviors. The constructor accepts an iterable of
elements. For multidimensional arrays, nested iterables must be provided that
match the array dimensions. The constructor verifies the iterables have
the proper dimensions. For example (using the two dimensional array subclass
from the previous section):
>>> from plum.int.big import UInt16
>>>
>>> class Array2x2(Array, dims=(2, 2), item_cls=UInt16):
... pass
...
>>> array = Array2x2([[1, 2], [3, 4]])
>>>
>>> # instance representation includes class name
Array2x2([[1, 2], [3, 4]])
>>>
>>> # instances have list methods (e.g. append(), clear(), count(), etc.)
>>> array.count([1, 2])
1
>>>
>>> # instances support len() function and normal indexing
>>> len(array)
2
>>> array[0][0]
1
>>> array[1] # class name in representation becomes generic for nested list
[3, 4]
>>>
>>> # instances have plum methods/properties (e.g. pack(), nbytes, etc.)
>>> array.nbytes
8
>>> array.pack()
bytearray(b'\x00\x01\x00\x02\x00\x03\x00\x04')
Tip
When constructing lists of elements for packing, consider instantiating an array type when the packing operation is not immediately performed. This way, element lists with improper dimensions get reported immediately rather than delayed until the packing operation.
The “Access” column produced by the dump
property provides index
information needed to access individual elemements:
>>> # access column in dump gives indication of
>>> array.dump()
+--------+--------+-------+-------+----------+
| Offset | Access | Value | Bytes | Type |
+--------+--------+-------+-------+----------+
| | | | | Array2x2 |
| | [0] | | | |
| 0 | [0] | 1 | 00 01 | UInt16 |
| 2 | [1] | 2 | 00 02 | UInt16 |
| | [1] | | | |
| 4 | [0] | 3 | 00 03 | UInt16 |
| 6 | [1] | 4 | 00 04 | UInt16 |
+--------+--------+-------+-------+----------+
Sized Arrays in Structures¶
See the Sized Array Structure Tutorial for information on creating structures with an array member and a second member for controlling its size (dimensions).