Python module to handle type-length-value (TLV) encoded data 8-bit type, 8-bit length, and N-byte value as described within the Apple HomeKit Accessory Protocol Specification Non-Commercial Version Release R2.
Project description
Type-Length-Value8 (TLV8) for python
Type-Length-Value (TLV) are used to encode arbitrary data. In this case the type and length are represented by 1 byte each. Hence the name TLV8.
A TLV8 entry consists of the following parts:
- the type: this 8 bit field denotes the type of information that is represented by the data.
- the length: this 8 bit field denotes the length of the data (this does not include the 2 bytes for type and length. For data longer than 255 bytes, there is a defined procedure available.
- the value: these length bytes represent the value of this TLV. The different types of data is represented differently:
- bytes: this is raw binary data and will be used as is, no further interpretation takes place
- tlv8: this is a specialized case of bytes values. Using this instead of pure bytes enables nesting of data and creating a hierarchy.
- integer: integers are stored in little-endian byte order and are encoded with the minimal number of bytes possible (1, 2, 4 or 8)
- float: floats are stored as little-endian ieee754 numbers
- string: strings are always UTF-8 encoded and do not contain the terminating NULL byte
TLV8 entries whose content is longer than 255 bytes are split up into fragments. The type is repeated is repeated in each fragment, only the last fragment may contain less than 255 bytes. Fragments of one TLV8 entry must be continuous.
Multiple TLV8 entries can be combined to create larger structures. Entries of different types can placed one after another. Entries of the same type must be separated by a TLV8 entry of a different type (and probably zero length).
TLV8 entries of unknown or unwanted type are to be silently ignored.
Examples
simple TLV8s
Encoding of some atomic examples:
- an empty TLV of type 42:
[42, None]
will be encoded asb'\x2a\x00'
. - a TLV of type 2 with 2 bytes
0x12, 0x34
:[2, b'\x12\x34']
will be encoded asb'\x02\x02\x12\x34'
- a TLV of type 3 that contains the TLV from above:
[3, [2, b'\x12\x34']]
will be encoded asb'\x03\x04\x02\x02\x12\x34'
- a TLV of type 4 that contains 1024:
[4, 1024]
will be encoded asb'\x04\0x02\x00\x04'
- a TLV of type 5 that contains 3.141:
[4, 3.141]
will be encoded asb'\x04\x04\x0a\xd7\x23\x41'
- a TLV of type 23 with string
Hello 🌍
:[23, 'Hello 🌍']
will be encoded asb'\x17\x0a\x48\x65\x6c\x6c\x6f\x20\xf0\x9f\x8c\x8d'
fragmented TLV8s
Encoding of a fragmented TLV8 entry:
- an TLV of type 6 that contains 256 bytes from 0 to 255:
[6, b'\x00\x01...\xfe\xff']
will be encoded asb'\x06\xff\x00...\xfe\x06\x01\xff'
combined TLV8s
Encoding of two TLV8 Entries that follow each other in the input list:
- the combination of 2 TLV8 entries (
[1, 123]
and[2, 'Hello']
) will be encoded asb'\x01\x01\x7b\x02\x05\x48\x65\x6c\x6c\x6f'
sequences of TLV8s of same type:
- a sequence of 3 TLV8 entries of type 1 (
[1, 1]
,[1, 2]
and[1, 1]
) will be encoded asb'\x01\x01\x01\xff\x00\x01\x01\x02\xff\x00\x01\x01\x03'
Using in code
There are two main use cases of this module.
Create a bytes representation
Here we want to have a comfortable way to create a data structure in python and to encode this structure into a bytes value.
encode a simple list
For example, create a representation containing the following structure:
- Type: 1, Value: 23
- Type: 2, Value: 2345
This can be code like that:
import tlv8
structure = [
tlv8.Entry(1, 23),
tlv8.Entry(2, 2345)
]
bytes_data = tlv8.encode(structure)
print(bytes_data)
And this will result in: b'\x01\x01\x17\x02\x02)\t'
Nesting structures
Representing a line ([x: 10, y: 20] - [x: 30, y: 40]) between to points could be represented like:
- Type: 1, Value:
- Type: 3, Value: 10
- Type: 4, Value: 20
- Type: 2, Value:
- Type: 3, Value: 30
- Type: 4, Value: 40
import tlv8
structure = [
tlv8.Entry(1, [
tlv8.Entry(3, 10),
tlv8.Entry(4, 10),
]),
tlv8.Entry(2, [
tlv8.Entry(3, 30),
tlv8.Entry(4, 40),
])
]
bytes_data = tlv8.encode(structure)
print(bytes_data)
And this will result in: b'\x01\x06\x03\x01\n\x04\x01\n\x02\x06\x03\x01\x1e\x04\x01('
Decode a bytes representation
Decoding TLV8 entries from bytes data will return all bytes from all first level entries. This includes possible separator entries between entries of the same type.
Decoding can be assisted by hinting with an expected structure. To represent the structure in python dict
objects are used and nested. The keys of the dict
objects are the type ids of the TLV8 entries. If the id of an entry is not contained in the structure, it will be ignored.
decode the simple list
import tlv8
in_data = b'\x01\x01\x17\x02\x02)\t'
expected_structure = {
1: tlv8.DataType.INTEGER,
2: tlv8.DataType.INTEGER
}
result = tlv8.decode(in_data, expected_structure)
print(tlv8.format_string(result))
This will result in:
[
<1, 23>,
<2, 2345>,
]
decode nested data
</code></pre>
<p>This will result in:</p>
<pre lang="text"><code>[
<1, [
<3, 10>,
<4, 10>,
]>,
<2, [
<3, 30>,
<4, 40>,
]>,
]
Coding
The module offers the following primary functions and classes.
function format_string
This function formats a list of TLV8 Entry objects as str. The hierarchy of the entries will be represented by increasing the indentation of the output.
The parameters are:
entries
: a python list of tlv8.Entries objectsindent
: the level of indentation to be used, this defaults to 0 and is increased on recursive calls for nested entries.
The function returns a str
instance and raises ValueError
instances if the input is not a list of tlv8.Entry
objects.
Example:
import tlv8
data = [
tlv8.Entry(1, 3.141),
tlv8.Entry(2, [
tlv8.Entry(3, 'hello'),
tlv8.Entry(4, 'world'),
]),
tlv8.Entry(1, 2)
]
print(tlv8.format_string(data))
This will become:
[
<1, 3.141>,
<2, [
<3, hello>,
<4, world>,
]>,
<1, 2>,
]
function encode
Function to encode a list of tlv8.Entry
objects into a sequence of bytes following the rules for creating TLVs. The separator_type_id
is used for the separating entries between two entries of the same type.
The parameters are:
entries
: a list oftlv8.Entry
objectsseparator_type_id
: the 8-bit type id of the separator to be used. The default is (as defined in table 5-6, page 51 of HomeKit Accessory Protocol Specification Non-Commercial Version Release R2) 0xff.
The function returns an instance of bytes
. This is empty if nothing was encoded. The function raises ValueError
if the input parameter is not a list of tlv8.Entry
objects or a data value is not encodable.
Example:
import tlv8
data = [
tlv8.Entry(1, 3.141),
tlv8.Entry(2, [
tlv8.Entry(3, 'hello'),
tlv8.Entry(4, 'world')
]),
tlv8.Entry(1, 2)
]
print(tlv8.encode(data))
This will result in:
b'\x01\x04%\x06I@\x02\x0e\x03\x05hello\x04\x05world\x01\x01\x02'
function decode
class DataType
This enumeration is used to represent the data type of a tlv8.Entry
.
Enumeration Entry | TLV8 type | Python type |
---|---|---|
BYTES | bytes | bytes |
TLV8 | tlv8 | custom class tlv8.Entry for encoding and dict for the expected structure during decoding |
INTEGER | integer | int |
FLOAT | float | float |
STRING | string | str |
AUTODETECT | n/a | this is used declare that a data type is not preset but will be determined by the python type of the data |
class Entry
This class represents a single entry in a TLV8 data set. The class overrides the methods __eq__
, __str__
and __repr__
to fit the needs of the application.
constructor
The constructor takes the following parameters:
type_id
: the type id of the entry. Must be between 0 and 255 (8-bit type id).data
: the data to be stored in this entry.data_type
: the data type of the entry. Defaults toDataType.AUTODETECT
.length
: if set, this overrides the automatic length detection. This used for integer, when there is special need to set higher byte count than the value would need.
The constructor raises a ValueError
if the type_id
is not within the 8-bit range.
encode() -> bytes
This function is called to encode the data stored in this Entry
. The data type of the data will be used to decide how to encode the data. It uses the tlv8.encode()
function to encode nested lists of tlv8.Entry
objects.
format_string() -> str
This function formats the data stored in this entry as readable string. It is mostly called by tlv8.format_string()
.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
File details
Details for the file tlv8-0.1.0.tar.gz
.
File metadata
- Download URL: tlv8-0.1.0.tar.gz
- Upload date:
- Size: 9.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/3.1.1 pkginfo/1.5.0.1 requests/2.22.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.42.1 CPython/3.7.3
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | b8deb2bf6823dcae0cac491af74dfee356f962951cc33c4ee7232d0b941bd690 |
|
MD5 | 7fd65ecb2a94c50b98426e581d40efe2 |
|
BLAKE2b-256 | eabb453bee282e90cf7d759f43974ffcdcc14c7f9a6bd75d6e46c2052d55f879 |