sltcodec 2.4.0__tar.gz → 2.4.1__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.
- {sltcodec-2.4.0 → sltcodec-2.4.1}/PKG-INFO +1 -1
- {sltcodec-2.4.0 → sltcodec-2.4.1}/pyproject.toml +1 -1
- {sltcodec-2.4.0 → sltcodec-2.4.1}/src/sltcodec/codec.py +25 -3
- {sltcodec-2.4.0 → sltcodec-2.4.1}/tests/test_codec.py +32 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.github/workflows/publish_to_pypi.yml +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.github/workflows/publish_to_testpypi.yml +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.gitignore +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.python-version +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.vscode/launch.json +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/.vscode/settings.json +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/LICENSE +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/README.md +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/README_pypi.md +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/bump_major.ps1 +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/bump_minor.ps1 +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/bump_patch.ps1 +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/src/sltcodec/__init__.py +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/src/sltcodec/types.py +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/tests/test_persistence.py +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/tests/test_types.py +0 -0
- {sltcodec-2.4.0 → sltcodec-2.4.1}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sltcodec
|
|
3
|
-
Version: 2.4.
|
|
3
|
+
Version: 2.4.1
|
|
4
4
|
Summary: Decode and encode bytearrays according to struct layout definitions using sltcore.
|
|
5
5
|
Project-URL: Homepage, https://github.com/fangface-hub/StructLayoutToolkitCodec
|
|
6
6
|
Project-URL: Documentation, https://readthedocs.org
|
|
@@ -134,8 +134,20 @@ def load_enum_def_dict(path: str | Path) -> dict[str, EnumDef]:
|
|
|
134
134
|
def _resolve_info_size(value: InfoSize | str, env: dict[str, Any]) -> InfoSize:
|
|
135
135
|
"""Resolve an InfoSize value that can be static or expression-based."""
|
|
136
136
|
if isinstance(value, str):
|
|
137
|
-
|
|
138
|
-
|
|
137
|
+
eval_env = dict(env)
|
|
138
|
+
|
|
139
|
+
def replace_info_size(match: re.Match[str]) -> str:
|
|
140
|
+
name = f"_info_size_{len(eval_env)}"
|
|
141
|
+
eval_env[name] = InfoSize(int(match.group(1)), int(match.group(2)))
|
|
142
|
+
return name
|
|
143
|
+
|
|
144
|
+
expression = re.sub(
|
|
145
|
+
r"\bInfoSize\s*\(\s*(-?\d+)\s*,\s*(-?\d+)\s*\)",
|
|
146
|
+
replace_info_size,
|
|
147
|
+
value,
|
|
148
|
+
)
|
|
149
|
+
stleval = SltEval(eval_env)
|
|
150
|
+
resolved_byte = stleval.eval(expression)
|
|
139
151
|
if isinstance(resolved_byte, InfoSize):
|
|
140
152
|
return resolved_byte
|
|
141
153
|
return InfoSize(resolved_byte, 0)
|
|
@@ -164,6 +176,13 @@ def _is_padding_field_def(field_def: FieldDef) -> bool:
|
|
|
164
176
|
and field_def.type in ["bytes", "bytearray"])
|
|
165
177
|
|
|
166
178
|
|
|
179
|
+
def _struct_def_has_dynamic_extent(struct_def: StructDef) -> bool:
|
|
180
|
+
"""Check whether a nested structure can consume data beyond its fields."""
|
|
181
|
+
return any(field_def.repeat == "end" or isinstance(field_def.offset, str)
|
|
182
|
+
or isinstance(field_def.size, str)
|
|
183
|
+
for field_def in struct_def.fields)
|
|
184
|
+
|
|
185
|
+
|
|
167
186
|
def _validate_padding_alignment_bits(padding_alignment_bits: int) -> None:
|
|
168
187
|
"""Validate padding alignment as a positive power-of-two bit size."""
|
|
169
188
|
if (isinstance(padding_alignment_bits, bool)
|
|
@@ -415,7 +434,10 @@ def decode_field(
|
|
|
415
434
|
nested_layout = _layout_for_struct_def(resolved_type,
|
|
416
435
|
type_dict,
|
|
417
436
|
name=field_def.name)
|
|
418
|
-
|
|
437
|
+
nested_data = bytearray(info.to_bytes)
|
|
438
|
+
if _struct_def_has_dynamic_extent(resolved_type) and offset.bit == 0:
|
|
439
|
+
nested_data = bytearray(data[offset.byte:])
|
|
440
|
+
nested_value = decode(nested_layout, nested_data,
|
|
419
441
|
padding_alignment_bits)
|
|
420
442
|
actual_size = nested_value.size
|
|
421
443
|
else:
|
|
@@ -232,6 +232,38 @@ def test_repeat_end_stops_when_offset_is_out_of_range():
|
|
|
232
232
|
assert [instance.value for instance in decoded.field_instances] == [None]
|
|
233
233
|
|
|
234
234
|
|
|
235
|
+
def test_info_size_expression_resolves_constructor():
|
|
236
|
+
"""Test that serialized InfoSize constructors work in expressions."""
|
|
237
|
+
resolved = _resolve_info_size("InfoSize(1, 0) * count", {"count": 3})
|
|
238
|
+
|
|
239
|
+
assert resolved == InfoSize(3, 0)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def test_repeat_end_uses_dynamic_nested_struct_size():
|
|
243
|
+
"""Test repeat='end' advances by a variable-size nested structure."""
|
|
244
|
+
length_field = FieldDef(name="length",
|
|
245
|
+
offset=InfoSize(0, 0),
|
|
246
|
+
size=InfoSize(1, 0),
|
|
247
|
+
type="unsigned int")
|
|
248
|
+
payload_field = FieldDef(name="payload",
|
|
249
|
+
offset=InfoSize(1, 0),
|
|
250
|
+
size="InfoSize(1, 0) * length",
|
|
251
|
+
type="bytearray")
|
|
252
|
+
record_field = FieldDef(
|
|
253
|
+
name="record",
|
|
254
|
+
offset=InfoSize(0, 0),
|
|
255
|
+
size=InfoSize(1, 0),
|
|
256
|
+
type=StructDef(fields=[length_field, payload_field]),
|
|
257
|
+
repeat="end")
|
|
258
|
+
|
|
259
|
+
decoded = decode(layout_for([record_field]), bytearray(b"\x02ab\x01c"))
|
|
260
|
+
|
|
261
|
+
assert [instance.field_def.name for instance in decoded.field_instances
|
|
262
|
+
] == ["record[0]", "record[1]"]
|
|
263
|
+
assert [instance.value.size for instance in decoded.field_instances
|
|
264
|
+
] == [InfoSize(3, 0), InfoSize(2, 0)]
|
|
265
|
+
|
|
266
|
+
|
|
235
267
|
def test_decode_out_of_range_sets_current_and_tail_values_to_none():
|
|
236
268
|
"""Test out-of-range decode fills current/tail field values with None."""
|
|
237
269
|
fields = [
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|