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 withpadding
. If set to acallable
(lambda or function) the result is a fixed or variable length string padded withpadding
.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:
- 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:
- 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_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 withpadding
. If set to acallable
(lambda or function) the result is a fixed or variable length string padded withpadding
.
- 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. Extendsoser.String
but length must not beNone
.- 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:
- Returns:
the number of bytes that were decoded.
- Return type:
- 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_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 withpadding
. If set to acallable
(lambda or function) the result is a fixed or variable length string padded withpadding
.
- 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:
- 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:
- 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 toFalse
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.
- 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
- 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 isFalse
.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:
- Returns:
the number of bytes that were decoded.
- Return type:
- 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 toFalse
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_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_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
.
- 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:
- 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:
- 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 toFalse
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_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_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
.
- 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:
- 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:
- 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 toFalse
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_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_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 adict
).
- 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
.
- up() ByteStruct | BitStruct
return parent element
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:
- Returns:
the number of bytes that were decoded.
- Return type:
- 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_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