sltcodec 1.2.0__tar.gz → 1.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.
- {sltcodec-1.2.0 → sltcodec-1.3.0}/PKG-INFO +60 -39
- {sltcodec-1.2.0 → sltcodec-1.3.0}/README.md +59 -38
- {sltcodec-1.2.0 → sltcodec-1.3.0}/pyproject.toml +1 -1
- sltcodec-1.3.0/src/sltcodec/__init__.py +21 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/src/sltcodec/codec.py +104 -75
- {sltcodec-1.2.0 → sltcodec-1.3.0}/src/sltcodec/types.py +101 -17
- {sltcodec-1.2.0 → sltcodec-1.3.0}/tests/test_codec.py +21 -17
- {sltcodec-1.2.0 → sltcodec-1.3.0}/uv.lock +1 -1
- sltcodec-1.2.0/src/sltcodec/__init__.py +0 -20
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.github/workflows/publish_to_pypi.yml +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.github/workflows/publish_to_testpypi.yml +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.gitignore +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.python-version +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.vscode/launch.json +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/.vscode/settings.json +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/LICENSE +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/bump_major.ps1 +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/bump_minor.ps1 +0 -0
- {sltcodec-1.2.0 → sltcodec-1.3.0}/bump_patch.ps1 +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: sltcodec
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.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
|
|
@@ -145,7 +145,7 @@ Output:
|
|
|
145
145
|
```python
|
|
146
146
|
bytearray(b"\x03\x04")
|
|
147
147
|
StructInstance(struct_def=StructDef(...), field_instances=[...])
|
|
148
|
-
[FieldInstance(field_def=FieldDef(name='pair', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=2, bit=0), type=
|
|
148
|
+
[FieldInstance(field_def=FieldDef(name='pair', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=2, bit=0), type=StructDef(...), scale=1.0, repeat=None),
|
|
149
149
|
value=StructInstance(struct_def=StructDef(...), field_instances=[...]))]
|
|
150
150
|
```
|
|
151
151
|
|
|
@@ -199,66 +199,87 @@ print(encoded_int, decoded_int)
|
|
|
199
199
|
print(encoded_float, decoded_float)
|
|
200
200
|
```
|
|
201
201
|
|
|
202
|
-
## Saving And Loading
|
|
202
|
+
## Saving And Loading Type Dictionaries
|
|
203
203
|
|
|
204
|
-
You can persist reusable structure definitions
|
|
205
|
-
`
|
|
204
|
+
You can persist reusable structure and enum definitions together via
|
|
205
|
+
`TypeDict` with `save_type_dict` / `load_type_dict`.
|
|
206
|
+
|
|
207
|
+
`InfoSize` values in `offset` and `size` are saved as typed dictionaries in
|
|
208
|
+
JSON. Expression-based offsets and sizes remain strings and are resolved when
|
|
209
|
+
the definition is used.
|
|
206
210
|
|
|
207
211
|
```python
|
|
208
212
|
from pathlib import Path
|
|
209
213
|
|
|
210
214
|
from sltcore import InfoSize
|
|
211
|
-
from sltcodec import FieldDef, StructDef,
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
from sltcodec import EnumDef, FieldDef, StructDef, TypeDict, load_type_dict, save_type_dict
|
|
216
|
+
|
|
217
|
+
type_dict = TypeDict(
|
|
218
|
+
struct_dict={
|
|
219
|
+
"Header": StructDef(
|
|
220
|
+
name="Header",
|
|
221
|
+
description="Simple header",
|
|
222
|
+
fields=[
|
|
223
|
+
FieldDef(name="kind", offset=InfoSize(0, 0), size=InfoSize(1, 0), type="unsigned int"),
|
|
224
|
+
FieldDef(name="flags", offset=InfoSize(1, 0), size=InfoSize(1, 0), type="unsigned int"),
|
|
225
|
+
],
|
|
226
|
+
)
|
|
227
|
+
},
|
|
228
|
+
enum_dict={
|
|
229
|
+
"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1}),
|
|
230
|
+
}
|
|
231
|
+
)
|
|
223
232
|
|
|
224
233
|
path = Path("struct_defs.json")
|
|
225
|
-
|
|
226
|
-
loaded =
|
|
234
|
+
save_type_dict(path, type_dict)
|
|
235
|
+
loaded = load_type_dict(path)
|
|
227
236
|
|
|
228
|
-
print(loaded["Header"].name)
|
|
229
|
-
print(loaded["Header"].description)
|
|
237
|
+
print(loaded.struct_dict["Header"].name)
|
|
238
|
+
print(loaded.struct_dict["Header"].description)
|
|
239
|
+
print(loaded.enum_dict["Status"].values["NG"])
|
|
230
240
|
```
|
|
231
241
|
|
|
232
242
|
Example output:
|
|
233
243
|
|
|
234
244
|
```python
|
|
235
|
-
|
|
236
|
-
|
|
245
|
+
Header
|
|
246
|
+
Simple header
|
|
247
|
+
1
|
|
237
248
|
```
|
|
238
249
|
|
|
239
250
|
## Enum Definitions
|
|
240
251
|
|
|
241
252
|
`FieldDef.enum_def_name` holds only the name of an `EnumDef`, not the definition
|
|
242
|
-
itself.
|
|
243
|
-
`
|
|
244
|
-
|
|
253
|
+
itself. In practice, `TypeDict` is most useful when you pass both
|
|
254
|
+
`type_dict.struct_dict` and `type_dict.enum_dict` together: one resolves named
|
|
255
|
+
layout types and the other resolves enum labels. The example below uses both in
|
|
256
|
+
one decode flow.
|
|
245
257
|
|
|
246
258
|
```python
|
|
247
259
|
from sltcore import InfoSize
|
|
248
|
-
from sltcodec import EnumDef, FieldDef, decode
|
|
260
|
+
from sltcodec import EnumDef, EnumDict, FieldDef, StructDef, StructDict, TypeDict, decode
|
|
261
|
+
|
|
262
|
+
enum_dict = EnumDict({"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1})})
|
|
263
|
+
|
|
264
|
+
packet_struct = StructDef(
|
|
265
|
+
name="Packet",
|
|
266
|
+
fields=[
|
|
267
|
+
FieldDef(name="status",
|
|
268
|
+
offset=InfoSize(0, 0),
|
|
269
|
+
size=InfoSize(1, 0),
|
|
270
|
+
type="unsigned int",
|
|
271
|
+
enum_def_name="Status"),
|
|
272
|
+
],
|
|
273
|
+
)
|
|
249
274
|
|
|
250
|
-
|
|
275
|
+
struct_dict = StructDict({"Packet": packet_struct})
|
|
251
276
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
offset=InfoSize(0, 0),
|
|
255
|
-
size=InfoSize(1, 0),
|
|
256
|
-
type="unsigned int",
|
|
257
|
-
enum_def_name="Status"),
|
|
258
|
-
]
|
|
277
|
+
type_dict = TypeDict(struct_dict=struct_dict.items_dict(),
|
|
278
|
+
enum_dict=enum_dict.items_dict())
|
|
259
279
|
|
|
260
|
-
decoded = decode(
|
|
261
|
-
|
|
280
|
+
decoded = decode(type_dict.struct_dict["Packet"],
|
|
281
|
+
bytearray(b"\x01"),
|
|
282
|
+
type_dict=type_dict)
|
|
262
283
|
|
|
263
284
|
print(decoded.field_instances[0].value)
|
|
264
285
|
print(decoded.field_instances[0].enum_item)
|
|
@@ -271,8 +292,8 @@ Output:
|
|
|
271
292
|
('NG', 1)
|
|
272
293
|
```
|
|
273
294
|
|
|
274
|
-
You can also persist enum definitions by name
|
|
275
|
-
`
|
|
295
|
+
You can also persist enum definitions by name through `TypeDict` using
|
|
296
|
+
`save_type_dict` / `load_type_dict`.
|
|
276
297
|
|
|
277
298
|
## Development
|
|
278
299
|
|
|
@@ -128,7 +128,7 @@ Output:
|
|
|
128
128
|
```python
|
|
129
129
|
bytearray(b"\x03\x04")
|
|
130
130
|
StructInstance(struct_def=StructDef(...), field_instances=[...])
|
|
131
|
-
[FieldInstance(field_def=FieldDef(name='pair', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=2, bit=0), type=
|
|
131
|
+
[FieldInstance(field_def=FieldDef(name='pair', offset=InfoSize(byte=0, bit=0), size=InfoSize(byte=2, bit=0), type=StructDef(...), scale=1.0, repeat=None),
|
|
132
132
|
value=StructInstance(struct_def=StructDef(...), field_instances=[...]))]
|
|
133
133
|
```
|
|
134
134
|
|
|
@@ -182,66 +182,87 @@ print(encoded_int, decoded_int)
|
|
|
182
182
|
print(encoded_float, decoded_float)
|
|
183
183
|
```
|
|
184
184
|
|
|
185
|
-
## Saving And Loading
|
|
185
|
+
## Saving And Loading Type Dictionaries
|
|
186
186
|
|
|
187
|
-
You can persist reusable structure definitions
|
|
188
|
-
`
|
|
187
|
+
You can persist reusable structure and enum definitions together via
|
|
188
|
+
`TypeDict` with `save_type_dict` / `load_type_dict`.
|
|
189
|
+
|
|
190
|
+
`InfoSize` values in `offset` and `size` are saved as typed dictionaries in
|
|
191
|
+
JSON. Expression-based offsets and sizes remain strings and are resolved when
|
|
192
|
+
the definition is used.
|
|
189
193
|
|
|
190
194
|
```python
|
|
191
195
|
from pathlib import Path
|
|
192
196
|
|
|
193
197
|
from sltcore import InfoSize
|
|
194
|
-
from sltcodec import FieldDef, StructDef,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
198
|
+
from sltcodec import EnumDef, FieldDef, StructDef, TypeDict, load_type_dict, save_type_dict
|
|
199
|
+
|
|
200
|
+
type_dict = TypeDict(
|
|
201
|
+
struct_dict={
|
|
202
|
+
"Header": StructDef(
|
|
203
|
+
name="Header",
|
|
204
|
+
description="Simple header",
|
|
205
|
+
fields=[
|
|
206
|
+
FieldDef(name="kind", offset=InfoSize(0, 0), size=InfoSize(1, 0), type="unsigned int"),
|
|
207
|
+
FieldDef(name="flags", offset=InfoSize(1, 0), size=InfoSize(1, 0), type="unsigned int"),
|
|
208
|
+
],
|
|
209
|
+
)
|
|
210
|
+
},
|
|
211
|
+
enum_dict={
|
|
212
|
+
"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1}),
|
|
213
|
+
}
|
|
214
|
+
)
|
|
206
215
|
|
|
207
216
|
path = Path("struct_defs.json")
|
|
208
|
-
|
|
209
|
-
loaded =
|
|
217
|
+
save_type_dict(path, type_dict)
|
|
218
|
+
loaded = load_type_dict(path)
|
|
210
219
|
|
|
211
|
-
print(loaded["Header"].name)
|
|
212
|
-
print(loaded["Header"].description)
|
|
220
|
+
print(loaded.struct_dict["Header"].name)
|
|
221
|
+
print(loaded.struct_dict["Header"].description)
|
|
222
|
+
print(loaded.enum_dict["Status"].values["NG"])
|
|
213
223
|
```
|
|
214
224
|
|
|
215
225
|
Example output:
|
|
216
226
|
|
|
217
227
|
```python
|
|
218
|
-
|
|
219
|
-
|
|
228
|
+
Header
|
|
229
|
+
Simple header
|
|
230
|
+
1
|
|
220
231
|
```
|
|
221
232
|
|
|
222
233
|
## Enum Definitions
|
|
223
234
|
|
|
224
235
|
`FieldDef.enum_def_name` holds only the name of an `EnumDef`, not the definition
|
|
225
|
-
itself.
|
|
226
|
-
`
|
|
227
|
-
|
|
236
|
+
itself. In practice, `TypeDict` is most useful when you pass both
|
|
237
|
+
`type_dict.struct_dict` and `type_dict.enum_dict` together: one resolves named
|
|
238
|
+
layout types and the other resolves enum labels. The example below uses both in
|
|
239
|
+
one decode flow.
|
|
228
240
|
|
|
229
241
|
```python
|
|
230
242
|
from sltcore import InfoSize
|
|
231
|
-
from sltcodec import EnumDef, FieldDef, decode
|
|
243
|
+
from sltcodec import EnumDef, EnumDict, FieldDef, StructDef, StructDict, TypeDict, decode
|
|
244
|
+
|
|
245
|
+
enum_dict = EnumDict({"Status": EnumDef(name="Status", values={"OK": 0, "NG": 1})})
|
|
246
|
+
|
|
247
|
+
packet_struct = StructDef(
|
|
248
|
+
name="Packet",
|
|
249
|
+
fields=[
|
|
250
|
+
FieldDef(name="status",
|
|
251
|
+
offset=InfoSize(0, 0),
|
|
252
|
+
size=InfoSize(1, 0),
|
|
253
|
+
type="unsigned int",
|
|
254
|
+
enum_def_name="Status"),
|
|
255
|
+
],
|
|
256
|
+
)
|
|
232
257
|
|
|
233
|
-
|
|
258
|
+
struct_dict = StructDict({"Packet": packet_struct})
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
offset=InfoSize(0, 0),
|
|
238
|
-
size=InfoSize(1, 0),
|
|
239
|
-
type="unsigned int",
|
|
240
|
-
enum_def_name="Status"),
|
|
241
|
-
]
|
|
260
|
+
type_dict = TypeDict(struct_dict=struct_dict.items_dict(),
|
|
261
|
+
enum_dict=enum_dict.items_dict())
|
|
242
262
|
|
|
243
|
-
decoded = decode(
|
|
244
|
-
|
|
263
|
+
decoded = decode(type_dict.struct_dict["Packet"],
|
|
264
|
+
bytearray(b"\x01"),
|
|
265
|
+
type_dict=type_dict)
|
|
245
266
|
|
|
246
267
|
print(decoded.field_instances[0].value)
|
|
247
268
|
print(decoded.field_instances[0].enum_item)
|
|
@@ -254,8 +275,8 @@ Output:
|
|
|
254
275
|
('NG', 1)
|
|
255
276
|
```
|
|
256
277
|
|
|
257
|
-
You can also persist enum definitions by name
|
|
258
|
-
`
|
|
278
|
+
You can also persist enum definitions by name through `TypeDict` using
|
|
279
|
+
`save_type_dict` / `load_type_dict`.
|
|
259
280
|
|
|
260
281
|
## Development
|
|
261
282
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from .codec import (PRIMITIVE_TYPES, decode, encode, encode_field,
|
|
2
|
+
load_type_dict, save_type_dict)
|
|
3
|
+
from .types import (EnumDef, EnumDict, FieldDef, FieldInstance, StructDef,
|
|
4
|
+
StructDict, StructInstance, TypeDict)
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"PRIMITIVE_TYPES",
|
|
8
|
+
"EnumDef",
|
|
9
|
+
"EnumDict",
|
|
10
|
+
"FieldDef",
|
|
11
|
+
"FieldInstance",
|
|
12
|
+
"StructDef",
|
|
13
|
+
"StructDict",
|
|
14
|
+
"StructInstance",
|
|
15
|
+
"TypeDict",
|
|
16
|
+
"decode",
|
|
17
|
+
"encode",
|
|
18
|
+
"encode_field",
|
|
19
|
+
"load_type_dict",
|
|
20
|
+
"save_type_dict",
|
|
21
|
+
]
|
|
@@ -8,7 +8,8 @@ from typing import Any
|
|
|
8
8
|
from sltcalc import SltEval
|
|
9
9
|
from sltcore import Info, InfoSize, bits_get, bits_set
|
|
10
10
|
|
|
11
|
-
from .types import EnumDef, FieldDef, FieldInstance, StructDef, StructInstance
|
|
11
|
+
from .types import (EnumDef, FieldDef, FieldInstance, StructDef, StructInstance,
|
|
12
|
+
TypeDict)
|
|
12
13
|
|
|
13
14
|
PRIMITIVE_TYPES = {
|
|
14
15
|
"bool",
|
|
@@ -46,44 +47,78 @@ def _validate_struct_instance(struct_instance: StructInstance) -> None:
|
|
|
46
47
|
f"{type(field_instance).__name__}")
|
|
47
48
|
|
|
48
49
|
|
|
49
|
-
def
|
|
50
|
-
|
|
51
|
-
"""Save a structure definition dictionary to a JSON file."""
|
|
50
|
+
def save_type_dict(path: str | Path, type_dict: TypeDict) -> None:
|
|
51
|
+
"""Save TypeDict to a JSON file."""
|
|
52
52
|
payload = {
|
|
53
|
-
|
|
54
|
-
|
|
53
|
+
"struct_dict": {
|
|
54
|
+
name: struct_def.to_dict()
|
|
55
|
+
for name, struct_def in type_dict.struct_dict.items()
|
|
56
|
+
},
|
|
57
|
+
"enum_dict": {
|
|
58
|
+
name: enum_def.to_dict()
|
|
59
|
+
for name, enum_def in type_dict.enum_dict.items()
|
|
60
|
+
},
|
|
55
61
|
}
|
|
56
62
|
Path(path).write_text(json.dumps(payload, ensure_ascii=False, indent=2),
|
|
57
63
|
encoding="utf-8")
|
|
58
64
|
|
|
59
65
|
|
|
66
|
+
def load_type_dict(path: str | Path) -> TypeDict:
|
|
67
|
+
"""Load TypeDict from a JSON file."""
|
|
68
|
+
data = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
69
|
+
|
|
70
|
+
struct_payload = data.get("struct_dict")
|
|
71
|
+
enum_payload = data.get("enum_dict")
|
|
72
|
+
|
|
73
|
+
if isinstance(struct_payload, dict) and isinstance(enum_payload, dict):
|
|
74
|
+
return TypeDict(
|
|
75
|
+
struct_dict={
|
|
76
|
+
name: StructDef.from_dict(struct_def_data)
|
|
77
|
+
for name, struct_def_data in struct_payload.items()
|
|
78
|
+
},
|
|
79
|
+
enum_dict={
|
|
80
|
+
name: EnumDef.from_dict(enum_def_data)
|
|
81
|
+
for name, enum_def_data in enum_payload.items()
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Backward compatibility: old files that stored only struct or enum maps.
|
|
86
|
+
legacy_entries: dict[str, Any] = data if isinstance(data, dict) else {}
|
|
87
|
+
if any(
|
|
88
|
+
isinstance(item, dict) and "fields" in item
|
|
89
|
+
for item in legacy_entries.values()):
|
|
90
|
+
return TypeDict(
|
|
91
|
+
struct_dict={
|
|
92
|
+
name: StructDef.from_dict(struct_def_data)
|
|
93
|
+
for name, struct_def_data in legacy_entries.items()
|
|
94
|
+
})
|
|
95
|
+
return TypeDict(
|
|
96
|
+
enum_dict={
|
|
97
|
+
name: EnumDef.from_dict(enum_def_data)
|
|
98
|
+
for name, enum_def_data in legacy_entries.items()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def save_struct_def_dict(path: str | Path,
|
|
103
|
+
struct_def_dict: dict[str, StructDef]) -> None:
|
|
104
|
+
"""Save a structure definition dictionary to a JSON file."""
|
|
105
|
+
save_type_dict(path, TypeDict(struct_dict=struct_def_dict))
|
|
106
|
+
|
|
107
|
+
|
|
60
108
|
def load_struct_def_dict(path: str | Path) -> dict[str, StructDef]:
|
|
61
109
|
"""Load a structure definition dictionary from a JSON file."""
|
|
62
|
-
|
|
63
|
-
return {
|
|
64
|
-
name: StructDef.from_dict(field_def_data)
|
|
65
|
-
for name, field_def_data in data.items()
|
|
66
|
-
}
|
|
110
|
+
return load_type_dict(path).struct_dict.items_dict()
|
|
67
111
|
|
|
68
112
|
|
|
69
113
|
def save_enum_def_dict(path: str | Path, enum_def_dict: dict[str,
|
|
70
114
|
EnumDef]) -> None:
|
|
71
115
|
"""Save an enum definition dictionary to a JSON file."""
|
|
72
|
-
|
|
73
|
-
name: enum_def.to_dict()
|
|
74
|
-
for name, enum_def in enum_def_dict.items()
|
|
75
|
-
}
|
|
76
|
-
Path(path).write_text(json.dumps(payload, ensure_ascii=False, indent=2),
|
|
77
|
-
encoding="utf-8")
|
|
116
|
+
save_type_dict(path, TypeDict(enum_dict=enum_def_dict))
|
|
78
117
|
|
|
79
118
|
|
|
80
119
|
def load_enum_def_dict(path: str | Path) -> dict[str, EnumDef]:
|
|
81
120
|
"""Load an enum definition dictionary from a JSON file."""
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
name: EnumDef.from_dict(enum_def_data)
|
|
85
|
-
for name, enum_def_data in data.items()
|
|
86
|
-
}
|
|
121
|
+
return load_type_dict(path).enum_dict.items_dict()
|
|
87
122
|
|
|
88
123
|
|
|
89
124
|
def _resolve_info_size(value: InfoSize | str, env: dict[str, Any]) -> InfoSize:
|
|
@@ -144,7 +179,7 @@ def _validate_padding_alignment_bits(padding_alignment_bits: int) -> None:
|
|
|
144
179
|
|
|
145
180
|
|
|
146
181
|
def _resolve_field_type(field_type: str | StructDef,
|
|
147
|
-
|
|
182
|
+
type_dict: TypeDict | None = None,
|
|
148
183
|
env: dict[str, Any] | None = None) -> str | StructDef:
|
|
149
184
|
"""Resolve a field type that can be primitive, named, or nested."""
|
|
150
185
|
if isinstance(field_type, StructDef):
|
|
@@ -152,7 +187,7 @@ def _resolve_field_type(field_type: str | StructDef,
|
|
|
152
187
|
if field_type in _PRIMITIVE_TYPES:
|
|
153
188
|
return field_type
|
|
154
189
|
|
|
155
|
-
resolved_struct_def = _get_struct_def(field_type,
|
|
190
|
+
resolved_struct_def = _get_struct_def(field_type, type_dict, env)
|
|
156
191
|
if isinstance(resolved_struct_def, StructDef):
|
|
157
192
|
return resolved_struct_def
|
|
158
193
|
if (isinstance(resolved_struct_def, str)
|
|
@@ -188,8 +223,7 @@ def _prepare_field_info(
|
|
|
188
223
|
field_def: FieldDef,
|
|
189
224
|
value: Any,
|
|
190
225
|
env: dict[str, Any],
|
|
191
|
-
|
|
192
|
-
enum_def_dict: dict[str, EnumDef] | None = None,
|
|
226
|
+
type_dict: TypeDict | None = None,
|
|
193
227
|
padding_alignment_bits: int = _DEFAULT_PADDING_ALIGNMENT_BITS,
|
|
194
228
|
) -> tuple[InfoSize, InfoSize, Info] | None:
|
|
195
229
|
"""Resolve a field definition into an offset, size, and info payload."""
|
|
@@ -198,7 +232,7 @@ def _prepare_field_info(
|
|
|
198
232
|
if size.byte == 0 and size.bit == 0:
|
|
199
233
|
return None
|
|
200
234
|
|
|
201
|
-
resolved_type = _resolve_field_type(field_def.type,
|
|
235
|
+
resolved_type = _resolve_field_type(field_def.type, type_dict, env)
|
|
202
236
|
if isinstance(resolved_type, StructDef):
|
|
203
237
|
if not isinstance(value, StructInstance):
|
|
204
238
|
raise TypeError(
|
|
@@ -206,8 +240,7 @@ def _prepare_field_info(
|
|
|
206
240
|
"values")
|
|
207
241
|
nested_bytes = encode(value,
|
|
208
242
|
bytearray(),
|
|
209
|
-
|
|
210
|
-
enum_def_dict,
|
|
243
|
+
type_dict,
|
|
211
244
|
padding_alignment_bits=padding_alignment_bits)
|
|
212
245
|
info = Info.from_bytes(bytes(nested_bytes), size, scale=field_def.scale)
|
|
213
246
|
else:
|
|
@@ -233,16 +266,15 @@ def encode_field(
|
|
|
233
266
|
value: Any,
|
|
234
267
|
buf: bytearray,
|
|
235
268
|
env: dict[str, Any] | None = None,
|
|
236
|
-
|
|
237
|
-
enum_def_dict: dict[str, EnumDef] | None = None,
|
|
269
|
+
type_dict: TypeDict | None = None,
|
|
238
270
|
padding_alignment_bits: int = _DEFAULT_PADDING_ALIGNMENT_BITS,
|
|
239
271
|
) -> None:
|
|
240
272
|
"""Encode a single field into a bytearray."""
|
|
241
273
|
if env is None:
|
|
242
274
|
env = {}
|
|
243
275
|
|
|
244
|
-
prepared = _prepare_field_info(field_def, value, env,
|
|
245
|
-
|
|
276
|
+
prepared = _prepare_field_info(field_def, value, env, type_dict,
|
|
277
|
+
padding_alignment_bits)
|
|
246
278
|
if prepared is None:
|
|
247
279
|
return
|
|
248
280
|
|
|
@@ -259,8 +291,7 @@ def encode_field(
|
|
|
259
291
|
def encode(
|
|
260
292
|
struct_instance: StructInstance,
|
|
261
293
|
buf: bytearray,
|
|
262
|
-
|
|
263
|
-
enum_def_dict: dict[str, EnumDef] | None = None,
|
|
294
|
+
type_dict: TypeDict | None = None,
|
|
264
295
|
padding_alignment_bits: int = _DEFAULT_PADDING_ALIGNMENT_BITS,
|
|
265
296
|
) -> bytearray:
|
|
266
297
|
"""Encode decode() result into a bytearray.
|
|
@@ -271,10 +302,9 @@ def encode(
|
|
|
271
302
|
The structure instance to encode.
|
|
272
303
|
buf : bytearray
|
|
273
304
|
The base bytearray instance to write into.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
A dictionary of enum definitions, by default None.
|
|
305
|
+
type_dict : TypeDict | None, optional
|
|
306
|
+
Type dictionaries including structure and enum definitions,
|
|
307
|
+
by default None.
|
|
278
308
|
padding_alignment_bits : int, optional
|
|
279
309
|
The padding alignment boundary in bits, by default 32.
|
|
280
310
|
|
|
@@ -304,8 +334,7 @@ def encode(
|
|
|
304
334
|
for i in range(field_def.repeat):
|
|
305
335
|
field_def_repeat = _split_repeated_field(
|
|
306
336
|
field_def, i, env, current_offset)
|
|
307
|
-
encode_field(field_def_repeat, values[i], buf, env,
|
|
308
|
-
struct_def_dict, enum_def_dict,
|
|
337
|
+
encode_field(field_def_repeat, values[i], buf, env, type_dict,
|
|
309
338
|
padding_alignment_bits)
|
|
310
339
|
env[field_def_repeat.name] = values[i]
|
|
311
340
|
if isinstance(current_offset, InfoSize) and isinstance(
|
|
@@ -313,7 +342,7 @@ def encode(
|
|
|
313
342
|
current_offset += resolved_size
|
|
314
343
|
continue
|
|
315
344
|
|
|
316
|
-
encode_field(field_def, value, buf, env,
|
|
345
|
+
encode_field(field_def, value, buf, env, type_dict,
|
|
317
346
|
padding_alignment_bits)
|
|
318
347
|
env[field_def.name] = value
|
|
319
348
|
|
|
@@ -327,8 +356,7 @@ def decode_field(
|
|
|
327
356
|
field_def: FieldDef,
|
|
328
357
|
data: bytearray | bytes,
|
|
329
358
|
env: dict[str, Any] | None = None,
|
|
330
|
-
|
|
331
|
-
enum_def_dict: dict[str, EnumDef] | None = None,
|
|
359
|
+
type_dict: TypeDict | None = None,
|
|
332
360
|
padding_alignment_bits: int = _DEFAULT_PADDING_ALIGNMENT_BITS,
|
|
333
361
|
) -> FieldInstance | None:
|
|
334
362
|
"""Decode a single field from a bytearray according to a field definition.
|
|
@@ -341,8 +369,9 @@ def decode_field(
|
|
|
341
369
|
The data to decode.
|
|
342
370
|
env : dict[str, Any] | None, optional
|
|
343
371
|
The environment for evaluating expressions, by default None.
|
|
344
|
-
|
|
345
|
-
|
|
372
|
+
type_dict : TypeDict | None, optional
|
|
373
|
+
Type dictionaries including structure and enum definitions,
|
|
374
|
+
by default None.
|
|
346
375
|
Returns
|
|
347
376
|
-------
|
|
348
377
|
FieldInstance | None
|
|
@@ -358,7 +387,7 @@ def decode_field(
|
|
|
358
387
|
byte_swap = _resolve_byte_swap(field_def, env)
|
|
359
388
|
if byte_swap:
|
|
360
389
|
info = _byte_swap_info(info, size)
|
|
361
|
-
resolved_type = _resolve_field_type(field_def.type,
|
|
390
|
+
resolved_type = _resolve_field_type(field_def.type, type_dict, env)
|
|
362
391
|
resolved_field_def = FieldDef(
|
|
363
392
|
name=field_def.name,
|
|
364
393
|
offset=offset,
|
|
@@ -374,40 +403,38 @@ def decode_field(
|
|
|
374
403
|
if isinstance(resolved_type, StructDef):
|
|
375
404
|
return FieldInstance(
|
|
376
405
|
field_def=resolved_field_def,
|
|
377
|
-
value=decode(resolved_type, bytearray(info.to_bytes),
|
|
378
|
-
struct_def_dict, enum_def_dict,
|
|
406
|
+
value=decode(resolved_type, bytearray(info.to_bytes), type_dict,
|
|
379
407
|
padding_alignment_bits),
|
|
380
408
|
)
|
|
381
409
|
if resolved_type == "bool":
|
|
382
410
|
return FieldInstance.from_value(resolved_field_def,
|
|
383
411
|
info.to_bool,
|
|
384
|
-
|
|
412
|
+
type_dict=type_dict)
|
|
385
413
|
if resolved_type == "signed int":
|
|
386
414
|
return FieldInstance.from_value(resolved_field_def,
|
|
387
415
|
info.to_signed_int,
|
|
388
|
-
|
|
416
|
+
type_dict=type_dict)
|
|
389
417
|
if resolved_type in ["int", "unsigned int"]:
|
|
390
418
|
return FieldInstance.from_value(resolved_field_def,
|
|
391
419
|
info.to_unsigned_int,
|
|
392
|
-
|
|
420
|
+
type_dict=type_dict)
|
|
393
421
|
if resolved_type == "float":
|
|
394
422
|
return FieldInstance.from_value(resolved_field_def,
|
|
395
423
|
info.to_float,
|
|
396
|
-
|
|
424
|
+
type_dict=type_dict)
|
|
397
425
|
if resolved_type in ["bytearray", "bytes"]:
|
|
398
426
|
return FieldInstance.from_value(resolved_field_def,
|
|
399
427
|
info.to_bytes,
|
|
400
|
-
|
|
428
|
+
type_dict=type_dict)
|
|
401
429
|
return FieldInstance.from_value(resolved_field_def,
|
|
402
430
|
info.raw_value,
|
|
403
|
-
|
|
431
|
+
type_dict=type_dict)
|
|
404
432
|
|
|
405
433
|
|
|
406
434
|
def decode(
|
|
407
435
|
struct_def: StructDef | list[FieldDef],
|
|
408
436
|
data: bytearray | bytes,
|
|
409
|
-
|
|
410
|
-
enum_def_dict: dict[str, EnumDef] | None = None,
|
|
437
|
+
type_dict: TypeDict | None = None,
|
|
411
438
|
padding_alignment_bits: int = _DEFAULT_PADDING_ALIGNMENT_BITS,
|
|
412
439
|
) -> StructInstance:
|
|
413
440
|
"""Decode a bytearray into field values according to a layout.
|
|
@@ -418,10 +445,9 @@ def decode(
|
|
|
418
445
|
The definitions of the fields to decode.
|
|
419
446
|
data : bytearray | bytes
|
|
420
447
|
The data to decode.
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
A dictionary of enum definitions, by default None.
|
|
448
|
+
type_dict : TypeDict | None, optional
|
|
449
|
+
Type dictionaries including structure and enum definitions,
|
|
450
|
+
by default None.
|
|
425
451
|
padding_alignment_bits : int, optional
|
|
426
452
|
The padding alignment boundary in bits, by default 32.
|
|
427
453
|
Returns
|
|
@@ -471,8 +497,8 @@ def decode(
|
|
|
471
497
|
if field_def.repeat is None or field_def.repeat <= 1:
|
|
472
498
|
resolved_offset = _resolve_offset(field_def, env)
|
|
473
499
|
append_padding_until(resolved_offset)
|
|
474
|
-
field_instance = decode_field(field_def, data, env,
|
|
475
|
-
|
|
500
|
+
field_instance = decode_field(field_def, data, env, type_dict,
|
|
501
|
+
padding_alignment_bits)
|
|
476
502
|
if field_instance is not None:
|
|
477
503
|
env[field_instance.field_def.name] = field_instance.value
|
|
478
504
|
result.append_field_instance(field_instance)
|
|
@@ -487,8 +513,7 @@ def decode(
|
|
|
487
513
|
field_def_repeat = _split_repeated_field(field_def, i, env,
|
|
488
514
|
current_offset)
|
|
489
515
|
field_instance = decode_field(field_def_repeat, data, env,
|
|
490
|
-
|
|
491
|
-
padding_alignment_bits)
|
|
516
|
+
type_dict, padding_alignment_bits)
|
|
492
517
|
if field_instance is not None:
|
|
493
518
|
env[field_instance.field_def.name] = field_instance.value
|
|
494
519
|
result.append_field_instance(field_instance)
|
|
@@ -501,17 +526,19 @@ def decode(
|
|
|
501
526
|
|
|
502
527
|
|
|
503
528
|
def _get_struct_def(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
529
|
+
struct_def_name: str,
|
|
530
|
+
type_dict: TypeDict | None = None,
|
|
531
|
+
env: dict[str, Any] | None = None,
|
|
532
|
+
) -> StructDef | str | None:
|
|
507
533
|
"""Get a structure definition from a dictionary or evaluate it.
|
|
508
534
|
|
|
509
535
|
Parameters
|
|
510
536
|
----------
|
|
511
537
|
struct_def_name : str
|
|
512
538
|
The name of the structure definition to get.
|
|
513
|
-
|
|
514
|
-
|
|
539
|
+
type_dict : TypeDict | None, optional
|
|
540
|
+
Type dictionaries including structure and enum definitions,
|
|
541
|
+
by default None.
|
|
515
542
|
env : dict[str, Any] | None, optional
|
|
516
543
|
The environment for evaluating the structure definition,
|
|
517
544
|
by default None.
|
|
@@ -521,8 +548,10 @@ def _get_struct_def(
|
|
|
521
548
|
StructDef | str | None
|
|
522
549
|
The structure definition, primitive type, or None if not found.
|
|
523
550
|
"""
|
|
524
|
-
if
|
|
525
|
-
|
|
551
|
+
struct_dict = None if type_dict is None else type_dict.struct_dict
|
|
552
|
+
|
|
553
|
+
if struct_dict and struct_def_name in struct_dict:
|
|
554
|
+
return struct_dict[struct_def_name]
|
|
526
555
|
|
|
527
556
|
stleval = SltEval(env)
|
|
528
557
|
try:
|
|
@@ -532,7 +561,7 @@ def _get_struct_def(
|
|
|
532
561
|
|
|
533
562
|
if isinstance(eval_result, str) and eval_result in _PRIMITIVE_TYPES:
|
|
534
563
|
return eval_result
|
|
535
|
-
if (isinstance(eval_result, str) and
|
|
536
|
-
and eval_result in
|
|
537
|
-
return
|
|
564
|
+
if (isinstance(eval_result, str) and struct_dict
|
|
565
|
+
and eval_result in struct_dict):
|
|
566
|
+
return struct_dict[eval_result]
|
|
538
567
|
return None
|
|
@@ -3,9 +3,10 @@ from __future__ import annotations
|
|
|
3
3
|
|
|
4
4
|
import json
|
|
5
5
|
import re
|
|
6
|
+
from collections.abc import MutableMapping
|
|
6
7
|
from dataclasses import dataclass, field
|
|
7
8
|
from functools import total_ordering
|
|
8
|
-
from typing import Any
|
|
9
|
+
from typing import Any, Dict, Iterator, Optional
|
|
9
10
|
|
|
10
11
|
from sltcalc import SltEval
|
|
11
12
|
from sltcore import Info, InfoSize
|
|
@@ -159,17 +160,19 @@ class FieldDef:
|
|
|
159
160
|
|
|
160
161
|
def to_dict(self) -> dict[str, Any]:
|
|
161
162
|
"""Convert this field definition to a JSON-serializable dictionary."""
|
|
163
|
+
|
|
164
|
+
def _serialize_info_like(value: Any) -> Any:
|
|
165
|
+
if isinstance(value, (Info, InfoSize)):
|
|
166
|
+
return json.loads(value.to_json())
|
|
167
|
+
return value
|
|
168
|
+
|
|
162
169
|
return {
|
|
163
170
|
"name":
|
|
164
171
|
self.name,
|
|
165
172
|
"offset":
|
|
166
|
-
self.offset
|
|
167
|
-
(Info,
|
|
168
|
-
InfoSize)) else self.offset,
|
|
173
|
+
_serialize_info_like(self.offset),
|
|
169
174
|
"size":
|
|
170
|
-
self.size
|
|
171
|
-
(Info,
|
|
172
|
-
InfoSize)) else self.size,
|
|
175
|
+
_serialize_info_like(self.size),
|
|
173
176
|
"type": {
|
|
174
177
|
"__type__": "StructDef",
|
|
175
178
|
"fields": self.type.to_dict(),
|
|
@@ -334,10 +337,10 @@ class FieldInstance:
|
|
|
334
337
|
def from_value(cls,
|
|
335
338
|
field_def: FieldDef,
|
|
336
339
|
value: Any,
|
|
337
|
-
|
|
340
|
+
type_dict: "TypeDict | None" = None,
|
|
338
341
|
is_padding: bool = False) -> "FieldInstance":
|
|
339
342
|
"""Create a FieldInstance and attach a matched enum item if any."""
|
|
340
|
-
enum_def = cls._resolve_enum_def(field_def,
|
|
343
|
+
enum_def = cls._resolve_enum_def(field_def, type_dict)
|
|
341
344
|
enum_item = cls._resolve_enum_item(enum_def, value)
|
|
342
345
|
return cls(field_def=field_def,
|
|
343
346
|
value=value,
|
|
@@ -345,17 +348,19 @@ class FieldInstance:
|
|
|
345
348
|
is_padding=is_padding)
|
|
346
349
|
|
|
347
350
|
@staticmethod
|
|
348
|
-
def _resolve_enum_def(
|
|
349
|
-
|
|
350
|
-
enum_def_dict: dict[str, EnumDef] | None) -> EnumDef | None:
|
|
351
|
+
def _resolve_enum_def(field_def: FieldDef,
|
|
352
|
+
type_dict: "TypeDict | None") -> EnumDef | None:
|
|
351
353
|
"""Resolve enum definition from field metadata or lookup dictionary."""
|
|
352
|
-
if
|
|
354
|
+
if type_dict is None:
|
|
355
|
+
return None
|
|
356
|
+
enum_dict = type_dict.enum_dict
|
|
357
|
+
if len(enum_dict) == 0:
|
|
353
358
|
return None
|
|
354
359
|
if field_def.enum_def_name is not None:
|
|
355
|
-
return
|
|
356
|
-
if isinstance(field_def.type, str) and field_def.type in
|
|
357
|
-
return
|
|
358
|
-
return
|
|
360
|
+
return enum_dict.get(field_def.enum_def_name)
|
|
361
|
+
if isinstance(field_def.type, str) and field_def.type in enum_dict:
|
|
362
|
+
return enum_dict[field_def.type]
|
|
363
|
+
return enum_dict.get(field_def.name)
|
|
359
364
|
|
|
360
365
|
@staticmethod
|
|
361
366
|
def _resolve_enum_item(enum_def: EnumDef | None,
|
|
@@ -594,3 +599,82 @@ class StructInstance:
|
|
|
594
599
|
|
|
595
600
|
if max_end_offset > self.size:
|
|
596
601
|
self.size = max_end_offset
|
|
602
|
+
|
|
603
|
+
|
|
604
|
+
@dataclass
|
|
605
|
+
class EnumDict(MutableMapping):
|
|
606
|
+
"""A dictionary-like container for EnumDef objects."""
|
|
607
|
+
_items: Dict[str, "EnumDef"]
|
|
608
|
+
|
|
609
|
+
def __init__(self, items: Optional[Dict[str, "EnumDef"]] = None):
|
|
610
|
+
self._items = items or {}
|
|
611
|
+
|
|
612
|
+
def __getitem__(self, key: str) -> "EnumDef":
|
|
613
|
+
return self._items[key]
|
|
614
|
+
|
|
615
|
+
def __setitem__(self, key: str, value: "EnumDef"):
|
|
616
|
+
self._items[key] = value
|
|
617
|
+
|
|
618
|
+
def __delitem__(self, key: str):
|
|
619
|
+
del self._items[key]
|
|
620
|
+
|
|
621
|
+
def __iter__(self) -> Iterator[str]:
|
|
622
|
+
return iter(self._items)
|
|
623
|
+
|
|
624
|
+
def __len__(self) -> int:
|
|
625
|
+
return len(self._items)
|
|
626
|
+
|
|
627
|
+
def items_dict(self) -> Dict[str, "EnumDef"]:
|
|
628
|
+
"""Return the underlying dictionary of EnumDef objects.
|
|
629
|
+
Returns:
|
|
630
|
+
Dict[str, EnumDef]: The underlying dictionary
|
|
631
|
+
of EnumDef objects."""
|
|
632
|
+
return self._items
|
|
633
|
+
|
|
634
|
+
|
|
635
|
+
@dataclass
|
|
636
|
+
class StructDict(MutableMapping):
|
|
637
|
+
"""A dictionary-like container for StructDef objects. """
|
|
638
|
+
_items: Dict[str, "StructDef"]
|
|
639
|
+
|
|
640
|
+
def __init__(self, items: Optional[Dict[str, "StructDef"]] = None):
|
|
641
|
+
self._items = items or {}
|
|
642
|
+
|
|
643
|
+
def __getitem__(self, key: str) -> "StructDef":
|
|
644
|
+
return self._items[key]
|
|
645
|
+
|
|
646
|
+
def __setitem__(self, key: str, value: "StructDef"):
|
|
647
|
+
self._items[key] = value
|
|
648
|
+
|
|
649
|
+
def __delitem__(self, key: str):
|
|
650
|
+
del self._items[key]
|
|
651
|
+
|
|
652
|
+
def __iter__(self) -> Iterator[str]:
|
|
653
|
+
return iter(self._items)
|
|
654
|
+
|
|
655
|
+
def __len__(self) -> int:
|
|
656
|
+
return len(self._items)
|
|
657
|
+
|
|
658
|
+
def items_dict(self) -> Dict[str, "StructDef"]:
|
|
659
|
+
"""Return the underlying dictionary of StructDef objects.
|
|
660
|
+
Returns:
|
|
661
|
+
Dict[str, StructDef]: The underlying dictionary
|
|
662
|
+
of StructDef objects."""
|
|
663
|
+
return self._items
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@dataclass
|
|
667
|
+
class TypeDict:
|
|
668
|
+
"""A dictionary-like container for EnumDef and StructDef objects."""
|
|
669
|
+
enum_dict: EnumDict
|
|
670
|
+
struct_dict: StructDict
|
|
671
|
+
|
|
672
|
+
def __init__(
|
|
673
|
+
self,
|
|
674
|
+
enum_dict: Optional[Dict[str, "EnumDef"]] = None,
|
|
675
|
+
struct_dict: Optional[Dict[str, "StructDef"]] = None,
|
|
676
|
+
):
|
|
677
|
+
"""Initialize TypeDict with optional EnumDef
|
|
678
|
+
and StructDef dictionaries."""
|
|
679
|
+
self.enum_dict = EnumDict(enum_dict)
|
|
680
|
+
self.struct_dict = StructDict(struct_dict)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
"""Tests for the sltcodec module."""
|
|
2
|
+
import json
|
|
2
3
|
from enum import Enum
|
|
3
4
|
from pathlib import Path
|
|
4
5
|
|
|
@@ -6,9 +7,8 @@ import pytest
|
|
|
6
7
|
from sltcore import InfoSize
|
|
7
8
|
|
|
8
9
|
from sltcodec import (PRIMITIVE_TYPES, EnumDef, FieldDef, FieldInstance,
|
|
9
|
-
StructDef, StructInstance, decode, encode,
|
|
10
|
-
|
|
11
|
-
save_enum_def_dict, save_struct_def_dict)
|
|
10
|
+
StructDef, StructInstance, TypeDict, decode, encode,
|
|
11
|
+
load_type_dict, save_type_dict)
|
|
12
12
|
from sltcodec.codec import decode_field
|
|
13
13
|
|
|
14
14
|
|
|
@@ -401,10 +401,9 @@ def test_decode_uses_enum_def_dict_to_set_enum_item():
|
|
|
401
401
|
offset=InfoSize(0, 0),
|
|
402
402
|
size=InfoSize(1, 0),
|
|
403
403
|
type="unsigned int")
|
|
404
|
+
type_dict = TypeDict(enum_dict={"status": enum_def})
|
|
404
405
|
|
|
405
|
-
decoded = decode([field_def],
|
|
406
|
-
bytearray(b"\x02"),
|
|
407
|
-
enum_def_dict={"status": enum_def})
|
|
406
|
+
decoded = decode([field_def], bytearray(b"\x02"), type_dict=type_dict)
|
|
408
407
|
|
|
409
408
|
assert decoded.field_instances[0].value == 2
|
|
410
409
|
assert decoded.field_instances[0].enum_item == ("NG", 2)
|
|
@@ -420,7 +419,7 @@ def test_field_instance_from_value_sets_enum_item_from_field_enum_def():
|
|
|
420
419
|
enum_def_name=enum_def.name)
|
|
421
420
|
|
|
422
421
|
field_instance = FieldInstance.from_value(
|
|
423
|
-
field_def, 1,
|
|
422
|
+
field_def, 1, type_dict=TypeDict(enum_dict={enum_def.name: enum_def}))
|
|
424
423
|
|
|
425
424
|
assert field_instance.enum_item == ("MANUAL", 1)
|
|
426
425
|
|
|
@@ -459,8 +458,8 @@ def test_struct_instance_size_json_round_trip():
|
|
|
459
458
|
assert struct_instance.size == InfoSize(8, 0)
|
|
460
459
|
|
|
461
460
|
|
|
462
|
-
def
|
|
463
|
-
"""Test
|
|
461
|
+
def test_type_dict_load_and_save_struct_dict(tmp_path: Path):
|
|
462
|
+
"""Test TypeDict save/load for struct definitions."""
|
|
464
463
|
struct_def = StructDef(name="ValueStruct",
|
|
465
464
|
description="Single value struct",
|
|
466
465
|
fields=[
|
|
@@ -472,14 +471,19 @@ def test_struct_def_dict_load_and_save(tmp_path: Path):
|
|
|
472
471
|
])
|
|
473
472
|
path = tmp_path / "field_defs.json"
|
|
474
473
|
|
|
475
|
-
|
|
476
|
-
|
|
474
|
+
save_type_dict(path, TypeDict(struct_dict={"value": struct_def}))
|
|
475
|
+
saved_field = json.loads(
|
|
476
|
+
path.read_text())["struct_dict"]["value"]["fields"][0]
|
|
477
477
|
|
|
478
|
-
assert
|
|
478
|
+
assert isinstance(saved_field["offset"], dict)
|
|
479
|
+
assert isinstance(saved_field["size"], dict)
|
|
480
|
+
loaded = load_type_dict(path)
|
|
479
481
|
|
|
482
|
+
assert loaded.struct_dict["value"] == struct_def
|
|
480
483
|
|
|
481
|
-
|
|
482
|
-
|
|
484
|
+
|
|
485
|
+
def test_type_dict_load_and_save_enum_dict(tmp_path: Path):
|
|
486
|
+
"""Test TypeDict save/load for enum definitions."""
|
|
483
487
|
enum_def = EnumDef(name="Status",
|
|
484
488
|
description="Status enum",
|
|
485
489
|
values={
|
|
@@ -488,10 +492,10 @@ def test_enum_def_dict_load_and_save(tmp_path: Path):
|
|
|
488
492
|
})
|
|
489
493
|
path = tmp_path / "enum_defs.json"
|
|
490
494
|
|
|
491
|
-
|
|
492
|
-
loaded =
|
|
495
|
+
save_type_dict(path, TypeDict(enum_dict={"status": enum_def}))
|
|
496
|
+
loaded = load_type_dict(path)
|
|
493
497
|
|
|
494
|
-
assert loaded["status"] == enum_def
|
|
498
|
+
assert loaded.enum_dict["status"] == enum_def
|
|
495
499
|
|
|
496
500
|
|
|
497
501
|
def test_struct_def_to_json_from_json_round_trip():
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
from .codec import (PRIMITIVE_TYPES, decode, encode, encode_field,
|
|
2
|
-
load_enum_def_dict, load_struct_def_dict,
|
|
3
|
-
save_enum_def_dict, save_struct_def_dict)
|
|
4
|
-
from .types import EnumDef, FieldDef, FieldInstance, StructDef, StructInstance
|
|
5
|
-
|
|
6
|
-
__all__ = [
|
|
7
|
-
"PRIMITIVE_TYPES",
|
|
8
|
-
"EnumDef",
|
|
9
|
-
"FieldDef",
|
|
10
|
-
"FieldInstance",
|
|
11
|
-
"StructDef",
|
|
12
|
-
"StructInstance",
|
|
13
|
-
"decode",
|
|
14
|
-
"encode",
|
|
15
|
-
"encode_field",
|
|
16
|
-
"load_enum_def_dict",
|
|
17
|
-
"load_struct_def_dict",
|
|
18
|
-
"save_enum_def_dict",
|
|
19
|
-
"save_struct_def_dict",
|
|
20
|
-
]
|
|
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
|