sltcodec 2.2.0__tar.gz → 2.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: sltcodec
3
- Version: 2.2.0
3
+ Version: 2.3.0
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
@@ -77,7 +77,7 @@ The public `PRIMITIVE_TYPES` set contains:
77
77
  {"bool", "signed int", "int", "unsigned int", "float", "bytearray", "bytes"}
78
78
  ```
79
79
 
80
- `FieldDef.type` may also be a nested `StructDef` or an expression string. Expressions can use values from previously processed fields, allowing dynamic types and sizes. Repeated fields, byte swapping, padding, ranges, and enum metadata are supported.
80
+ `FieldDef.type` may also be a nested `StructDef` or an expression string. Expressions can use values from previously processed fields, allowing dynamic types, sizes, and repeat counts. Repeated fields, byte swapping, padding, ranges, and enum metadata are supported.
81
81
 
82
82
  ## Enums And Named Structures
83
83
 
@@ -180,7 +180,7 @@ internal implementation details and are omitted from the public method tables.
180
180
  | `size` | `InfoSize \| str` | Static size or expression. |
181
181
  | `type` | `str \| StructDef` | Primitive, named, or nested field type. |
182
182
  | `scale` | `float` | Numeric scale applied to the field. |
183
- | `repeat` | `int \| None` | Number of repeated field values. |
183
+ | `repeat` | `int \| str \| None` | Number of repeated field values, or an expression that evaluates to the count. |
184
184
  | `description` | `str \| None` | Optional field description. |
185
185
  | `range_expression` | `str \| None` | Optional value-range expression. |
186
186
  | `enum_def_name` | `str \| None` | Name of the associated enum definition. |
@@ -60,7 +60,7 @@ The public `PRIMITIVE_TYPES` set contains:
60
60
  {"bool", "signed int", "int", "unsigned int", "float", "bytearray", "bytes"}
61
61
  ```
62
62
 
63
- `FieldDef.type` may also be a nested `StructDef` or an expression string. Expressions can use values from previously processed fields, allowing dynamic types and sizes. Repeated fields, byte swapping, padding, ranges, and enum metadata are supported.
63
+ `FieldDef.type` may also be a nested `StructDef` or an expression string. Expressions can use values from previously processed fields, allowing dynamic types, sizes, and repeat counts. Repeated fields, byte swapping, padding, ranges, and enum metadata are supported.
64
64
 
65
65
  ## Enums And Named Structures
66
66
 
@@ -163,7 +163,7 @@ internal implementation details and are omitted from the public method tables.
163
163
  | `size` | `InfoSize \| str` | Static size or expression. |
164
164
  | `type` | `str \| StructDef` | Primitive, named, or nested field type. |
165
165
  | `scale` | `float` | Numeric scale applied to the field. |
166
- | `repeat` | `int \| None` | Number of repeated field values. |
166
+ | `repeat` | `int \| str \| None` | Number of repeated field values, or an expression that evaluates to the count. |
167
167
  | `description` | `str \| None` | Optional field description. |
168
168
  | `range_expression` | `str \| None` | Optional value-range expression. |
169
169
  | `enum_def_name` | `str \| None` | Name of the associated enum definition. |
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sltcodec"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = "Decode and encode bytearrays according to struct layout definitions using sltcore."
5
5
  readme = "README_pypi.md"
6
6
  requires-python = ">=3.12"
@@ -149,6 +149,13 @@ def _resolve_byte_swap(field_def: FieldDef, env: dict[str, Any]) -> bool:
149
149
  return field_def.byte_swap
150
150
 
151
151
 
152
+ def _resolve_repeat(value: int | str | None, env: dict[str, Any]) -> int | None:
153
+ """Resolve a static or expression-based repeat count."""
154
+ if isinstance(value, str):
155
+ return int(SltEval(env).eval(value))
156
+ return value
157
+
158
+
152
159
  def _is_padding_field_def(field_def: FieldDef) -> bool:
153
160
  """Check whether a field definition represents padding."""
154
161
  return (field_def.name.startswith("padding[")
@@ -328,17 +335,18 @@ def encode(
328
335
 
329
336
  for field_value in struct_instance.field_instances:
330
337
  field_def = field_value.field_def
338
+ repeat = _resolve_repeat(field_def.repeat, env)
331
339
  if _is_padding_field_def(field_def):
332
340
  has_padding = True
333
341
  value = field_value.value
334
342
  if _is_padding_field_def(field_def):
335
343
  padding_size = _resolve_info_size(field_def.size, env)
336
344
  value = bytearray(padding_size.bytes)
337
- if field_def.repeat is not None and field_def.repeat > 1:
345
+ if repeat is not None and repeat > 1:
338
346
  current_offset = _resolve_info_size(field_def.offset, env)
339
347
  values = list(value)
340
348
 
341
- for i in range(field_def.repeat):
349
+ for i in range(repeat):
342
350
  field_def_repeat = _repeated_field_def(field_def, i,
343
351
  current_offset,
344
352
  field_def.size)
@@ -530,8 +538,9 @@ def decode(
530
538
  current_position = target_offset
531
539
 
532
540
  for field_def in _as_field_defs(struct_def_obj):
541
+ repeat = _resolve_repeat(field_def.repeat, env)
533
542
  # Handle non-repeated fields
534
- if field_def.repeat is None or field_def.repeat <= 1:
543
+ if repeat is None or repeat <= 1:
535
544
  resolved_offset = _resolve_info_size(field_def.offset, env)
536
545
  append_padding_until(resolved_offset)
537
546
  field_instance = decode_field(field_def, data, env, type_dict,
@@ -545,7 +554,7 @@ def decode(
545
554
  # Handle repeated fields
546
555
  current_offset = _resolve_info_size(field_def.offset, env)
547
556
  append_padding_until(current_offset)
548
- for i in range(field_def.repeat):
557
+ for i in range(repeat):
549
558
  field_def_repeat = _repeated_field_def(field_def, i, current_offset,
550
559
  field_def.size)
551
560
  field_instance = decode_field(field_def_repeat, data, env,
@@ -98,7 +98,7 @@ class FieldDef:
98
98
  metadata={"desc": "The type of the field"})
99
99
  scale: float = field(default=1.0,
100
100
  metadata={"desc": "The scale of the field"})
101
- repeat: int | None = field(
101
+ repeat: int | str | None = field(
102
102
  default=None, metadata={"desc": "The repeat count of the field"})
103
103
  description: str | None = field(
104
104
  default=None, metadata={"desc": "The description of the field"})
@@ -231,7 +231,7 @@ class FieldDef:
231
231
  self.name,
232
232
  self._sortable_type(self.type),
233
233
  self.scale,
234
- -1 if self.repeat is None else self.repeat,
234
+ self._sortable_repeat(self.repeat),
235
235
  "" if self.description is None else self.description,
236
236
  "" if self.range_expression is None else self.range_expression,
237
237
  "" if self.enum_def_name is None else self.enum_def_name,
@@ -245,6 +245,15 @@ class FieldDef:
245
245
  return (0, value.byte, value.bit)
246
246
  return (1, value)
247
247
 
248
+ @staticmethod
249
+ def _sortable_repeat(value: int | str | None) -> tuple[int, int | str]:
250
+ """Build a comparable key for static or expression repeat counts."""
251
+ if value is None:
252
+ return 0, -1
253
+ if isinstance(value, int):
254
+ return 1, value
255
+ return 2, value
256
+
248
257
  @staticmethod
249
258
  def _sortable_type(value: str | "StructDef") -> tuple[Any, ...]:
250
259
  """Build a comparable key for primitive or nested field types."""
@@ -166,6 +166,38 @@ def test_encode_layout_handles_repeat():
166
166
  ]
167
167
 
168
168
 
169
+ def test_expression_repeat_uses_previous_field_value():
170
+ """Test that repeat expressions are evaluated against the field env."""
171
+ seed_field = FieldDef(name="count",
172
+ offset=InfoSize(0, 0),
173
+ size=InfoSize(1, 0),
174
+ type="unsigned int")
175
+ repeated_field = FieldDef(name="value",
176
+ offset=InfoSize(1, 0),
177
+ size=InfoSize(1, 0),
178
+ type="unsigned int",
179
+ repeat="count + 1")
180
+ struct_def = StructDef(fields=[seed_field, repeated_field])
181
+
182
+ encoded = encode(
183
+ layout_for(struct_def),
184
+ StructInstance(
185
+ struct_def=struct_def,
186
+ field_instances=[
187
+ FieldInstance(seed_field, 2),
188
+ FieldInstance(repeated_field, [5, 6, 7]),
189
+ ],
190
+ ),
191
+ bytearray(),
192
+ )
193
+
194
+ assert encoded == bytearray(b"\x02\x05\x06\x07")
195
+ decoded = decode(layout_for(struct_def), encoded)
196
+
197
+ assert [instance.value
198
+ for instance in decoded.field_instances] == [2, 5, 6, 7]
199
+
200
+
169
201
  def test_encode_recurses_for_nested_field_types():
170
202
  """Test that encoding a field with a nested field type works correctly."""
171
203
  child_field_defs = [
@@ -39,7 +39,7 @@ def test_field_def_to_json_from_json_round_trip():
39
39
  size=InfoSize(1, 0),
40
40
  type="unsigned int",
41
41
  scale=2.0,
42
- repeat=3,
42
+ repeat="count + 1",
43
43
  description="A repeated value",
44
44
  range_expression="0 <= value <= 255",
45
45
  enum_def_name=enum_def.name)
@@ -48,6 +48,7 @@ def test_field_def_to_json_from_json_round_trip():
48
48
  restored = FieldDef.from_json(payload)
49
49
 
50
50
  assert restored == field_def
51
+ assert restored.repeat == "count + 1"
51
52
  assert restored.range_expression == "0 <= value <= 255"
52
53
  assert restored.enum_def_name == enum_def.name
53
54
  assert restored.to_dict()["enum_def_name"] == "ValueKind"
@@ -74,7 +74,7 @@ wheels = [
74
74
 
75
75
  [[package]]
76
76
  name = "sltcodec"
77
- version = "2.1.1"
77
+ version = "2.2.0"
78
78
  source = { editable = "." }
79
79
  dependencies = [
80
80
  { name = "sltcalc" },
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