[plum.bytearray] Tutorial: Using ByteArray Type

The ByteArray type inherits bytearray characteristics as a result of it being a subclass of it. The plum type collection includes this type for applications that require an array of bytes. This tutorial shows sample usages and the ByteArray type’s properties.

Out of Box

By default, the ByteArray type consumes all bytes given to it when unpacking, packing or instantiating:

>>> from plum import pack, unpack
>>> from plum.bytearray import ByteArray
>>>
>>> unpack(ByteArray, b'\x00\x01')
bytearray(b'\x00\x01')
>>>
>>> pack(ByteArray, [0, 1])
bytearray(b'\x00\x01')
>>>
>>> ByteArray([0, 1])
ByteArray(b'\x00\x01')

Fixed Size Subclass

To lock the size of the byte array for use within a composite type such as Structure, subclass ByteArray and specify the number of bytes using the nbytes keyword argument:

>>> from plum import pack, unpack
>>> from plum.bytearray import ByteArray
>>> from plum.int.little import UInt8
>>> from plum.structure import Member, Structure
>>>
>>> class ByteArray1(ByteArray, nbytes=4):
...     """Interpret bytes as a byte array of length 4."""
...
>>> class Struct1(Structure):
...     barray: bytearray = Member(cls=ByteArray1)
...     bookend: int = Member(cls=UInt8)
...
>>> struct1 = unpack(Struct1, b'\x00\x01\x02\x03\x99')
>>> struct1.dump()
+--------+----------------+--------------------------------+-------------+------------+
| Offset | Access         | Value                          | Bytes       | Type       |
+--------+----------------+--------------------------------+-------------+------------+
|        |                |                                |             | Struct1    |
|        | [0] (.barray)  |                                |             | ByteArray1 |
| 0      |   [0:4]        | bytearray(b'\x00\x01\x02\x03') | 00 01 02 03 |            |
| 4      | [1] (.bookend) | 153                            | 99          | UInt8      |
+--------+----------------+--------------------------------+-------------+------------+
>>>
>>> pack(Struct1, {'barray': [0, 1, 2, 3], 'bookend': 0x99})
bytearray(b'\x00\x01\x02\x03\x99')

Use the fill keyword argument to provide an integer value to extend initial values with when the initial value contains fewer than nbytes bytes:

>>> class ByteArray2(ByteArray, nbytes=4, fill=0):
...     """Interpret bytes as a byte array of length 4."""
...
>>> class Struct2(Structure):
...     barray: bytearray = Member(cls=ByteArray2)
...     bookend: int = Member(cls=UInt8)
...
>>> struct2 = Struct2(barray=ByteArray2([1,2]), bookend=0x99)
>>> struct2.dump()
+--------+----------------+--------------------------------+-------------+------------+
| Offset | Access         | Value                          | Bytes       | Type       |
+--------+----------------+--------------------------------+-------------+------------+
|        |                |                                |             | Struct2    |
|        | [0] (.barray)  |                                |             | ByteArray2 |
| 0      |   [0:4]        | bytearray(b'\x01\x02\x00\x00') | 01 02 00 00 |            |
| 4      | [1] (.bookend) | 153                            | 99          | UInt8      |
+--------+----------------+--------------------------------+-------------+------------+

Sized Structure Member

To couple a size member to a byte array member, use the SizeMember and VariableSizeMember classes. The following example demonstrates. See the Sized Object Tutorial for details.

>>> from plum import pack, unpack
>>> from plum.bytearray import ByteArray
>>> from plum.int.little import UInt8
>>> from plum.structure import Member, SizeMember, Structure, VariableSizeMember
>>>
>>> class Struct3(Structure):
...     size: int = SizeMember(cls=UInt8)
...     barray: bytearray = VariableSizeMember(size_member=size, cls=ByteArray)
...     bookend: int = Member(cls=UInt8)
...
>>> # size member automatically filled in
>>> struct3 = Struct3(barray=[0] * 8, bookend=0x99)
>>> struct3.dump()
+--------+----------------+------------------------------------------------+-------------------------+-----------+
| Offset | Access         | Value                                          | Bytes                   | Type      |
+--------+----------------+------------------------------------------------+-------------------------+-----------+
|        |                |                                                |                         | Struct3   |
|  0     | [0] (.size)    | 8                                              | 08                      | UInt8     |
|        | [1] (.barray)  |                                                |                         | ByteArray |
|  1     |   [0:8]        | bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00') | 00 00 00 00 00 00 00 00 |           |
|  9     | [2] (.bookend) | 153                                            | 99                      | UInt8     |
+--------+----------------+------------------------------------------------+-------------------------+-----------+
>>>
>>> unpack(Struct3, b'\x01\x02\x99').dump()
+--------+----------------+--------------------+-------+-----------+
| Offset | Access         | Value              | Bytes | Type      |
+--------+----------------+--------------------+-------+-----------+
|        |                |                    |       | Struct3   |
| 0      | [0] (.size)    | 1                  | 01    | UInt8     |
|        | [1] (.barray)  |                    |       | ByteArray |
| 1      |   [0:1]        | bytearray(b'\x02') | 02    |           |
| 2      | [2] (.bookend) | 153                | 99    | UInt8     |
+--------+----------------+--------------------+-------+-----------+

Properties

The ByteArray constructor accepts the same argument variations as bytearray:

>>> from plum.bytearray import ByteArray
>>>
>>> # iterable of ints
>>> ByteArray([0, 1, 2])
ByteArray(b'\x00\x01\x02')
>>>
>>> # string
>>> ByteArray('Hello World!', encoding='ascii', errors='strict')
ByteArray(b'Hello World!')
>>>
>>> # bytes or buffer
>>> ByteArray(b'\x00\x01\x02')
ByteArray(b'\x00\x01\x02')
>>>
>>> # integer size
>>> ByteArray(3)
ByteArray(b'\x00\x00\x00')
>>>
>>> # empty
>>> ByteArray()
ByteArray(b'')

ByteArray types follow all other bytearray method behaviors.