Skip to content
This repository has been archived by the owner on Mar 8, 2024. It is now read-only.

Using pycrate core objects

mitshell edited this page Sep 27, 2017 · 9 revisions

How to use pycrate core objects:

  • the Charpy object: a bit string handler and consumer
  • the pack_val function: a bit string packer
  • Atom objects: Element representing basic values
  • the Envelope object: sequence of Elements
  • the Array object: repeated list of an immutable Element
  • the Sequence object: repeated list of a mutable Element

Charpy

The Charpy class enables to deal with bit strings in Python ; more specifically, it exposes a simple API to consume a given bytes' buffer into signed or unsigned, big or little endian, integral values, or shorter bytes' buffers, providing a length in bits.

The class is defined in the pycrate_core/charpy.py file.

>>> from pycrate_core.charpy import Charpy
>>> help(Charpy) # a doc string is provided
[...]
>>> c = Charpy(b'this is a buffer')
>>> c
charpy(b'this is a buffer')

The representation of a Charpy instance is customizable thanks to the 2 class attributes: _REPR (type of representation) and _REPR_MAX (maximum length for the representation). All possible representations are listed in the _REPR_POS class attribute. A Charpy instance also provides direct methods to get the corresponding bit-string and hex-string:

>>> c.bin()
'01110100011010000110100101110011001000000110100101110011001000000110000100100000011000100111010101100110011001100110010101110010'
>>> c.hex()
'74686973206973206120627566666572'

It saves its initial bytes' buffer into the _buf attribute and its corresponding length in bits in the _len_bit attribute. Then, it uses a cursor to go forward (and backward) in the buffer to extract values at the given offset. The cursor value is stored in the _cur attribute.

>>> c._cur # current position of the cursor in the Charpy instance
0
>>> c.len_bit() # current length in bits of the remaining buffer
128

It provides methods to extract values from the buffer given a length in bits as argument, moving the cursor forward (all methods starting with get_) or not (all methods starting with to_).

>>> c.get_int(12) # returns the value of a 12 bits integer
1862
>>> c
charpy(b"\x86\x972\x06\x972\x06\x12\x06'VffW ")
>>> c.get_int_le(12) # returns the value of a little endian 12 bits integer
-122
>>> c.get_uint(27) # returns the value of a 27 bits unsigned integer
79269940
>>> c.len_bit() # current length in bits of the remaining buffer (128-12-12-27)
81
>>> c._cur # 12 + 12 + 27
47
>>> c.get_bytes(38) # returns the buffer corresponding to the next 38 bits (zeroing last bits of the last byte)
'\xb9\x900\x900'
>>> c.get_bitlist(6) # returns the list of next 0 or 1
[0, 1, 0, 0, 1, 1]
>>> c.get_bytelist(22) # returns the list of next 22 bits unsigned byte value (zeroing last bits of the last byte)
[171, 51, 48]
>>> c.len_bit()
15
>>> c._cur
113

All the values that can be extracted are:

  • get_bytes() / to_bytes(): to get a bytes' buffer
  • to_int() / get_int(): to get a big endian signed integral value
  • to_uint() / get_uint(): to get a big endian unsigned integral value
  • to_int_le() / get_int_le(): to get a little endian signed integral value
  • to_uint_le() / get_uint_le(): to get a little endian unsigned integral value
  • to_bitlist() / get_bitlist(): to get a list of 0 and 1 integral values
  • to_bytelist() / get_bytelist(): to get a list of uint8 values

When the buffer and cursor do not provide long enough data for the extraction of a given length in bits, a CharpyErr exception is raised.

>>> c.get_uint(32) # this raises as there is only 15 remaining bits available from the original buffer

Traceback (most recent call last):
  File "<pyshell#20>", line 1, in <module>
    c.get_uint(32)
  File "C:\Python27\lib\site-packages\pycrate-0.1.0-py2.7.egg\pycrate_core\charpy.py", line 797, in get_uint
    .format(bitlen, self._len_bit-self._cur)))
CharpyErr: bitlen overflow: 32, max 15
>>> c.rewind(17) # if no args are passed, i.e. c.rewind(), this sets the cursor to 0, reinitializing the original buffer
>>> c._cur
96
>>> c.len_bit()
32
>>> c.get_uint(32)
1717986674

Some more methods are defined in the Charpy object, e.g. to concatenate some more data at the end of the buffer stored in the _buf attribute, and to overwrite some Python built-ins. Give a look at the source code for this.

pack_val

The pack_val() function allows to pack a list of composite values: signed or unsigned, big or little endian, integral values, or bytes buffers, specifying their length in bits. The function then returns the resulting bytes buffer after concatenating all those composite values. It is the revert part of the Charpy class.

The function is defined in pycrate_core/utils_py2.py and pycrate_core/utils_py3.py, and is made available in an uniform way by importing the utils.py module.

>>> from pycrate_core.utils import pack_val
>>> help(pack_val) # a doc string is provided
[...]

The function gets a serie of 3-tuple as argument, each 3-tuple indicating the type, value and length in bits of the element to be packed. the type must be one of the following: TYPE_BYTES, TYPE_UINT, TYPE_UINT_LE, TYPE_INT or TYPE_INT_LE. When using TYPE_BYTES, the value must be a bytes' buffer, for all other types, it must be a (signed or unsigned) integral value. For little endian integral value, the length in bits must be byte-aligned.

It returns a 2-tuple with a bytes' buffer and its length in bits. In case of error, a PycrateErr exception is raised.

>>> pack_val((TYPE_BYTES, b'AAAA', 32), (TYPE_UINT, 256, 12), (TYPE_INT, -256, 12))
(b'AAAA\x10\x0f\x00', 56)
>>> pack_val((TYPE_BYTES, b'AAAA', 31), (TYPE_UINT, 256, 11), (TYPE_INT, -256, 11))
(b'AAA@@8\x00', 53)

Element parent class

The pycrate library provides a set of classes and routines to define simple and complex structures, and automate then the packing of values according to those structures to return bytes' buffer, and unpacking bytes' buffer to return the values according to them.

The Element class is the master parent class for defining structures with Pycrate. It is defined in the pycrate_core/elt.py file. It defines common methods for atomic (Atom, ...) and composite (Envelope, ...) classes. It must not be used directly when defining structures.

Atom basic classes

TODO

Envelope class

TODO

Array class

TODO

Sequence class

TODO