Context Based Types

The Context

Every OSER instance is aware of the context it is located in. Context based types can use the context to realize context dependent instances. For example it is possible to create a variable length payload for a network packet or create one instance that can represent multiple different network packets, etc. .

The context can be used using a lambda-expression or a method.

String

A oser.String can be used to encode and decode strings. The length can be either fixed (int), variable (callable) or None for null-terminated strings.

class oser.String(length: Callable | int | None = None, value: bytes | str = b'', padding: bytes | str = b'\x00')

String builds a string serializer with a fixed or variable length.

Parameters:
  • length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

  • value="" – the initial string value.

  • padding="" – that padding pattern used for filling the encoded data if value is shorter than length.

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data – the data buffer that is decoded.

  • full_data – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

get() bytes

Return the value.

get_byte_size() int

Return the size in bytes.

get_size() int

Return the size in bytes.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: bytes | str) None

Set the value.

Parameters:

value – the new value

set_fuzzing_values(values: Generator[Any, None, None] | List[Any] | None) None

Set fuzzing values.

Parameters:

values – the values used for fuzzing.

set_length(length: Callable | int | None) None

Set the length.

Parameters:

length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

up() ByteStruct | BitStruct

Return the parent element.

Fixed length usage:

>>> from oser import String, to_hex

>>> instance = String(length=10, value=b"abcdefghi")
>>> print(instance)
'abcdefghi'

>>> print(instance.introspect())
   -    -  String():
   0 \x61      'a'
   1 \x62      'b'
   2 \x63      'c'
   3 \x64      'd'
   4 \x65      'e'
   5 \x66      'f'
   6 \x67      'g'
   7 \x68      'h'
   8 \x69      'i'
   9 \x00      '\x00'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9
\x61\x62\x63\x64\x65\x66\x67\x68\x69\x00
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
10
>>> print(instance)
'abcdefghi\x00'

Variable length usage in a oser.ByteStruct:

>>> from oser import ByteStruct, UBInt16, String, to_hex

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(12)
...         self.string = String(length=lambda self: self.length.get(), value="abcdefghijkl")
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 12 (UBInt16)
    string: 'abcdefghijkl'

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 12 (UBInt16)
   1 \x0c
   -    -      string: String():
   2 \x61          'a'
   3 \x62          'b'
   4 \x63          'c'
   5 \x64          'd'
   6 \x65          'e'
   7 \x66          'f'
   8 \x67          'g'
   9 \x68          'h'
  10 \x69          'i'
  11 \x6a          'j'
  12 \x6b          'k'
  13 \x6c          'l'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9| 10| 11| 12| 13
\x00\x0C\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C
>>> bytes_decoded = instance.decode(b"\x00\x1A\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6A\x6B\x6C\x6D\x6E\x6F\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7A\x00")
>>> print(bytes_decoded)
28
>>> print(instance)
Data():
    length: 26 (UBInt16)
    string: 'abcdefghijklmnopqrstuvwxyz'

Null-terminated oser.String:

>>> from oser import String, to_hex

>>> instance = String(length=None, value=b"abc")
>>> print(instance)
'abc'

>>> print(instance.introspect())
   -    -  String():
   0 \x61      'a'
   1 \x62      'b'
   2 \x63      'c'
   3 \x00      '\x00'

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3
\x61\x62\x63\x00
>>> bytes_decoded = instance.decode(b"abcdefg\x00this text will not be parsed!")
>>> print(bytes_decoded)
8
>>> print(instance)
'abcdefg'

>>> print(instance.size())
8
>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7
\x61\x62\x63\x64\x65\x66\x67\x00

Data

A oser.Data can be used to encode and decode data (strings). The length can be either fixed or variable.

class oser.Data(length: Callable | int, value: bytes | str = b'', padding: bytes | str = b'\x00')

Data builds a data serializer with fixed or variable length. Extends oser.String but length must not be None.

Parameters:
  • length – states the data length. Must be a callable (e.g. lambda) or an integer.

  • value="" – the initial data value.

  • padding=b"" – that padding pattern used for filling the encoded data if value is shorter than length.

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

get() bytes

Return the value.

get_byte_size() int

Return the size in bytes.

get_size() int

Return the size in bytes.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None (object) – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: bytes | str) None

Set the value.

Parameters:

value – the new value

set_fuzzing_values(values: Generator[Any, None, None] | List[Any] | None) None

Set fuzzing values.

Parameters:

values (iterable) – the values used for fuzzing.

set_length(length: Callable | int | None) None

Set the length.

Parameters:

length=None – states the string length. Can be a callable (e.g. lambda) or a scalar. If set to None the result is a null-terminated string. If set to an Integer the result is a fixed length string padded with padding. If set to a callable (lambda or function) the result is a fixed or variable length string padded with padding.

up() ByteStruct | BitStruct

Return the parent element.

Array

An oser.Array can be used to repeat parts of an instance. The length can be either fixed or variable.

class oser.Array(length: Callable | int, prototype: ByteStruct | BitStruct | ByteType | BitType, args: Tuple | None = None, kwargs: Dict | None = None, values: List[ByteStruct | BitStruct | ByteType | BitType] | None = None)

Array builds an array serializer with a fixed or variable length.

Parameters:
  • length – states the array length. Can be a callable (e.g. lambda) or a scalar.

  • prototype – a class that is the prototype for values

  • args=None – a tuple of positional arguments that are passed to prototype when it is instantiated when array size is extended. Note: don’t forget a leading comma when passing one value (1,) ((1) is an integer!).

  • kwargs=None – a dictionary of named arguments that are passed to prototype when it is instantiated when array size is extended.

  • values=None – the initial array values. Can be left empty. If empty the array is automatically resized with instances of prototype(*args, **kwargs).

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data – the data buffer that is decoded.

  • full_data – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

from_dict(data: Dict) None

Fill self with data.

Parameters:

data – data to be used to fill the calling instance.

fuzzing_iterator(copy: bool = False) Generator[Any, None, None]

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters:

copy=False – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields:

the fuzzing combinations.

get() Array

Return the value.

Returns:

the value

get_byte_size() int

Return the current size in bytes.

get_size() int

Return the current size in bytes or bits according to the context.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

to_dict() List[Dict[Any, Any]]

Return self as a dict()

up() ByteStruct | BitStruct

return parent element

Fixed length usage:

>>> from oser import Array, UBInt16, to_hex

>>> instance = Array(length=3, prototype=UBInt16, values=[UBInt16(ii) for ii in range(3)])
>>> print(instance)
Array():
[
    @0: 0 (UBInt16)
    @1: 1 (UBInt16)
    @2: 2 (UBInt16)
]

>>> print(instance.introspect())
   -    -  Array():
   -    -  [
   0 \x00      @0: 0 (UBInt16)
   1 \x00
   2 \x00      @1: 1 (UBInt16)
   3 \x01
   4 \x00      @2: 2 (UBInt16)
   5 \x02
   -    -  ]

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5
\x00\x00\x00\x01\x00\x02
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
6
>>> print(instance)
Array():
[
    @0: 0 (UBInt16)
    @1: 1 (UBInt16)
    @2: 2 (UBInt16)
]

>>> print(instance[10])
IndexError: list index out of range

Variable length usage:

>>> from oser import ByteStruct, Array, UBInt16, to_hex

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(4)
...         self.data = Array(length=lambda self: self.length.get(),
...                           prototype=UBInt16,
...                           args=(1337,), # when range is extended
...                                         # instantiate prototype with this arguments
...                           values=[UBInt16(ii) for ii in range(2)])
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 1337 (UBInt16)
        @3: 1337 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 4 (UBInt16)
   1 \x04
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x05          @2: 1337 (UBInt16)
   7 \x39
   8 \x05          @3: 1337 (UBInt16)
   9 \x39
   -    -      ]

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8|  9
\x00\x04\x00\x00\x00\x01\x05\x39\x05\x39
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
10
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 1337 (UBInt16)
        @3: 1337 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 4 (UBInt16)
   1 \x04
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x05          @2: 1337 (UBInt16)
   7 \x39
   8 \x05          @3: 1337 (UBInt16)
   9 \x39
   -    -      ]

>>> print(instance.data[3])
1337 (UBInt16)

>>> instance.decode(b"\x00\x14\x00\x00\x00\x01\x00\x02\x00\x03\x00\x04\x00\x05\x00\x06\x00\x07\x00\x08\x00\x09\x00\x0A\x00\x0B\x00\x0C\x00\x0D\x00\x0E\x00\x0F\x00\x10\x00\x11\x00\x12\x00\x13")
42
>>> print(instance)
Data():
    length: 20 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 2 (UBInt16)
        @3: 3 (UBInt16)
        @4: 4 (UBInt16)
        @5: 5 (UBInt16)
        @6: 6 (UBInt16)
        @7: 7 (UBInt16)
        @8: 8 (UBInt16)
        @9: 9 (UBInt16)
        @10: 10 (UBInt16)
        @11: 11 (UBInt16)
        @12: 12 (UBInt16)
        @13: 13 (UBInt16)
        @14: 14 (UBInt16)
        @15: 15 (UBInt16)
        @16: 16 (UBInt16)
        @17: 17 (UBInt16)
        @18: 18 (UBInt16)
        @19: 19 (UBInt16)
    ]

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      length: 20 (UBInt16)
   1 \x14
   -    -      data: Array():
   -    -      [
   2 \x00          @0: 0 (UBInt16)
   3 \x00
   4 \x00          @1: 1 (UBInt16)
   5 \x01
   6 \x00          @2: 2 (UBInt16)
   7 \x02
   8 \x00          @3: 3 (UBInt16)
   9 \x03
  10 \x00          @4: 4 (UBInt16)
  11 \x04
  12 \x00          @5: 5 (UBInt16)
  13 \x05
  14 \x00          @6: 6 (UBInt16)
  15 \x06
  16 \x00          @7: 7 (UBInt16)
  17 \x07
  18 \x00          @8: 8 (UBInt16)
  19 \x08
  20 \x00          @9: 9 (UBInt16)
  21 \x09
  22 \x00          @10: 10 (UBInt16)
  23 \x0a
  24 \x00          @11: 11 (UBInt16)
  25 \x0b
  26 \x00          @12: 12 (UBInt16)
  27 \x0c
  28 \x00          @13: 13 (UBInt16)
  29 \x0d
  30 \x00          @14: 14 (UBInt16)
  31 \x0e
  32 \x00          @15: 15 (UBInt16)
  33 \x0f
  34 \x00          @16: 16 (UBInt16)
  35 \x10
  36 \x00          @17: 17 (UBInt16)
  37 \x11
  38 \x00          @18: 18 (UBInt16)
  39 \x12
  40 \x00          @19: 19 (UBInt16)
  41 \x13
   -    -      ]

>>> print(instance.data[15])
15 (UBInt16)

Setting and getting items:

>>> from oser import ByteStruct, Array, UBInt16

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.length = UBInt16(4)
...         self.data = Array(length=lambda self: self.length.get(),
...                           prototype=UBInt16,
...                           values=[UBInt16(ii) for ii in range(2)])
...
>>> instance = Data()
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 1 (UBInt16)
        @2: 0 (UBInt16)
        @3: 0 (UBInt16)
    ]


>>> # set a single item
... instance.data[1] = 23
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 0 (UBInt16)
        @1: 23 (UBInt16)
        @2: 0 (UBInt16)
        @3: 0 (UBInt16)
    ]


>>> # set multiple items (with a slice)
... instance.data[:] = [1, 2, 3, 4]
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 1 (UBInt16)
        @1: 2 (UBInt16)
        @2: 3 (UBInt16)
        @3: 4 (UBInt16)
    ]


>>> # set every second item
... instance.data[::2] = [23, 24]
>>> print(instance)
Data():
    length: 4 (UBInt16)
    data: Array():
    [
        @0: 23 (UBInt16)
        @1: 2 (UBInt16)
        @2: 24 (UBInt16)
        @3: 4 (UBInt16)
    ]


>>> # get a single item
... print(instance.data[3])
4 (UBInt16)


>>> # get a slice
... print(instance.data[0:2])
[<oser.typedef.UBInt16 object at 0x7f8d8a86a748>, <oser.typedef.UBInt16 object at 0x7f8d8a86d470>]

If

oser.If is used to build conditional parsers when the condition is True or False. Compared to IfElse If is invisble if the condition is False.

class oser.If(condition: Callable | bool, if_true: ByteStruct | BitStruct | ByteType | BitType)

If builds a conditional serializer for boolean conditions that is invisible if the condition is False.

It is an alias for IfElse(condition=condition, if_true=if_true, if_false=Nothing())

Parameters:
  • condition – the condition which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • if_true – is used if condition is True.

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

from_dict(data: Dict) None

Fill self with data.

Parameters:

data (dict) – data to be used to fill the calling instance.

fuzzing_iterator(copy: bool = False) Generator[Any, None, None]

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters:

copy=False (bool) – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields:

the fuzzing combinations.

get() ByteStruct | BitStruct | ByteType | BitType

Return the current value.

get_byte_size() int

Return the current size in bytes.

get_current() ByteStruct | BitStruct | ByteType | BitType

Return the currently selected object.

get_false_value() ByteStruct | BitStruct | ByteType | BitType

Return the value for a False condition.

get_size() int

Return the current size in bytes or bits according to the context.

get_true_value() ByteStruct | BitStruct | ByteType | BitType

Return the value for a True condition.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None (object) – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value.

Parameters:

value – the new value

set_false_value(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value for a False condition.

Parameters:

value – the new value for a False condition.

set_true_value(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value for a True condition.

Parameters:

value – the new value for a True condition.

to_dict() Dict[Any, Any] | List[Dict[Any, Any]]

Return self as a dict()

up() ByteStruct | BitStruct

return parent element

Usage:

>>> from oser import ByteStruct, If, UBInt8, UBInt64, to_hex

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.condition = UBInt8(1)
...         self.data = If(condition=lambda self: self.condition.get() > 0,
...                        if_true=UBInt8(1))
...
>>> instance = Data()
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
2
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> instance.condition.set(0)
>>> print(instance)
Data():
    condition: 0 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      condition: 0 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0
\x00

IfElse

oser.IfElse is used to build conditional parsers when the condition is True or False. If there are more possible values oser.Switch can be used.

class oser.IfElse(condition: Callable | bool, if_true: ByteStruct | BitStruct | ByteType | BitType, if_false: ByteStruct | BitStruct | ByteType | BitType)

IfElse builds a conditional serializer for boolean conditions.

Parameters:
  • condition – the condition which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • if_true – is used if condition is True.

  • if_false – is used if condition is False.

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data – the data buffer that is decoded.

  • full_data – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

from_dict(data: Dict) None

Fill self with data.

Parameters:

data – data to be used to fill the calling instance.

fuzzing_iterator(copy: bool = False) Generator[Any, None, None]

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters:

copy=False – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields:

the fuzzing combinations.

get() ByteStruct | BitStruct | ByteType | BitType

Return the current value.

get_byte_size() int

Return the current size in bytes.

get_current() ByteStruct | BitStruct | ByteType | BitType

Return the currently selected object.

get_false_value() ByteStruct | BitStruct | ByteType | BitType

Return the value for a False condition.

get_size() int

Return the current size in bytes or bits according to the context.

get_true_value() ByteStruct | BitStruct | ByteType | BitType

Return the value for a True condition.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value.

Parameters:

value – the new value

set_false_value(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value for a False condition.

Parameters:

value – the new value for a False condition.

set_true_value(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the value for a True condition.

Parameters:

value – the new value for a True condition.

to_dict() Dict[Any, Any] | List[Dict[Any, Any]]

Return self as a dict()

up() ByteStruct | BitStruct

return parent element

Usage:

>>> from oser import ByteStruct, IfElse, UBInt8, UBInt64, to_hex

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.condition = UBInt8(1)
...         self.data = IfElse(condition=lambda self: self.condition.get() > 0,
...                            if_true=UBInt8(1),
...                            if_false=UBInt64(0x0123456789abcdef)
...                            )
...
>>> instance = Data()
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
2
>>> print(instance)
Data():
    condition: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      condition: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> instance.condition.set(0)
>>> print(instance)
Data():
    condition: 0 (UBInt8)
    data: 81985529216486895 (UBInt64)

>>> print(instance.introspect())
   -    -  Data():
   0 \x00      condition: 0 (UBInt8)
   1 \x01      data: 81985529216486895 (UBInt64)
   2 \x23
   3 \x45
   4 \x67
   5 \x89
   6 \xab
   7 \xcd
   8 \xef

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4|  5|  6|  7|  8
\x00\x01\x23\x45\x67\x89\xAB\xCD\xEF

Switch

oser.Switch is used to build conditional parsers where condition is not only True or False. If the condition can only be True or False oser.IfElse can be used, too.

class oser.Switch(condition: Callable | int, values: Dict[Any, ByteStruct | BitStruct | ByteType | BitType], default: ByteStruct | BitStruct | ByteType | BitType | None = None)

Switch builds a conditional serializer.

Parameters:
  • condition – the key which selects the desired values. Can be a callable (e.g. lambda) or a scalar.

  • values – a dictionary that holds key-value pairs. The key is selected by the value (or return value) of condition and the value is used as the actual serializer.

  • default=None – if not None this value is used as a serializer if condition is not found in values

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data – the data buffer that is decoded.

  • full_data – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

delete_value(key: Any) None

Delete the value identified by key.

Parameters:

key – the key for the value to be deleted.

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

from_dict(data: Dict) None

Fill self with data.

Parameters:

data – data to be used to fill the calling instance.

fuzzing_iterator(copy: bool = False) Generator[Any, None, None]

The fuzzing iterator iterates over all combinations of set fuzzing values (oser.ByteType.set_fuzzing_values()). If no fuzzing values are set the current struct is yielded.

Parameters:

copy=False – if set to True the generated fuzzing values are deep copies of the original. Creating these deep copies is slow. If set to False the original struct is retruned and the generated value must be used immediately since the next generated overwrites the values on the same value.

Yields:

the fuzzing combinations.

get() ByteStruct | BitStruct | ByteType | BitType

Return the current value.

get_byte_size() int

Return the current size in bytes.

get_current() ByteStruct | BitStruct | ByteType | BitType

Return the currently selected object.

get_items() Iterable[ByteStruct | BitStruct | ByteType | BitType]

Return the items of the values (like in a dict).

get_keys() Iterable[Any]

Return the keys of the values (like in a dict).

get_size() int

Return the current size in bytes or bits according to the context.

get_value(key: Any) ByteStruct | BitStruct | ByteType | BitType

Get the value for key.

Parameters:

key – the key for the condition.

Returns:

the value for the condition key.

get_values() Iterable[ByteStruct | BitStruct | ByteType | BitType]

Return the values of the values (like in a dict).

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: ByteStruct | BitStruct | ByteType | BitType) None

Set the current value.

Parameters:

value – the new value.

set_value(key: Any, value: ByteStruct | BitStruct | ByteType | BitType) None

Get the value for key.

Parameters:
  • key – the key for the condition.

  • value – the value for the key.

to_dict() Dict

Return self as a dict()

up() ByteStruct | BitStruct

return parent element

update_values(values: Dict[Any, ByteStruct | BitStruct | ByteType | BitType]) None

Update the values. Overwrites or adds new keys and values.

Parameters:

values – the dict with the new values.

Usage:

>>> from oser import ByteStruct, Switch, UBInt8, UBInt16, UBInt32, Null, to_hex

>>> class Data(ByteStruct):
...     def __init__(self):
...         super(Data, self).__init__()
...
...         self.type = UBInt8(1)
...         self.data = Switch(lambda self: self.type.get(),
...                            values={
...                                1: UBInt8(1),
...                                2: UBInt16(2),
...                                3: UBInt32(3)
...                            },
...                            default=Null())
...
>>> instance = Data()
>>> print(instance)
Data():
    type: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      type: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1
\x01\x01
>>> bytes_decoded = instance.decode(binary)
>>> print(bytes_decoded)
2
>>> print(instance)
Data():
    type: 1 (UBInt8)
    data: 1 (UBInt8)

>>> print(instance.introspect())
   -    -  Data():
   0 \x01      type: 1 (UBInt8)
   1 \x01      data: 1 (UBInt8)

>>> # 3
... instance.type.set(3)
>>> print(instance)
Data():
    type: 3 (UBInt8)
    data: 3 (UBInt32)

>>> print(instance.introspect())
   -    -  Data():
   0 \x03      type: 3 (UBInt8)
   1 \x00      data: 3 (UBInt32)
   2 \x00
   3 \x00
   4 \x03

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0|  1|  2|  3|  4
\x03\x00\x00\x00\x03
>>> # non existent value -> Null()
... instance.type.set(100)
>>> print(instance)
Data():
    type: 100 (UBInt8)
    data: Null (Null)

>>> print(instance.introspect())
   -    -  Data():
   0 \x64      type: 100 (UBInt8)
   -    -      data: Null

>>> binary = instance.encode()
>>> print(to_hex(binary))
   0
\x64

Value

oser.Value evaluates a value that only appears in string or introspection representation but not in the encoded data.

class oser.Value(value: Callable | Any, format: str | None = None)

Value evaluates a value for introspection. It does not appear in the encoded data.

Parameters:
  • value (callable) – a method or a lambda to generate the value.

  • format=None (string) – Use “hex” to format values to hexadecimal. Use “bin” to format values to binary.

decode(data: bytes, full_data: bytes = b'', context_data: bytes = b'') int

Decode a binary string and return the number of bytes that were decoded.

Parameters:
  • data (bytes) – the data buffer that is decoded.

  • full_data (bytes) – the binary data string until the part to be decoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the number of bytes that were decoded.

Return type:

int

encode(full_data: bytes = b'', context_data: bytes = b'') bytes

Return the encoded binary string.

Parameters:
  • full_data (bytes) – the binary data string until the part to be encoded. The user normally does not need to supply this.

  • context_data (bytes) – the binary data of the current context. The user normally does not need to supply this.

Returns:

the encoded binary string.

Return type:

bytes

get() Any

Return the value.

Returns:

the value.

get_byte_size() int

The size is always 0 because Value does not appear in the encoded data.

get_size() int

The size is always 0 because Value does not appear in the encoded data.

introspect(stop_at: ByteStruct | BitStruct | ByteType | BitType | None = None) str

Return the introspection representation of the object as a string.

Parameters:

stop_at=None (object) – stop introspection at stop_at.

root() ByteStruct | BitStruct

return root element

set(value: Callable | Any) None

Set the value.

Parameters:

value – the new value

set_fuzzing_values(values: Generator[Any, None, None] | List[Any] | None) None

Set fuzzing values.

Parameters:

values (iterable) – the values used for fuzzing.

up() ByteStruct | BitStruct

Return the parent element.

Usage:

>>> from oser import ByteStruct, ULInt8, ULInt16, to_hex, Value

>>> class Foo(ByteStruct):
...     def __init__(self):
...         super(Foo, self).__init__()
...         self.a = ULInt8(2)
...         self.b = ULInt16(7)
...         self.value = Value(value=lambda self: self.a.get() * self.b.get())
...
>>> foo = Foo()
>>> print(foo)
Foo():
    a: 2 (ULInt8)
    b: 7 (ULInt16)
    value: 14 (Value)

>>> print(foo.introspect())
   -    -  Foo():
   0 \x02      a: 2 (ULInt8)
   1 \x07      b: 7 (ULInt16)
   2 \x00
   -    -      value: 14
>>> binary = foo.encode()
>>> print(to_hex(binary))
   0|  1|  2
\x02\x07\x00