[plum] Structure Tutorial: Embedded Bit Field Members

This tutorial shows how to use the Member and BitFieldMember member definition classes within a structure definition and the differences between them.

Nested

The following shows use of the standard Member member definition to include the nibble1 and nibble2 bitfields within the structure. In the example, the Sample structure “nests” the bit fields within the nibbles member.

>>> from plum.int.bitfields import BitField, BitFields
>>> from plum.int.little import UInt8
>>> from plum.structure import Structure, Member
>>>
>>> class TwoNibbles(BitFields, nbytes=1):
...     nibble1: int = BitField(pos=0, size=4)
...     nibble2: int = BitField(pos=4, size=4)
...
>>> class Sample(Structure):
...    nibbles: int = Member(cls=TwoNibbles)
...    byte: int = Member(cls=UInt8)
...

The structure instantiation accepts a value for nibbles rather than the individual bit fields. Access to the bit fields goes through the nibbles member as well:

>>> sample = Sample(nibbles=TwoNibbles(0x21), byte=0xff)
>>> sample.nibbles.nibble1
1
>>> sample.nibbles.nibble2
2

Flattened

The following shows use of the BitFieldMember member definition to include the nibble1 and nibble2 bit fields directly within the structure.

>>> from plum.int.bitfields import BitField, BitFields
>>> from plum.int.little import UInt8
>>> from plum.structure import Structure, BitFieldMember
>>>
>>> class TwoNibbles(BitFields, nbytes=1):
...     nibble1: int = BitField(pos=0, size=4)
...     nibble2: int = BitField(pos=4, size=4)
...
>>> class Sample(Structure):
...    nibble1: int = BitFieldMember(cls=TwoNibbles)
...    nibble2: int = BitFieldMember(cls=TwoNibbles, default=2)
...    byte: int = Member(cls=UInt8)
...

Direct inclusion of the bit fields within the structure causes the instantiation to accept values for each of the bit fields instead of one value for them all. It also allows direct access from the structure:

>>> sample = Sample(nibble1=1, nibble2=2, byte=0xff)
>>> sample.nibble1
1
>>> sample.nibble2
2

As shown for nibble2, the BitFieldMember accepts a default value for a bit field that may differ from those specified in the TwoNibbles bit fields collection. This allows the bit field value to be skipped when instantiating or packing:

>>> sample = Sample(nibble1=1, byte=0xff)
>>> sample.pack().hex()
'21ff'
>>>
>>> Sample.pack({'nibble1': 1, 'byte': 0xff}).hex()
'21ff'

Defining the bit fields directly within the structure only changes the instantiation and access as described. The core members of the structure itself remain the same (the bitfields are contained within the same [0] member):

>>> sample = Sample(nibble1=1, nibble2=2, byte=0xff)
>>> sample[0]
TwoNibbles(nibble1=1, nibble2=2)