pycstructdataparser-lib 1.0.1__tar.gz → 1.0.3__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.4
2
2
  Name: pycstructdataparser-lib
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Parse C/C++ struct definitions from header text and serialize/deserialize binary data with ctypes — no compilation needed
5
5
  Author: NGC13009
6
6
  License-Expression: GPL-3.0-only
@@ -33,7 +33,7 @@ Dynamic: license-file
33
33
  </p>
34
34
 
35
35
  <p align="center">
36
- [<a href="README_CN.md">中文说明书</a>] | English
36
+ [<a href="https://github.com/NGC13009/pycstructdataparser_lib/blob/main/README_CN.md">中文说明书</a>] | English
37
37
  </p>
38
38
 
39
39
  **Automatically parse struct definitions directly from C/C++ header text and process binary data in Python** — No compilation required, no manual `ctypes.Structure` subclass writing needed, zero boilerplate code. Seamless data transfer between C/C++ and Python with automated data unpacking/packing.
@@ -107,7 +107,7 @@ pack output: 21 bytes
107
107
  - **Nested Structs**: Supports struct types used as fields within other structs
108
108
  - **Serialization (pack)**: Converts Python dictionaries into C struct binary bytes
109
109
  - **Deserialization (unpack)**: Parses raw bytes back into nested Python dictionaries
110
- - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing
110
+ - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing, plus value‑domain checks (bitfield overflow, integer type range)
111
111
 
112
112
  ## Installation Methods
113
113
 
@@ -239,7 +239,10 @@ Serializes a Python dictionary into binary bytes corresponding to a C struct.
239
239
  - `parser`: A `CStructParser` instance on which `parse()` has been called.
240
240
  - `struct_name`: The target struct type name.
241
241
  - `data_dict`: A dictionary mapping field names to field values.
242
- - `strict`: If `True` (default), raises `ValueError` when non-bitfield fields are missing; if `False`, missing fields are silently filled with 0.
242
+ - `strict`: If `True` (default):
243
+ - Raises `ValueError` when a **non-bitfield** field is missing.
244
+ - Performs **value‑domain validation** on each scalar value: bitfield values must be within `[0, 2^width - 1]`; integer types must be within their type min/max (e.g. `uint8_t` → `[0, 255]`, `int32_t` → `[-2147483648, 2147483647]`); floating‑point and struct types are not range‑checked.
245
+ - If `False`, missing fields are silently filled with 0 and no value‑domain checks are performed.
243
246
 
244
247
  ### `unpack(parser, struct_name, data)`
245
248
 
@@ -295,4 +298,5 @@ Deserializes raw binary bytes into a nested Python dictionary.
295
298
 
296
299
  ## License
297
300
 
298
- This project is licensed under [GPLv3.0](LICENSE).
301
+ This project is licensed under [GPLv3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
302
+ [NGC13009/pycstructdataparser_lib.git](https://github.com/NGC13009/pycstructdataparser_lib.git)
@@ -7,7 +7,7 @@
7
7
  </p>
8
8
 
9
9
  <p align="center">
10
- [<a href="README_CN.md">中文说明书</a>] | English
10
+ [<a href="https://github.com/NGC13009/pycstructdataparser_lib/blob/main/README_CN.md">中文说明书</a>] | English
11
11
  </p>
12
12
 
13
13
  **Automatically parse struct definitions directly from C/C++ header text and process binary data in Python** — No compilation required, no manual `ctypes.Structure` subclass writing needed, zero boilerplate code. Seamless data transfer between C/C++ and Python with automated data unpacking/packing.
@@ -81,7 +81,7 @@ pack output: 21 bytes
81
81
  - **Nested Structs**: Supports struct types used as fields within other structs
82
82
  - **Serialization (pack)**: Converts Python dictionaries into C struct binary bytes
83
83
  - **Deserialization (unpack)**: Parses raw bytes back into nested Python dictionaries
84
- - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing
84
+ - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing, plus value‑domain checks (bitfield overflow, integer type range)
85
85
 
86
86
  ## Installation Methods
87
87
 
@@ -213,7 +213,10 @@ Serializes a Python dictionary into binary bytes corresponding to a C struct.
213
213
  - `parser`: A `CStructParser` instance on which `parse()` has been called.
214
214
  - `struct_name`: The target struct type name.
215
215
  - `data_dict`: A dictionary mapping field names to field values.
216
- - `strict`: If `True` (default), raises `ValueError` when non-bitfield fields are missing; if `False`, missing fields are silently filled with 0.
216
+ - `strict`: If `True` (default):
217
+ - Raises `ValueError` when a **non-bitfield** field is missing.
218
+ - Performs **value‑domain validation** on each scalar value: bitfield values must be within `[0, 2^width - 1]`; integer types must be within their type min/max (e.g. `uint8_t` → `[0, 255]`, `int32_t` → `[-2147483648, 2147483647]`); floating‑point and struct types are not range‑checked.
219
+ - If `False`, missing fields are silently filled with 0 and no value‑domain checks are performed.
217
220
 
218
221
  ### `unpack(parser, struct_name, data)`
219
222
 
@@ -269,4 +272,5 @@ Deserializes raw binary bytes into a nested Python dictionary.
269
272
 
270
273
  ## License
271
274
 
272
- This project is licensed under [GPLv3.0](LICENSE).
275
+ This project is licensed under [GPLv3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
276
+ [NGC13009/pycstructdataparser_lib.git](https://github.com/NGC13009/pycstructdataparser_lib.git)
@@ -14,7 +14,49 @@ import re
14
14
  import ctypes
15
15
  from typing import Any, Dict, List, Optional, Tuple
16
16
 
17
- from ctype_map_def import TYPE_MAP
17
+ from ctype_map_def import TYPE_MAP, get_value_range
18
+
19
+
20
+ # Value‑range validation (used by pack() when strict=True)
21
+ def _validate_value(value: Any, ctypes_type: Any, bitwidth: Optional[int] = None, field_path: str = "?") -> None:
22
+ """Validate *value* against the domain of *ctypes_type*.
23
+
24
+ Rules (applied only when *strict* is True):
25
+
26
+ * **Bitfield** (``bitwidth is not None``):
27
+ Raises :exc:`ValueError` if *value* < 0 or *value* >= 2\\ :sup:`bitwidth`.
28
+ * **Integer type** (registered in ``_VALUE_RANGE_DEFS``):
29
+ Raises :exc:`ValueError` if *value* lies outside ``[min, max]``.
30
+ * **Floating‑point types / structs / pointers**:
31
+ No validation performed (silently passes).
32
+
33
+ Args:
34
+ value: The raw Python value (typically ``int``) to validate.
35
+ ctypes_type: The target ctypes type (e.g. ``ctypes.c_uint8``).
36
+ bitwidth: Bit‑width for bitfield fields; ``None`` for normal fields.
37
+ field_path: Human‑readable path used in error messages
38
+ (e.g. ``"BBBtype.eee"``).
39
+
40
+ Raises:
41
+ ValueError: If the value is out of the allowed domain.
42
+ """
43
+ # --- 1. Bitfield check ---
44
+ if bitwidth is not None:
45
+ max_val = (1 << bitwidth) - 1
46
+ if not isinstance(value, int) or value < 0 or value > max_val:
47
+ raise ValueError(f"Value {value} out of range for bitfield '{field_path}' "
48
+ f"(width={bitwidth}, allowed [0, {max_val}])")
49
+ return
50
+
51
+ # --- 2. Integer type range check ---
52
+ rng = get_value_range(ctypes_type)
53
+ if rng is None:
54
+ return # float / struct / pointer — skip
55
+
56
+ lo, hi = rng
57
+ if not isinstance(value, int) or value < lo or value > hi:
58
+ raise ValueError(f"Value {value} out of range for field '{field_path}' "
59
+ f"(type={ctypes_type.__name__}, allowed [{lo}, {hi}])")
18
60
 
19
61
 
20
62
  # Recursive conversion helper (dict ↔ ctypes.Structure)
@@ -37,37 +79,63 @@ def _struct_to_dict(obj: Any) -> Any:
37
79
  return obj
38
80
 
39
81
 
40
- def _dict_to_struct(obj: ctypes.Structure, data: Dict[str, Any], strict: bool = True) -> None:
82
+ def _dict_to_struct(obj: ctypes.Structure, data: Dict[str, Any], strict: bool = True, parent_path: str = "") -> None:
41
83
  """Recursively fill a ctypes Structure from a plain Python dict.
42
84
 
43
- When *strict* is True, a ``ValueError`` is raised if *data* is missing
44
- a **non-bitfield** key. Bitfield fields are always optional (default to 0).
45
- When *strict* is False, all missing keys silently default to 0.
85
+ When *strict* is True:
86
+
87
+ * Raises :exc:`ValueError` if *data* is missing a **non-bitfield** key.
88
+ Bitfield fields are always optional (default to 0).
89
+ * Validates each scalar value against its type domain (bitwidth for
90
+ bitfields, integer range for integer types).
91
+
92
+ When *strict* is False, all missing keys silently default to 0 and
93
+ no value‑domain checks are performed.
94
+
95
+ Args:
96
+ obj: The ctypes Structure instance to fill.
97
+ data: The source dictionary.
98
+ strict: Enable strict validation.
99
+ parent_path: Accumulated field path for error messages
100
+ (e.g. ``"BBBtype"``, ``"BBBtype.aaalist[3]"``).
46
101
  """
47
- # Pre-compute bitfield names so strict mode can exclude them
48
- bitfield_names: set = set()
102
+ # Pre-compute bitfield name→width map for validation
103
+ bitfield_widths: Dict[str, int] = {}
49
104
  for f in obj._fields_:
50
105
  if len(f) == 3: # (name, type, width)
51
- bitfield_names.add(f[0])
106
+ bitfield_widths[f[0]] = f[2]
52
107
 
53
108
  for f in obj._fields_:
54
109
  field_name = f[0]
110
+ ftype = f[1] # ctypes type
55
111
  is_bitfield = len(f) == 3
112
+ field_path = f"{parent_path}.{field_name}" if parent_path else field_name
56
113
 
57
114
  if field_name in data:
58
115
  value = data[field_name]
59
116
  target = getattr(obj, field_name)
60
117
 
61
118
  if isinstance(target, ctypes.Structure):
62
- _dict_to_struct(target, value, strict)
119
+ # Nested struct — recurse
120
+ _dict_to_struct(target, value, strict, field_path)
121
+
63
122
  elif isinstance(target, ctypes.Array):
123
+ elem_type = target._type_
64
124
  for i, item in enumerate(value):
65
125
  elem = target[i]
126
+ elem_path = f"{field_path}[{i}]"
66
127
  if isinstance(elem, ctypes.Structure):
67
- _dict_to_struct(elem, item, strict)
128
+ _dict_to_struct(elem, item, strict, elem_path)
68
129
  else:
130
+ # Scalar array element — validate in strict mode
131
+ if strict:
132
+ _validate_value(item, elem_type, None, elem_path)
69
133
  target[i] = item
70
134
  else:
135
+ # Plain scalar — validate in strict mode, then assign
136
+ if strict:
137
+ bw = f[2] if is_bitfield else None
138
+ _validate_value(value, ftype, bw, field_path)
71
139
  setattr(obj, field_name, value)
72
140
 
73
141
  elif strict and not is_bitfield:
@@ -324,9 +392,18 @@ def pack(parser: CStructParser, struct_name: str, data_dict: Dict[str, Any], str
324
392
  parser: A :class:`CStructParser` that has had :meth:`parse` called.
325
393
  struct_name: The struct type name to use.
326
394
  data_dict: A dictionary whose keys correspond to struct field names.
327
- strict: If ``True`` (default), raises :class:`ValueError` when a
328
- **non-bitfield** key is missing. If ``False``, missing keys
329
- silently default to 0.
395
+ strict: If ``True`` (default):
396
+
397
+ * Raises :class:`ValueError` when a **non‑bitfield** key is missing.
398
+ * Validates each scalar value against its type domain:
399
+
400
+ * **Bitfields** — value must be in ``[0, 2\\ :sup:`width`\\ - 1]``.
401
+ * **Integer types** — value must be in ``[type_min, type_max]``
402
+ (e.g. ``uint8_t`` → ``[0, 255]``, ``int32_t`` → ``[-2147483648, 2147483647]``).
403
+ * **Floating‑point / struct** types are **not** range‑checked.
404
+
405
+ If ``False``, missing keys silently default to 0 and no value‑domain
406
+ checks are performed.
330
407
 
331
408
  Returns:
332
409
  Raw binary bytes.
@@ -0,0 +1,113 @@
1
+ # coding = utf-8
2
+ # Arch = manyArch
3
+ #
4
+ # @File name: type_map_def.py
5
+ # @brief: Type definition file for Python struct parser
6
+ # @attention: None
7
+ # @Author: NGC13009
8
+ # @History: 2026-05-07 Create
9
+
10
+ import ctypes
11
+ from typing import Any, Dict, List, Optional, Tuple
12
+
13
+ # Character types
14
+ _CHAR_TYPES = {
15
+ 'char': ctypes.c_byte,
16
+ 'signed char': ctypes.c_byte,
17
+ 'unsigned char': ctypes.c_ubyte,
18
+ }
19
+
20
+ # Integer types (short/int/long)
21
+ _INTEGER_TYPES = {
22
+ 'short': ctypes.c_short,
23
+ 'unsigned short': ctypes.c_ushort,
24
+ 'short int': ctypes.c_short,
25
+ 'unsigned short int': ctypes.c_ushort,
26
+ 'int': ctypes.c_int,
27
+ 'signed int': ctypes.c_int,
28
+ 'unsigned int': ctypes.c_uint,
29
+ 'long': ctypes.c_long,
30
+ 'unsigned long': ctypes.c_ulong,
31
+ 'long int': ctypes.c_long,
32
+ 'unsigned long int': ctypes.c_ulong,
33
+ 'long long': ctypes.c_longlong,
34
+ 'unsigned long long': ctypes.c_ulonglong,
35
+ 'long long int': ctypes.c_longlong,
36
+ 'unsigned long long int': ctypes.c_ulonglong,
37
+ }
38
+
39
+ # Floating-point types
40
+ _FLOAT_TYPES = {
41
+ 'float': ctypes.c_float,
42
+ 'double': ctypes.c_double,
43
+ 'long double': ctypes.c_longdouble,
44
+ }
45
+
46
+ # Fixed-width types (stdint.h)
47
+ _STDINT_TYPES = {
48
+ 'int8_t': ctypes.c_int8,
49
+ 'uint8_t': ctypes.c_uint8,
50
+ 'int16_t': ctypes.c_int16,
51
+ 'uint16_t': ctypes.c_uint16,
52
+ 'int32_t': ctypes.c_int32,
53
+ 'uint32_t': ctypes.c_uint32,
54
+ 'int64_t': ctypes.c_int64,
55
+ 'uint64_t': ctypes.c_uint64,
56
+ }
57
+
58
+ # Alias mapping
59
+ _ALIAS_TYPES = {
60
+ 'float32_t': ctypes.c_float,
61
+ 'float64_t': ctypes.c_double,
62
+ }
63
+
64
+ # Final aggregation
65
+ TYPE_MAP: Dict[str, Any] = {**_CHAR_TYPES, **_INTEGER_TYPES, **_FLOAT_TYPES, **_STDINT_TYPES, **_ALIAS_TYPES}
66
+
67
+ # Value‑range definitions for strict value‑domain validation
68
+ #
69
+ # Each entry is a (min, max) tuple for the corresponding ctypes type.
70
+ # Used by _validate_value() in cstruct_parser.py when pack(strict=True).
71
+ # Floating‑point types and struct types are omitted — they are not
72
+ # subject to integer‑range validation.
73
+ #
74
+ # Overflow definitions follow the C standard two's‑complement ranges.
75
+ # For unsigned types, min is always 0.
76
+
77
+ _VALUE_RANGE_DEFS: Dict[Any, Tuple[int, int]] = {
78
+ # Character types
79
+ ctypes.c_byte: (-128, 127),
80
+ ctypes.c_ubyte: (0, 255),
81
+
82
+ # Short types
83
+ ctypes.c_short: (-32768, 32767),
84
+ ctypes.c_ushort: (0, 65535),
85
+
86
+ # Int types
87
+ ctypes.c_int: (-2147483648, 2147483647),
88
+ ctypes.c_uint: (0, 4294967295),
89
+
90
+ # Long types (platform‑dependent; these are Win64 / LP64 values)
91
+ ctypes.c_long: (-9223372036854775808, 9223372036854775807),
92
+ ctypes.c_ulong: (0, 18446744073709551615),
93
+
94
+ # Long‑long types
95
+ ctypes.c_longlong: (-9223372036854775808, 9223372036854775807),
96
+ ctypes.c_ulonglong: (0, 18446744073709551615),
97
+
98
+ # Fixed‑width stdint types
99
+ ctypes.c_int8: (-128, 127),
100
+ ctypes.c_uint8: (0, 255),
101
+ ctypes.c_int16: (-32768, 32767),
102
+ ctypes.c_uint16: (0, 65535),
103
+ ctypes.c_int32: (-2147483648, 2147483647),
104
+ ctypes.c_uint32: (0, 4294967295),
105
+ ctypes.c_int64: (-9223372036854775808, 9223372036854775807),
106
+ ctypes.c_uint64: (0, 18446744073709551615),
107
+ }
108
+
109
+
110
+ def get_value_range(ctypes_type: Any) -> Optional[Tuple[int, int]]:
111
+ """Return the ``(min, max)`` integer range for *ctypes_type*, or ``None``
112
+ if the type has no range constraint (floats, structs, pointers, etc.)."""
113
+ return _VALUE_RANGE_DEFS.get(ctypes_type)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pycstructdataparser-lib
3
- Version: 1.0.1
3
+ Version: 1.0.3
4
4
  Summary: Parse C/C++ struct definitions from header text and serialize/deserialize binary data with ctypes — no compilation needed
5
5
  Author: NGC13009
6
6
  License-Expression: GPL-3.0-only
@@ -33,7 +33,7 @@ Dynamic: license-file
33
33
  </p>
34
34
 
35
35
  <p align="center">
36
- [<a href="README_CN.md">中文说明书</a>] | English
36
+ [<a href="https://github.com/NGC13009/pycstructdataparser_lib/blob/main/README_CN.md">中文说明书</a>] | English
37
37
  </p>
38
38
 
39
39
  **Automatically parse struct definitions directly from C/C++ header text and process binary data in Python** — No compilation required, no manual `ctypes.Structure` subclass writing needed, zero boilerplate code. Seamless data transfer between C/C++ and Python with automated data unpacking/packing.
@@ -107,7 +107,7 @@ pack output: 21 bytes
107
107
  - **Nested Structs**: Supports struct types used as fields within other structs
108
108
  - **Serialization (pack)**: Converts Python dictionaries into C struct binary bytes
109
109
  - **Deserialization (unpack)**: Parses raw bytes back into nested Python dictionaries
110
- - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing
110
+ - **Strict Mode**: Optionally validates that all non-bitfield fields are present during packing, plus value‑domain checks (bitfield overflow, integer type range)
111
111
 
112
112
  ## Installation Methods
113
113
 
@@ -239,7 +239,10 @@ Serializes a Python dictionary into binary bytes corresponding to a C struct.
239
239
  - `parser`: A `CStructParser` instance on which `parse()` has been called.
240
240
  - `struct_name`: The target struct type name.
241
241
  - `data_dict`: A dictionary mapping field names to field values.
242
- - `strict`: If `True` (default), raises `ValueError` when non-bitfield fields are missing; if `False`, missing fields are silently filled with 0.
242
+ - `strict`: If `True` (default):
243
+ - Raises `ValueError` when a **non-bitfield** field is missing.
244
+ - Performs **value‑domain validation** on each scalar value: bitfield values must be within `[0, 2^width - 1]`; integer types must be within their type min/max (e.g. `uint8_t` → `[0, 255]`, `int32_t` → `[-2147483648, 2147483647]`); floating‑point and struct types are not range‑checked.
245
+ - If `False`, missing fields are silently filled with 0 and no value‑domain checks are performed.
243
246
 
244
247
  ### `unpack(parser, struct_name, data)`
245
248
 
@@ -295,4 +298,5 @@ Deserializes raw binary bytes into a nested Python dictionary.
295
298
 
296
299
  ## License
297
300
 
298
- This project is licensed under [GPLv3.0](LICENSE).
301
+ This project is licensed under [GPLv3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
302
+ [NGC13009/pycstructdataparser_lib.git](https://github.com/NGC13009/pycstructdataparser_lib.git)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "pycstructdataparser-lib"
7
- version = "1.0.1"
7
+ version = "1.0.3"
8
8
  description = "Parse C/C++ struct definitions from header text and serialize/deserialize binary data with ctypes — no compilation needed"
9
9
  readme = "README.md"
10
10
  license = "GPL-3.0-only"
@@ -1,65 +0,0 @@
1
- # coding = utf-8
2
- # Arch = manyArch
3
- #
4
- # @File name: type_map_def.py
5
- # @brief: Type definition file for Python struct parser
6
- # @attention: None
7
- # @Author: NGC13009
8
- # @History: 2026-05-07 Create
9
-
10
- import ctypes
11
- from typing import Any, Dict, List, Optional, Tuple
12
-
13
- # Character types
14
- _CHAR_TYPES = {
15
- 'char': ctypes.c_byte,
16
- 'signed char': ctypes.c_byte,
17
- 'unsigned char': ctypes.c_ubyte,
18
- }
19
-
20
- # Integer types (short/int/long)
21
- _INTEGER_TYPES = {
22
- 'short': ctypes.c_short,
23
- 'unsigned short': ctypes.c_ushort,
24
- 'short int': ctypes.c_short,
25
- 'unsigned short int': ctypes.c_ushort,
26
- 'int': ctypes.c_int,
27
- 'signed int': ctypes.c_int,
28
- 'unsigned int': ctypes.c_uint,
29
- 'long': ctypes.c_long,
30
- 'unsigned long': ctypes.c_ulong,
31
- 'long int': ctypes.c_long,
32
- 'unsigned long int': ctypes.c_ulong,
33
- 'long long': ctypes.c_longlong,
34
- 'unsigned long long': ctypes.c_ulonglong,
35
- 'long long int': ctypes.c_longlong,
36
- 'unsigned long long int': ctypes.c_ulonglong,
37
- }
38
-
39
- # Floating-point types
40
- _FLOAT_TYPES = {
41
- 'float': ctypes.c_float,
42
- 'double': ctypes.c_double,
43
- 'long double': ctypes.c_longdouble,
44
- }
45
-
46
- # Fixed-width types (stdint.h)
47
- _STDINT_TYPES = {
48
- 'int8_t': ctypes.c_int8,
49
- 'uint8_t': ctypes.c_uint8,
50
- 'int16_t': ctypes.c_int16,
51
- 'uint16_t': ctypes.c_uint16,
52
- 'int32_t': ctypes.c_int32,
53
- 'uint32_t': ctypes.c_uint32,
54
- 'int64_t': ctypes.c_int64,
55
- 'uint64_t': ctypes.c_uint64,
56
- }
57
-
58
- # Alias mapping
59
- _ALIAS_TYPES = {
60
- 'float32_t': ctypes.c_float,
61
- 'float64_t': ctypes.c_double,
62
- }
63
-
64
- # Final aggregation
65
- TYPE_MAP: Dict[str, Any] = {**_CHAR_TYPES, **_INTEGER_TYPES, **_FLOAT_TYPES, **_STDINT_TYPES, **_ALIAS_TYPES}