ltcodecs 4.3.1__tar.gz → 4.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/.gitignore +2 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/.gitlab-ci.yml +8 -5
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/PKG-INFO +1 -1
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/codec_configs.rst +87 -2
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/reference/codec_aliases.rst +4 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/reference/codecs.rst +16 -2
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/reference/config_schema.rst +95 -1
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/reference/python_api.rst +16 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/ros_messages.rst +38 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/pyproject.toml +2 -2
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/__init__.py +5 -0
- ltcodecs-4.4.0/src/ltcodecs/_length_modes.py +61 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/bytes_codec.py +27 -8
- ltcodecs-4.4.0/src/ltcodecs/constant_codec.py +41 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/dict_field_codec.py +28 -2
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/field_codec.py +9 -0
- ltcodecs-4.4.0/src/ltcodecs/inspect_cli.py +69 -0
- ltcodecs-4.4.0/src/ltcodecs/inspection.py +363 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/optional_field_codec.py +4 -1
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/padding_codec.py +2 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/ros_msg_field_codec.py +40 -9
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/string_codecs.py +41 -13
- ltcodecs-4.4.0/src/ltcodecs/variable_len_array_codec.py +199 -0
- ltcodecs-4.4.0/src/ltcodecs/variable_len_bit_array_codec.py +117 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/PKG-INFO +1 -1
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/SOURCES.txt +8 -1
- ltcodecs-4.4.0/src/ltcodecs.egg-info/entry_points.txt +2 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/scm_file_list.json +7 -1
- ltcodecs-4.4.0/src/ltcodecs.egg-info/scm_version.json +8 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/tests/test_codecs.py +250 -1
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/tests/test_dict_codec.py +49 -0
- ltcodecs-4.4.0/tests/test_inspection.py +133 -0
- ltcodecs-4.3.1/src/ltcodecs/variable_len_array_codec.py +0 -113
- ltcodecs-4.3.1/src/ltcodecs.egg-info/scm_version.json +0 -8
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/.gitlab/merge_request_templates/Default.md +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/CONTRIBUTING.md +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/LICENSE +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/README.md +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/Makefile +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/conf.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/index.rst +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/quickstart.rst +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/requirements.txt +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/docs/tests.rst +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/requirements.txt +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/setup.cfg +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/bool_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/ccl_latlon_bcd_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/ccl_latlon_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/dict_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/exceptions.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/exponentialgolomb_integer_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/fixed_len_array_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/fixedint_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/float_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/ieee_float_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/linspace_float_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/lzma_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/metadata_field_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/multiple_field_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/ros_message_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/rostime_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/string_enum_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs/varint_codec.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/dependency_links.txt +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/requires.txt +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/src/ltcodecs.egg-info/top_level.txt +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/tests/__init__.py +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/tests/dict_codec.yaml +0 -0
- {ltcodecs-4.3.1 → ltcodecs-4.4.0}/tests/string_msg_codec.yaml +0 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
include:
|
|
2
2
|
- project: acomms/ci_templates
|
|
3
|
-
ref:
|
|
3
|
+
ref: v3.2.0
|
|
4
|
+
# Note: Don't forget to update CI_TEMPLATES_REF to match this
|
|
4
5
|
file:
|
|
5
6
|
- /templates/python-docs.yml
|
|
6
7
|
- /templates/python-tests.yml
|
|
@@ -20,18 +21,20 @@ stages:
|
|
|
20
21
|
- deploy
|
|
21
22
|
|
|
22
23
|
variables:
|
|
24
|
+
# Must match the ci_templates include ref when using shared Dockerfiles.
|
|
25
|
+
CI_TEMPLATES_REF: "v3.2.0"
|
|
26
|
+
|
|
23
27
|
PYTHON_DOCS_PDF_ARTIFACT: "ltcodecs.pdf"
|
|
24
28
|
PYTHON_DOCS_PDF_SOURCE: "docs/_build/latex/ltcodecs.pdf"
|
|
25
29
|
|
|
26
30
|
ROS_ACOMMS_REF: "devel"
|
|
27
31
|
|
|
28
|
-
PYTHON_PYTEST_ARGS: "-vv -rA --capture=tee-sys"
|
|
29
|
-
PYTHON_PYTEST_COVERAGE_ARGS: "--cov=ltcodecs"
|
|
30
|
-
PYTHON_PYTEST_TARGET: "tests/"
|
|
31
|
-
|
|
32
32
|
native_tests:
|
|
33
33
|
extends: .armw_native_pytest_template
|
|
34
34
|
variables:
|
|
35
|
+
PYTHON_PYTEST_ARGS: "-vv -rA --capture=tee-sys"
|
|
36
|
+
PYTHON_PYTEST_COVERAGE_ARGS: "--cov=ltcodecs"
|
|
37
|
+
PYTHON_PYTEST_TARGET: "tests/"
|
|
35
38
|
NATIVE_TEST_SETUP_COMMANDS: |
|
|
36
39
|
pip install .
|
|
37
40
|
|
|
@@ -54,6 +54,19 @@ Common scalar aliases include ``string``, ``integer``, ``bool``, ``float``,
|
|
|
54
54
|
natural constraint, and ``linspace_float`` when a physical step size or number
|
|
55
55
|
of values is the natural constraint.
|
|
56
56
|
|
|
57
|
+
Constants
|
|
58
|
+
---------
|
|
59
|
+
|
|
60
|
+
Use ``constant`` for a configured field value that is not sent on the wire.
|
|
61
|
+
The codec emits zero bits, does not require the field to be present in the
|
|
62
|
+
source dictionary, and returns its configured value during decoding.
|
|
63
|
+
|
|
64
|
+
.. code-block:: yaml
|
|
65
|
+
|
|
66
|
+
protocol_version:
|
|
67
|
+
codec: constant
|
|
68
|
+
value: 2
|
|
69
|
+
|
|
57
70
|
Nested Dictionaries
|
|
58
71
|
-------------------
|
|
59
72
|
|
|
@@ -107,7 +120,7 @@ Variable-Length Arrays
|
|
|
107
120
|
----------------------
|
|
108
121
|
|
|
109
122
|
Use ``variable_len_array`` for a list whose encoded length is included in the
|
|
110
|
-
bit stream.
|
|
123
|
+
bit stream by default.
|
|
111
124
|
|
|
112
125
|
.. code-block:: yaml
|
|
113
126
|
|
|
@@ -117,7 +130,8 @@ bit stream.
|
|
|
117
130
|
element_type: uint8
|
|
118
131
|
|
|
119
132
|
``max_length`` is required. Values longer than ``max_length`` are currently
|
|
120
|
-
truncated during encoding.
|
|
133
|
+
truncated during encoding. Set ``min_length`` when the list always has at
|
|
134
|
+
least a known number of elements; encoding fails if a shorter list is provided.
|
|
121
135
|
|
|
122
136
|
For element codecs that need parameters, put those parameters under
|
|
123
137
|
``element_params``:
|
|
@@ -142,6 +156,55 @@ Set ``nullable: true`` when an empty list should encode as a single bit. When
|
|
|
142
156
|
nullable: true
|
|
143
157
|
element_type: uint8
|
|
144
158
|
|
|
159
|
+
With ``nullable: true`` and the default ``length_mode: varint``, the initial
|
|
160
|
+
bit indicates whether the list is longer than ``min_length``. If it is not,
|
|
161
|
+
the length prefix is omitted and the decoder reads exactly ``min_length``
|
|
162
|
+
elements.
|
|
163
|
+
|
|
164
|
+
Use ``length_mode: egint`` to encode the array length with exponential-Golomb
|
|
165
|
+
coding instead of the fixed-width ``varint`` length codec:
|
|
166
|
+
|
|
167
|
+
.. code-block:: yaml
|
|
168
|
+
|
|
169
|
+
sparse_samples:
|
|
170
|
+
codec: variable_len_array
|
|
171
|
+
min_length: 2
|
|
172
|
+
max_length: 10
|
|
173
|
+
length_mode: egint
|
|
174
|
+
element_type: uint8
|
|
175
|
+
|
|
176
|
+
Use ``length_mode: fill_outer`` when an outer frame already determines the
|
|
177
|
+
field size. This omits the length prefix and decodes elements sequentially
|
|
178
|
+
until the current bit stream is exhausted. A ``fill_outer`` field must be the
|
|
179
|
+
final encoded field in its containing ``dict`` or ``msg`` frame.
|
|
180
|
+
|
|
181
|
+
.. code-block:: yaml
|
|
182
|
+
|
|
183
|
+
payload:
|
|
184
|
+
codec: variable_len_array
|
|
185
|
+
max_length: 10
|
|
186
|
+
length_mode: fill_outer
|
|
187
|
+
element_type: uint8
|
|
188
|
+
|
|
189
|
+
Bit-Length Byte Arrays
|
|
190
|
+
----------------------
|
|
191
|
+
|
|
192
|
+
``variable_len_bit_array`` is a compact codec for a ``uint8`` payload with an
|
|
193
|
+
exact bit length. It replaces a separately encoded size field with a three-bit
|
|
194
|
+
unused-tail count.
|
|
195
|
+
|
|
196
|
+
.. code-block:: yaml
|
|
197
|
+
|
|
198
|
+
payload:
|
|
199
|
+
codec: variable_len_bit_array
|
|
200
|
+
max_length: 65535
|
|
201
|
+
bit_length_field: payload_size_in_bits
|
|
202
|
+
|
|
203
|
+
At encode time, the source message supplies both ``payload`` and
|
|
204
|
+
``payload_size_in_bits``. The bit-length field must not appear as another
|
|
205
|
+
configured field. Decoding returns the payload and derives
|
|
206
|
+
``payload_size_in_bits`` from the byte count and trailing unused-bit count.
|
|
207
|
+
|
|
145
208
|
Fixed-Length Arrays
|
|
146
209
|
-------------------
|
|
147
210
|
|
|
@@ -180,6 +243,28 @@ with ``element_params``.
|
|
|
180
243
|
z:
|
|
181
244
|
codec: float32
|
|
182
245
|
|
|
246
|
+
An all-constant element message can carry only the array length. Each decoded
|
|
247
|
+
element is still a fresh ROS message instance:
|
|
248
|
+
|
|
249
|
+
.. code-block:: yaml
|
|
250
|
+
|
|
251
|
+
links:
|
|
252
|
+
codec: variable_len_array
|
|
253
|
+
max_length: 7
|
|
254
|
+
element_type: msg
|
|
255
|
+
element_params:
|
|
256
|
+
ros_type: ros_acomms_net_msgs/Link
|
|
257
|
+
fields:
|
|
258
|
+
source:
|
|
259
|
+
codec: constant
|
|
260
|
+
value: 0
|
|
261
|
+
dest:
|
|
262
|
+
codec: constant
|
|
263
|
+
value: 0
|
|
264
|
+
modem:
|
|
265
|
+
codec: constant
|
|
266
|
+
value: ""
|
|
267
|
+
|
|
183
268
|
Optional Fields
|
|
184
269
|
---------------
|
|
185
270
|
|
|
@@ -45,8 +45,12 @@ Alias Codec Class
|
|
|
45
45
|
``string_enum`` :py:class:`~ltcodecs.string_enum_codec.StringEnumCodec`
|
|
46
46
|
``lzma`` :py:class:`~ltcodecs.lzma_codec.LzmaCodec`
|
|
47
47
|
``optional`` :py:class:`~ltcodecs.optional_field_codec.OptionalFieldCodec`
|
|
48
|
+
``constant`` :py:class:`~ltcodecs.constant_codec.ConstantCodec`
|
|
48
49
|
======================== ====================================================
|
|
49
50
|
|
|
51
|
+
``variable_len_bit_array``
|
|
52
|
+
:py:class:`~ltcodecs.variable_len_bit_array_codec.VariableLenBitArrayCodec`
|
|
53
|
+
|
|
50
54
|
Metadata Aliases
|
|
51
55
|
----------------
|
|
52
56
|
|
|
@@ -73,8 +73,17 @@ Container Codecs
|
|
|
73
73
|
|
|
74
74
|
``variable_len_array``
|
|
75
75
|
Encodes a list with a length prefix. ``max_length`` controls the largest
|
|
76
|
-
representable list
|
|
77
|
-
|
|
76
|
+
representable list, and ``min_length`` can require a lower bound. The
|
|
77
|
+
length prefix uses ``length_codec: varint`` by default; use
|
|
78
|
+
``length_codec: egint`` when exponential-Golomb coding is a better fit for
|
|
79
|
+
the expected length distribution. ``element_type`` and ``element_params``
|
|
80
|
+
configure the element codec.
|
|
81
|
+
|
|
82
|
+
``variable_len_bit_array``
|
|
83
|
+
Encodes a variable-length ``uint8`` array and derives an exact bit length
|
|
84
|
+
for a second field. The wire format contains the array length, byte values,
|
|
85
|
+
and a three-bit count of unused final-byte bits. Configure the derived field
|
|
86
|
+
with ``bit_length_field``; it is not a separate configured wire field.
|
|
78
87
|
|
|
79
88
|
``fixed_len_array``
|
|
80
89
|
Encodes a fixed number of elements. The encoded bitstream does not include
|
|
@@ -84,6 +93,11 @@ Container Codecs
|
|
|
84
93
|
Encodes a boolean presence field. When true, the configured
|
|
85
94
|
``target_fields`` are encoded immediately after the presence bit.
|
|
86
95
|
|
|
96
|
+
``constant``
|
|
97
|
+
Returns its configured ``value`` without consuming payload bits. This is
|
|
98
|
+
useful for decoded fields that are known by configuration rather than sent
|
|
99
|
+
on the wire. Constant fields do not need a source value during encoding.
|
|
100
|
+
|
|
87
101
|
Special-Purpose Codecs
|
|
88
102
|
----------------------
|
|
89
103
|
|
|
@@ -37,6 +37,16 @@ Common Field Keys
|
|
|
37
37
|
Maximum number of characters, bytes, or array elements for variable-length
|
|
38
38
|
codecs.
|
|
39
39
|
|
|
40
|
+
``min_length``
|
|
41
|
+
Minimum number of elements for ``variable_len_array``. Encoding fails when
|
|
42
|
+
the provided list is shorter than this value.
|
|
43
|
+
|
|
44
|
+
``length_mode``
|
|
45
|
+
Length handling mode for ``string``, ``bytes``, and
|
|
46
|
+
``variable_len_array``. Supported values are ``varint``, ``egint``, and
|
|
47
|
+
``fill_outer``. ``fill_outer`` omits the length prefix and relies on the
|
|
48
|
+
containing frame to define the field size.
|
|
49
|
+
|
|
40
50
|
``length``
|
|
41
51
|
Number of elements for ``fixed_len_array``.
|
|
42
52
|
|
|
@@ -46,6 +56,14 @@ Common Field Keys
|
|
|
46
56
|
``element_params``
|
|
47
57
|
Nested configuration passed to the element codec for an array.
|
|
48
58
|
|
|
59
|
+
``bit_length_field``
|
|
60
|
+
Name of the derived bit-length field emitted by ``variable_len_bit_array``
|
|
61
|
+
during decoding. The named field is supplied when encoding but must not be
|
|
62
|
+
configured separately in the same mapping.
|
|
63
|
+
|
|
64
|
+
``value``
|
|
65
|
+
YAML-native value returned by the zero-bit ``constant`` codec.
|
|
66
|
+
|
|
49
67
|
``nullable``
|
|
50
68
|
When true on ``variable_len_array``, an empty list is encoded as a single
|
|
51
69
|
absent/present bit.
|
|
@@ -91,15 +109,52 @@ Nested ROS messages use ``ros_type`` and may optionally provide ``fields``:
|
|
|
91
109
|
Arrays
|
|
92
110
|
------
|
|
93
111
|
|
|
94
|
-
Variable-length arrays include a length prefix:
|
|
112
|
+
Variable-length arrays include a length prefix by default:
|
|
113
|
+
|
|
114
|
+
.. code-block:: yaml
|
|
115
|
+
|
|
116
|
+
samples:
|
|
117
|
+
codec: variable_len_array
|
|
118
|
+
max_length: 8
|
|
119
|
+
element_type: uint16
|
|
120
|
+
|
|
121
|
+
Use ``min_length`` when every encoded array has at least a known number of
|
|
122
|
+
elements. With ``nullable: true`` and ``length_mode: varint``, a false flag
|
|
123
|
+
means the array has exactly ``min_length`` elements, so the length prefix is
|
|
124
|
+
omitted in that case.
|
|
125
|
+
|
|
126
|
+
.. code-block:: yaml
|
|
127
|
+
|
|
128
|
+
samples:
|
|
129
|
+
codec: variable_len_array
|
|
130
|
+
min_length: 2
|
|
131
|
+
max_length: 8
|
|
132
|
+
nullable: true
|
|
133
|
+
element_type: uint16
|
|
134
|
+
|
|
135
|
+
Use ``length_mode: egint`` to encode lengths with exponential-Golomb coding:
|
|
95
136
|
|
|
96
137
|
.. code-block:: yaml
|
|
97
138
|
|
|
98
139
|
samples:
|
|
99
140
|
codec: variable_len_array
|
|
141
|
+
min_length: 2
|
|
100
142
|
max_length: 8
|
|
143
|
+
length_mode: egint
|
|
101
144
|
element_type: uint16
|
|
102
145
|
|
|
146
|
+
Use ``length_mode: fill_outer`` for strings, bytes, or variable-length arrays
|
|
147
|
+
whose size is defined by an outer frame rather than an internal prefix. A
|
|
148
|
+
``fill_outer`` field must be the final encoded field in its containing
|
|
149
|
+
``dict`` or ``msg`` frame.
|
|
150
|
+
|
|
151
|
+
.. code-block:: yaml
|
|
152
|
+
|
|
153
|
+
payload:
|
|
154
|
+
codec: bytes
|
|
155
|
+
max_length: 32
|
|
156
|
+
length_mode: fill_outer
|
|
157
|
+
|
|
103
158
|
Fixed-length arrays omit the length prefix:
|
|
104
159
|
|
|
105
160
|
.. code-block:: yaml
|
|
@@ -138,6 +193,45 @@ Arrays of ROS messages use ``element_type: msg``:
|
|
|
138
193
|
z:
|
|
139
194
|
codec: float32
|
|
140
195
|
|
|
196
|
+
Bit-Length Byte Arrays
|
|
197
|
+
----------------------
|
|
198
|
+
|
|
199
|
+
Use ``variable_len_bit_array`` for a ``uint8`` payload whose last byte can
|
|
200
|
+
contain between zero and seven unused bits. The configuration key is the byte
|
|
201
|
+
array field. ``bit_length_field`` names the related value that is derived on
|
|
202
|
+
decode, so it is omitted as a separate field configuration.
|
|
203
|
+
|
|
204
|
+
.. code-block:: yaml
|
|
205
|
+
|
|
206
|
+
payload:
|
|
207
|
+
codec: variable_len_bit_array
|
|
208
|
+
max_length: 65535
|
|
209
|
+
bit_length_field: payload_size_in_bits
|
|
210
|
+
|
|
211
|
+
For a two-byte payload with ``payload_size_in_bits: 13``, the codec writes the
|
|
212
|
+
normal length prefix and two bytes followed by a three-bit value of ``3``. On
|
|
213
|
+
decode it returns the byte array and derives ``payload_size_in_bits`` as
|
|
214
|
+
``13``. The supplied bit length must describe the final payload byte: it must
|
|
215
|
+
be between ``8 * len(payload) - 7`` and ``8 * len(payload)``, or zero for an
|
|
216
|
+
empty payload.
|
|
217
|
+
|
|
218
|
+
Constants
|
|
219
|
+
---------
|
|
220
|
+
|
|
221
|
+
Use ``constant`` for a configured value that consumes no bits. The value is
|
|
222
|
+
returned during decoding and replaces any source value during encoding.
|
|
223
|
+
|
|
224
|
+
.. code-block:: yaml
|
|
225
|
+
|
|
226
|
+
source:
|
|
227
|
+
codec: constant
|
|
228
|
+
value: 0
|
|
229
|
+
|
|
230
|
+
This is especially useful in explicit nested ROS message configurations. A
|
|
231
|
+
``msg`` codec whose configured children are all constants can be encoded
|
|
232
|
+
without an element object; a variable-length array of those messages can use
|
|
233
|
+
``None`` placeholders solely to communicate its length.
|
|
234
|
+
|
|
141
235
|
Top-Level Options
|
|
142
236
|
-----------------
|
|
143
237
|
|
|
@@ -20,6 +20,14 @@ ltcodecs.bytes\_codec module
|
|
|
20
20
|
:undoc-members:
|
|
21
21
|
:show-inheritance:
|
|
22
22
|
|
|
23
|
+
ltcodecs.constant\_codec module
|
|
24
|
+
-------------------------------
|
|
25
|
+
|
|
26
|
+
.. automodule:: ltcodecs.constant_codec
|
|
27
|
+
:members:
|
|
28
|
+
:undoc-members:
|
|
29
|
+
:show-inheritance:
|
|
30
|
+
|
|
23
31
|
ltcodecs.ccl\_latlon\_bcd\_codec module
|
|
24
32
|
---------------------------------------
|
|
25
33
|
|
|
@@ -189,6 +197,14 @@ ltcodecs.variable\_len\_array\_codec module
|
|
|
189
197
|
:undoc-members:
|
|
190
198
|
:show-inheritance:
|
|
191
199
|
|
|
200
|
+
ltcodecs.variable\_len\_bit\_array\_codec module
|
|
201
|
+
---------------------------------------------------
|
|
202
|
+
|
|
203
|
+
.. automodule:: ltcodecs.variable_len_bit_array_codec
|
|
204
|
+
:members:
|
|
205
|
+
:undoc-members:
|
|
206
|
+
:show-inheritance:
|
|
207
|
+
|
|
192
208
|
ltcodecs.varint\_codec module
|
|
193
209
|
-----------------------------
|
|
194
210
|
|
|
@@ -114,6 +114,18 @@ is treated like this explicit configuration:
|
|
|
114
114
|
The default maximum length for inferred variable-length arrays is currently
|
|
115
115
|
``10``. Use an explicit config when that limit should be different.
|
|
116
116
|
|
|
117
|
+
For a ROS ``uint8`` payload whose exact bit length is also needed, use the
|
|
118
|
+
explicit ``variable_len_bit_array`` codec. The payload field remains a normal
|
|
119
|
+
ROS array; the named bit-length field is derived during decoding rather than
|
|
120
|
+
encoded as a separate field.
|
|
121
|
+
|
|
122
|
+
.. code-block:: yaml
|
|
123
|
+
|
|
124
|
+
payload:
|
|
125
|
+
codec: variable_len_bit_array
|
|
126
|
+
max_length: 65535
|
|
127
|
+
bit_length_field: payload_size_in_bits
|
|
128
|
+
|
|
117
129
|
Nested Message Fields
|
|
118
130
|
---------------------
|
|
119
131
|
|
|
@@ -137,6 +149,32 @@ encoded or when individual nested fields need custom codecs.
|
|
|
137
149
|
Without a ``fields`` mapping, ``RosMsgFieldCodec`` infers the nested message's
|
|
138
150
|
fields from the ROS message definition.
|
|
139
151
|
|
|
152
|
+
Constant Nested Fields
|
|
153
|
+
----------------------
|
|
154
|
+
|
|
155
|
+
Use ``constant`` to reconstruct nested fields without transmitting them. A
|
|
156
|
+
constant field consumes zero bits and returns its configured YAML value on
|
|
157
|
+
decode.
|
|
158
|
+
|
|
159
|
+
.. code-block:: yaml
|
|
160
|
+
|
|
161
|
+
link:
|
|
162
|
+
codec: msg
|
|
163
|
+
ros_type: ros_acomms_net_msgs/Link
|
|
164
|
+
fields:
|
|
165
|
+
source:
|
|
166
|
+
codec: constant
|
|
167
|
+
value: 0
|
|
168
|
+
dest:
|
|
169
|
+
codec: constant
|
|
170
|
+
value: 0
|
|
171
|
+
|
|
172
|
+
When every configured child of a nested ``msg`` is constant, the message codec
|
|
173
|
+
does not require a source message while encoding. This permits a
|
|
174
|
+
``variable_len_array`` of such messages to use ``None`` values as input solely
|
|
175
|
+
to represent the desired array length; decoding constructs a fresh ROS message
|
|
176
|
+
for every element.
|
|
177
|
+
|
|
140
178
|
Metadata
|
|
141
179
|
--------
|
|
142
180
|
|
|
@@ -26,7 +26,7 @@ homepage = "https://git.whoi.edu/acomms/ltcodec/"
|
|
|
26
26
|
documentation = "https://acomms.pages.whoi.edu/ltcodec/"
|
|
27
27
|
|
|
28
28
|
[project.scripts]
|
|
29
|
-
|
|
29
|
+
inspect_codec = "ltcodecs.inspect_cli:main"
|
|
30
30
|
|
|
31
31
|
[project.optional-dependencies]
|
|
32
32
|
# No optional dependencies
|
|
@@ -36,4 +36,4 @@ requires = ["setuptools>=64", "wheel", "setuptools_scm>=8"]
|
|
|
36
36
|
build-backend = "setuptools.build_meta"
|
|
37
37
|
|
|
38
38
|
[tool.setuptools_scm]
|
|
39
|
-
write_to = "_version.py"
|
|
39
|
+
write_to = "_version.py"
|
|
@@ -12,18 +12,21 @@ from .string_enum_codec import StringEnumCodec
|
|
|
12
12
|
from .bytes_codec import BytesCodec
|
|
13
13
|
from .metadata_field_codec import MetadataFieldCodec
|
|
14
14
|
from .variable_len_array_codec import VariableLenArrayCodec
|
|
15
|
+
from .variable_len_bit_array_codec import VariableLenBitArrayCodec
|
|
15
16
|
from .fixed_len_array_codec import FixedLenArrayCodec
|
|
16
17
|
from .dict_field_codec import DictFieldCodec
|
|
17
18
|
from .ros_msg_field_codec import RosMsgFieldCodec
|
|
18
19
|
from .ccl_latlon_codec import CclLatLonCodec
|
|
19
20
|
from .ccl_latlon_bcd_codec import CclLatLonBcdCodec
|
|
20
21
|
from .padding_codec import PaddingCodec
|
|
22
|
+
from .constant_codec import ConstantCodec
|
|
21
23
|
from .rostime_codec import RosTimeCodec
|
|
22
24
|
from .dict_codec import DictCodec
|
|
23
25
|
from .ros_message_codec import RosMessageCodec
|
|
24
26
|
from .lzma_codec import LzmaCodec
|
|
25
27
|
from .optional_field_codec import OptionalFieldCodec
|
|
26
28
|
from .exceptions import EncodingFailed
|
|
29
|
+
from .inspection import CodecInspector, FieldReport, InspectionReport, ValidationIssue
|
|
27
30
|
|
|
28
31
|
field_codec_classes = {
|
|
29
32
|
"integer": VarintCodec,
|
|
@@ -53,11 +56,13 @@ field_codec_classes = {
|
|
|
53
56
|
"msg": RosMsgFieldCodec,
|
|
54
57
|
"ros_msg": RosMsgFieldCodec,
|
|
55
58
|
"variable_len_array": VariableLenArrayCodec,
|
|
59
|
+
"variable_len_bit_array": VariableLenBitArrayCodec,
|
|
56
60
|
"fixed_len_array": FixedLenArrayCodec,
|
|
57
61
|
"ccl_latlon": CclLatLonCodec,
|
|
58
62
|
"ccl_latlon_bcd": CclLatLonBcdCodec,
|
|
59
63
|
"pad": PaddingCodec,
|
|
60
64
|
"padding": PaddingCodec,
|
|
65
|
+
"constant": ConstantCodec,
|
|
61
66
|
"time": RosTimeCodec,
|
|
62
67
|
"rostime": RosTimeCodec,
|
|
63
68
|
"string_enum": StringEnumCodec,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared helpers for codecs that encode variable-length values.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from bitstring import BitArray, ConstBitStream
|
|
8
|
+
|
|
9
|
+
from .exponentialgolomb_integer_codec import ExponentialGolombIntegerCodec
|
|
10
|
+
from .varint_codec import VarintCodec
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
VALID_LENGTH_MODES = ("varint", "egint", "fill_outer")
|
|
14
|
+
PREFIXED_LENGTH_MODES = ("varint", "egint")
|
|
15
|
+
FILL_OUTER_LENGTH_MODE = "fill_outer"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_length_mode(length_mode: str, codec_name: str) -> str:
|
|
19
|
+
if length_mode not in VALID_LENGTH_MODES:
|
|
20
|
+
raise ValueError(
|
|
21
|
+
f"{codec_name} has invalid length_mode parameter: {length_mode}, "
|
|
22
|
+
f"should be one of {', '.join(VALID_LENGTH_MODES)}."
|
|
23
|
+
)
|
|
24
|
+
return length_mode
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def make_length_codec(length_mode: str, min_value: int, max_value: int):
|
|
28
|
+
if length_mode == "varint":
|
|
29
|
+
return VarintCodec(min_value=min_value, max_value=max_value)
|
|
30
|
+
if length_mode == "egint":
|
|
31
|
+
return ExponentialGolombIntegerCodec(min_value=min_value, max_value=max_value)
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def encode_length(length_mode: str, length_codec, length: int) -> BitArray:
|
|
36
|
+
if length_mode == FILL_OUTER_LENGTH_MODE:
|
|
37
|
+
return BitArray()
|
|
38
|
+
length_bits, _ = length_codec.encode(length)
|
|
39
|
+
return BitArray(length_bits)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def decode_length(length_mode: str, length_codec, bits_to_decode: ConstBitStream) -> int:
|
|
43
|
+
if length_mode == FILL_OUTER_LENGTH_MODE:
|
|
44
|
+
raise ValueError("fill_outer length mode does not encode a length prefix")
|
|
45
|
+
return length_codec.decode(bits_to_decode)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def remaining_bits(bits_to_decode: ConstBitStream) -> int:
|
|
49
|
+
return len(bits_to_decode) - bits_to_decode.pos
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def prefix_min_length_bits(length_mode: str, length_codec) -> int:
|
|
53
|
+
if length_mode == FILL_OUTER_LENGTH_MODE:
|
|
54
|
+
return 0
|
|
55
|
+
return length_codec.min_length_bits
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def prefix_max_length_bits(length_mode: str, length_codec) -> int:
|
|
59
|
+
if length_mode == FILL_OUTER_LENGTH_MODE:
|
|
60
|
+
return 0
|
|
61
|
+
return length_codec.max_length_bits
|
|
@@ -8,7 +8,16 @@ This module contains the BytesCodec class, which is used to encode and decode by
|
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
from bitstring import BitArray, ConstBitStream, Bits
|
|
10
10
|
from .field_codec import FieldCodec
|
|
11
|
-
from .
|
|
11
|
+
from ._length_modes import (
|
|
12
|
+
FILL_OUTER_LENGTH_MODE,
|
|
13
|
+
decode_length,
|
|
14
|
+
encode_length,
|
|
15
|
+
make_length_codec,
|
|
16
|
+
prefix_max_length_bits,
|
|
17
|
+
prefix_min_length_bits,
|
|
18
|
+
remaining_bits,
|
|
19
|
+
validate_length_mode,
|
|
20
|
+
)
|
|
12
21
|
from .exceptions import EncodingFailed
|
|
13
22
|
|
|
14
23
|
|
|
@@ -17,9 +26,10 @@ class BytesCodec(FieldCodec):
|
|
|
17
26
|
codec for bytes
|
|
18
27
|
"""
|
|
19
28
|
|
|
20
|
-
def __init__(self, max_length: int, fail_on_overflow=False, **kwargs) -> None:
|
|
29
|
+
def __init__(self, max_length: int, fail_on_overflow=False, length_mode: str = "varint", **kwargs) -> None:
|
|
21
30
|
self.max_length = max_length
|
|
22
|
-
self.
|
|
31
|
+
self.length_mode = validate_length_mode(length_mode, "BytesCodec")
|
|
32
|
+
self.length_codec = make_length_codec(self.length_mode, min_value=0, max_value=self.max_length)
|
|
23
33
|
self.fail_on_overflow = fail_on_overflow
|
|
24
34
|
|
|
25
35
|
def encode(self, value: bytes) -> tuple[Bits, bytes]:
|
|
@@ -33,9 +43,8 @@ class BytesCodec(FieldCodec):
|
|
|
33
43
|
f"BytesCodec: value with length {len(value)} is too long to encode (codec max_length={self.max_length}"
|
|
34
44
|
)
|
|
35
45
|
value = value[0 : self.max_length]
|
|
36
|
-
length_bits, _ = self.length_codec.encode(len(value))
|
|
37
46
|
value_bits = BitArray(bytes=value)
|
|
38
|
-
value_bits.prepend(
|
|
47
|
+
value_bits.prepend(encode_length(self.length_mode, self.length_codec, len(value)))
|
|
39
48
|
return value_bits, value
|
|
40
49
|
|
|
41
50
|
def decode(self, bits_to_decode: ConstBitStream) -> bytes:
|
|
@@ -44,14 +53,24 @@ class BytesCodec(FieldCodec):
|
|
|
44
53
|
|
|
45
54
|
:param bits_to_decode: the bits to decode
|
|
46
55
|
"""
|
|
47
|
-
|
|
56
|
+
if self.length_mode == FILL_OUTER_LENGTH_MODE:
|
|
57
|
+
num_bits = remaining_bits(bits_to_decode)
|
|
58
|
+
if num_bits % 8:
|
|
59
|
+
raise ValueError(
|
|
60
|
+
"BytesCodec length_mode='fill_outer' received bits that do not align to bytes."
|
|
61
|
+
)
|
|
62
|
+
num_bytes = num_bits // 8
|
|
63
|
+
if num_bytes > self.max_length:
|
|
64
|
+
raise ValueError(f"BytesCodec decoded {num_bytes} bytes, exceeding max_length={self.max_length}.")
|
|
65
|
+
else:
|
|
66
|
+
num_bytes = decode_length(self.length_mode, self.length_codec, bits_to_decode)
|
|
48
67
|
value = bits_to_decode.read("bytes:" + str(num_bytes))
|
|
49
68
|
return value
|
|
50
69
|
|
|
51
70
|
@property
|
|
52
71
|
def min_length_bits(self) -> int:
|
|
53
|
-
return self.length_codec
|
|
72
|
+
return prefix_min_length_bits(self.length_mode, self.length_codec)
|
|
54
73
|
|
|
55
74
|
@property
|
|
56
75
|
def max_length_bits(self) -> int:
|
|
57
|
-
return self.length_codec
|
|
76
|
+
return prefix_max_length_bits(self.length_mode, self.length_codec) + (8 * self.max_length)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"""Zero-bit codec for configured constant values."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from copy import deepcopy
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from bitstring import BitArray, ConstBitStream
|
|
9
|
+
|
|
10
|
+
from .field_codec import FieldCodec
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ConstantCodec(FieldCodec):
|
|
14
|
+
"""Return a configured value without encoding it on the wire.
|
|
15
|
+
|
|
16
|
+
The source message need not contain a field using this codec. Each encode
|
|
17
|
+
and decode operation returns a deep copy so mutable configured values are
|
|
18
|
+
never shared between decoded messages.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
value: YAML-native value returned during decoding.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
requires_encode_value = False
|
|
25
|
+
|
|
26
|
+
def __init__(self, value: Any, **kwargs: Any) -> None:
|
|
27
|
+
self.value = value
|
|
28
|
+
|
|
29
|
+
def encode(self, value: Any = None) -> tuple[BitArray, Any]:
|
|
30
|
+
return BitArray(), deepcopy(self.value)
|
|
31
|
+
|
|
32
|
+
def decode(self, bits_to_decode: ConstBitStream) -> Any:
|
|
33
|
+
return deepcopy(self.value)
|
|
34
|
+
|
|
35
|
+
@property
|
|
36
|
+
def min_length_bits(self) -> int:
|
|
37
|
+
return 0
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def max_length_bits(self) -> int:
|
|
41
|
+
return 0
|