| BitPacket (version 0.1.0, Sun Apr 01, 2007 12:53) | home |
An object-oriented representation of bit field structures.
INTRODUCTION
These classes represent simple bit fields, and fixed and variable
structures of bit fields which might be used to construct
packets. BitStructure and BitVariableStructure are BitField
themselves and all of them can be used together. That is, we can
add any BitField subclass into a BitStructure or
BitVariableStructure.
Note that some of the code found in this documentation might not
be self-contained, it may depend on code explained in previous
sections.
SINGLE BIT FIELDS
A packet might be formed by mutiple fields that could be single
bit fields, integer fields, structure fields or variable structure
fields.
An example of a packet could be:
+-------+-----------+---------+------------------+
| id | address | nbytes | data |
+-------+-----------+---------+------------------+
<- 1 -> <--- 4 ---> <-- 2 --> <---- nbytes ---->
That is, a packet with four fields:
- Indetifier: 1 byte
- Memory address: 4 bytes
- Number of data bytes: 2 bytes
- Data: number of data bytes
The first field could be constructed by the following piece of
code:
>>> bf = BitField('id', BYTE_SIZE, 0x54)
>>> bf.value() == 0x54
True
that would create a BitField instance of a field named 'id' of 1
byte size and value 0x54.
UNPACKING SINGLE BIT FIELDS
In order to unpack a single field from a data buffer, one would
create a BitField without any initialisation and assign the data
buffer when ready:
>>> data = array.array('B', [0x35])
>>> bf = BitField('id', BYTE_SIZE)
>>> bf.set_array(data)
>>> bf.array()
array('B', [53])
>>> print bf
(id = 0x35)
FIXED STRUCTURES
A packet is built by many fields which could form an
structure. This structure can be represented using the
BitStructure class.
An example of a simple packet could be:
+-------+-----------+
| id | address |
+-------+-----------+
<- 1 -> <--- 4 --->
That is, a packet (structure) with two fields:
- Indetifier: 1 byte
- Memory address: 4 bytes
This packet could be constructed by:
>>> bs = BitStructure('mystructure')
The line above creates an empty packet named 'mystructure'. So,
now we need to add fields to it. This can be done by calling the
append() method:
>>> bs.append(BitField('id', BYTE_SIZE, 0x54))
>>> bs.append(BitField('address', INTEGER_SIZE, 0x10203040))
>>> print bs
(mystructure =
(id = 0x54)
(address = 0x10203040))
As you can see, this has added two fields of different sizes into
our packet.
ACCESSING FIXED STRUCTRES MEMBERS
Structure fields can be obtained as in a dictionary, that is, by
its name. Following the last example:
>>> print '0x%X' % bs['id']
0x54
>>> print '0x%X' % bs['address']
0x10203040
UNPACKING STRUCTURES
To be able to unpack an integer value or an array of bytes into a
BitStructure, we only need to create the desired packet without
initializing any field and assign the integer value or array of
bytes to it.
>>> bs = BitStructure('mypacket')
>>> bs.append(BitField('id', BYTE_SIZE))
>>> bs.append(BitField('address', INTEGER_SIZE))
>>> print bs
(mypacket =
(id = 0x0)
(address = 0x0))
So, now we can unpack the following array of bytes:
>>> data = array.array('B', [0x38, 0x87, 0x34, 0x21, 0x40])
into our previously defined structure:
>>> bs.set_array(data)
>>> print bs
(mypacket =
(id = 0x38)
(address = 0x87342140))
STRUCTURES AS CLASSES
An interesting, and obvious, use, is to subclass BitStructure to
create your own reusable structures. Then, we could create the
structure defined in the previous section as a new class:
>>> class MyStructure(BitStructure):
... def __init__(self, id = 0, address = 0):
... BitStructure.__init__(self, 'mystructure')
... append(BitField('id', BYTE_SIZE, id))
... append(BitField('address', INTEGER_SIZE, address))
...
... def id(self):
... return self['id']
...
... def address(self):
... return self['address']
...
>>> ms = MyStructure(0x33, 0x50607080)
>>> print ms
(mystructure =
(id = 0x33)
(address = 0x50607080))
We can now use the accessors of our class to print its content:
>>> print '0x%X' % ms.id()
0x33
>>> print '0x%X' % ms.address()
0x50607080
VARIABLE STRUCTURES
Sometimes we need to create packets that have a number of repeated
structures in it. Normally, these kind of packets have a field
indicating the number of repeated structures and the structures
after it.
+-------+-------+-----------+-------+-----------+
| count | id | address | id | address |
+-------+-------+-----------+-------+-----------+
<- 1 -> <- 1 -> <--- 4 ---> <- 1 -> <--- 4 --->
We can achieve this by using the BitVariableStructure class which
is a subclass of BitStructure. This class already contains a
counter field of a given size and at the beginning the structure
does not contain any more fields, thus the counter is set to
zero. The fields which will form the variable structure need to be
added as in BitStructure and the counter will be automatically
increased.
>>> bs = BitStructure('mystructure')
>>> bs.append(BitField('id', BYTE_SIZE, 0x54))
>>> bs.append(BitField('address', INTEGER_SIZE, 0x10203040))
>>> packet = BitVariableStructure('mypacket', BYTE_SIZE)
>>> packet.append(bs)
>>> print packet
(mypacket =
(counter = 0x1)
(mystructure0 =
(id = 0x54)
(address = 0x10203040)))
We can see that the packet has a counter field of value 1 and our
created structure 'mystructure'. Note that the structure has a new
name 'mystructure0'. This is because the BitStructure class does
not allow to have fields with the same name, thus when the
'mystructure' field has been added the name has automatically
changed.
Finally, we can also build more complex packets, such as the one
below, where we have two variable structures one inside of the
other.
+-------+-------+-------+-----------+------
| cnt1 | id | cnt2 | address | ...
+-------+-------+-------+-----------+------
<- 1 -> <- 1 -> <- 1 -> <--- 4 --->
<----- cnt2 ----->
<-------------- cnt1 ------------>
This can easly be done with the following piece of code:
>>> adds = BitVariableStructure('addresses', BYTE_SIZE)
>>> adds.append(BitField('address', INTEGER_SIZE, 0x10203040))
>>> adds.append(BitField('address', INTEGER_SIZE, 0x40506080))
>>> ids = BitStructure('ids')
>>> ids.append(BitField('id', BYTE_SIZE, 0x34))
>>> ids.append(adds)
>>> vs = BitVariableStructure('packet', BYTE_SIZE)
>>> vs.append(ids)
>>> print vs
(packet =
(counter = 0x1)
(ids0 =
(id = 0x34)
(addresses =
(counter = 0x2)
(address0 = 0x10203040)
(address1 = 0x40506080))))
We have created a variable structure with two addresses 'adds' and
we have added it to the fixed structure 'ids'. Our packet has been
created as a BitVariableStructure named 'packet' and the 'ids'
fixed structure has been added to it.
ACCESSING VARIABLE STRUCTRES MEMBERS
In order to access members of variable structures we could follow
two methods: access by name or access by index.
Both methods require to know how many members the variable
structure contains. This is given by the counter() method. So, to
access the members by name you need to remember that names are
dynamically changed when added to a variable structure, thus
appending and 'id' field will become 'id0' (if no member has been
previously added), and appending 'id' again will become
'id1'. Knowing this, we could easily iterate through variable
structure members from the structure created in the previous
section:
>>> for i in range(vs.counter()):
... ids = vs.field('ids%d' %i)
... adds = ids.field('addresses')
... for j in range(adds.counter()):
... print adds.field('address%d' % j)
...
(address0 = 0x10203040)
(address1 = 0x40506080)
UNPACKING VARIABLE STRUCTURES
In order to unpack a variable structure, the BitVariableStructure
class needs to know the type of the multiple structures (all of
the same type) that might contain. This is done by assigning an
instance of the desired type into the 'base_field' parameter of
the BitVariableStructure constructor. So, taking the last example
defined in the 'VARIABLE STRUCTURES' section, we could do the
following:
>>> addr = BitField('address', INTEGER_SIZE)
>>> adds = BitVariableStructure('addresses', BYTE_SIZE,
... base_field = addr)
>>> ids = BitStructure('ids')
>>> ids.append(BitField('id', BYTE_SIZE))
>>> ids.append(adds)
>>> vs = BitVariableStructure('packet', BYTE_SIZE,
... base_field = ids)
>>> print vs
(packet =
(counter = 0x0))
The BitVariableStructure 'packet' is empty, so, now we can unpack
the following array of bytes:
>>> data = array.array('B', [0x01, 0x34, 0x02, 0x10, 0x20, 0x30, 0x40,
... 0x40, 0x50, 0x60, 0x80])
into our previously defined variable structure:
>>> vs.set_array(data)
>>> print vs
(packet =
(counter = 0x1)
(ids0 =
(id = 0x34)
(addresses =
(counter = 0x2)
(address0 = 0x10203040)
(address1 = 0x40506080))))
As we can see, the BitVariableStructure class dynamically creates
copies of the 'base_field' parameter in order to reconstruct the
whole structure.
| Modules | ||||||
| ||||||
| Classes | ||||||||||||||||||||||||||
|
| ||||||||||||||||||||||||||
| Data | ||
| BIT_SIZE = 1 BYTE_SIZE = 8 CHAR_SIZE = 8 INTEGER_SIZE = 32 SHORT_SIZE = 16 SIGNED_CHAR_SIZE = 8 SIGNED_INTEGER_SIZE = 32 SIGNED_SHORT_SIZE = 16 UNSIGNED_CHAR_SIZE = 8 UNSIGNED_INTEGER_SIZE = 32 UNSIGNED_SHORT_SIZE = 16 WORD_SIZE = 16 __author__ = 'Aleix Conchillo Flaque <aleix@member.fsf.org>' __copyright__ = 'Copyright (C) 2007 Aleix Conchillo Flaque' __date__ = 'Sun Apr 01, 2007 12:53' __license__ = 'GPL' __url__ = 'http://hacks-galore.org/aleix/BitPacket' __version__ = '0.1.0' | ||
| Author | ||
| Aleix Conchillo Flaque <aleix@member.fsf.org> | ||