pycstructdataparser-lib 1.0.0__tar.gz → 1.0.2__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.
- pycstructdataparser_lib-1.0.2/PKG-INFO +299 -0
- pycstructdataparser_lib-1.0.2/README.md +273 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/ctype_map_def.py +0 -4
- pycstructdataparser_lib-1.0.2/pycstructdataparser_lib.egg-info/PKG-INFO +299 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/pyproject.toml +1 -1
- pycstructdataparser_lib-1.0.0/PKG-INFO +0 -168
- pycstructdataparser_lib-1.0.0/README.md +0 -142
- pycstructdataparser_lib-1.0.0/pycstructdataparser_lib.egg-info/PKG-INFO +0 -168
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/LICENSE +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/MANIFEST.in +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/cstruct_parser.py +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/pycstructdataparser_lib.egg-info/SOURCES.txt +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/pycstructdataparser_lib.egg-info/dependency_links.txt +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/pycstructdataparser_lib.egg-info/top_level.txt +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/pycstructdataparser_lib.py +0 -0
- {pycstructdataparser_lib-1.0.0 → pycstructdataparser_lib-1.0.2}/setup.cfg +0 -0
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pycstructdataparser-lib
|
|
3
|
+
Version: 1.0.2
|
|
4
|
+
Summary: Parse C/C++ struct definitions from header text and serialize/deserialize binary data with ctypes — no compilation needed
|
|
5
|
+
Author: NGC13009
|
|
6
|
+
License-Expression: GPL-3.0-only
|
|
7
|
+
Project-URL: Homepage, https://github.com/NGC13009/cStructDataParser
|
|
8
|
+
Project-URL: Source, https://github.com/NGC13009/cStructDataParser
|
|
9
|
+
Project-URL: Tracker, https://github.com/NGC13009/cStructDataParser/issues
|
|
10
|
+
Keywords: ctypes,C struct,binary,serialization,deserialization,header parser,pack,unpack,c-struct,binary-parser,embedded,protocol,pragma-pack,bitfield,struct-parser
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: System :: Archiving :: Packaging
|
|
22
|
+
Requires-Python: >=3.8
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Dynamic: license-file
|
|
26
|
+
|
|
27
|
+
# pycstructdataparser-lib
|
|
28
|
+
|
|
29
|
+
<p align="center">
|
|
30
|
+
<a href="https://pypi.org/project/pycstructdataparser-lib/"><img src="https://img.shields.io/pypi/v/pycstructdataparser-lib" alt="PyPI"></a>
|
|
31
|
+
<a href="https://pypi.org/project/pycstructdataparser-lib/"><img src="https://img.shields.io/pypi/pyversions/pycstructdataparser-lib" alt="Python Versions"></a>
|
|
32
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
[<a href="https://github.com/NGC13009/pycstructdataparser_lib/blob/main/README_CN.md">中文说明书</a>] | English
|
|
37
|
+
</p>
|
|
38
|
+
|
|
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.
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
Specifically, this package uses Python's built-in `ctypes` module to parse struct definitions from C/C++ `.h` files and handle their corresponding binary data. You only need to provide the struct definition text from the header file to accomplish binary data serialization (pack) and deserialization (unpack).
|
|
44
|
+
|
|
45
|
+
With this module, users no longer need to rewrite Python CRUD code each time data field definitions change — packing/unpacking methods can be generated directly from `.h` files.
|
|
46
|
+
|
|
47
|
+
Ideal for data communication between Python and C/C++ programs, such as:
|
|
48
|
+
|
|
49
|
+
- TCP/UDP bitstream message transmission
|
|
50
|
+
- Local binary data file read/write (`.bin` files, `.dat` files, etc.)
|
|
51
|
+
- Project scenarios where data protocols change frequently
|
|
52
|
+
- Development stages with rapid protocol iteration
|
|
53
|
+
|
|
54
|
+
## Quick Start
|
|
55
|
+
|
|
56
|
+
Install directly from PyPI:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
pip install pycstructdataparser-lib
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then it's ready to use. The following code demonstrates the complete core workflow:
|
|
63
|
+
|
|
64
|
+
> Define struct → Parse → Pack → Unpack
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from pycstructdataparser_lib import CStructParser, pack, unpack
|
|
68
|
+
|
|
69
|
+
# 1. Prepare C struct definition text
|
|
70
|
+
header = '''
|
|
71
|
+
#pragma pack(push, 1)
|
|
72
|
+
typedef struct {
|
|
73
|
+
unsigned char id;
|
|
74
|
+
unsigned int value;
|
|
75
|
+
float data[4];
|
|
76
|
+
} MyStruct;
|
|
77
|
+
#pragma pack(pop)
|
|
78
|
+
'''
|
|
79
|
+
|
|
80
|
+
# 2. Parse the header file and register the struct type
|
|
81
|
+
parser = CStructParser()
|
|
82
|
+
parser.parse(header)
|
|
83
|
+
|
|
84
|
+
# 3. Pack a Python dictionary into binary bytes (serialization)
|
|
85
|
+
original = {'id': 10, 'value': 12345, 'data': [1.0, 2.0, 3.0, 4.0]}
|
|
86
|
+
raw = pack(parser, 'MyStruct', original)
|
|
87
|
+
print(f"pack output: {len(raw)} bytes")
|
|
88
|
+
|
|
89
|
+
# 4. Unpack binary bytes back into a Python dictionary (deserialization)
|
|
90
|
+
restored = unpack(parser, 'MyStruct', raw)
|
|
91
|
+
print(restored)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Expected output:
|
|
95
|
+
|
|
96
|
+
```powershell
|
|
97
|
+
pack output: 21 bytes
|
|
98
|
+
{'id': 10, 'value': 12345, 'data': [1.0, 2.0, 3.0, 4.0]}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Features
|
|
102
|
+
|
|
103
|
+
- **Bitfield Support**: Parses bitfield declarations in the form `unsigned char field : 2;`
|
|
104
|
+
- **C Header Parsing**: Recognizes `typedef struct { … } TypeName;` blocks, automatically extracting struct definitions
|
|
105
|
+
- **`#pragma pack` Support**: Correctly handles `#pragma pack(push, N)` and `#pragma pack(pop)` alignment stack
|
|
106
|
+
- **Array Support**: Handles fixed-size arrays, e.g., `Type array[10];`
|
|
107
|
+
- **Nested Structs**: Supports struct types used as fields within other structs
|
|
108
|
+
- **Serialization (pack)**: Converts Python dictionaries into C struct binary bytes
|
|
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
|
|
111
|
+
|
|
112
|
+
## Installation Methods
|
|
113
|
+
|
|
114
|
+
### Method 1: PyPI Installation (Recommended)
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
pip install pycstructdataparser-lib
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
### Method 2: Local Use from GitHub Repository
|
|
121
|
+
|
|
122
|
+
After cloning the repository, you can install it in editable mode or add the repository directory to the Python search path.
|
|
123
|
+
|
|
124
|
+
**Editable Mode Installation (Recommended)**:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
git clone https://github.com/NGC13009/pycstructdataparser_lib.git
|
|
128
|
+
cd pycstructdataparser_lib
|
|
129
|
+
pip install -e .
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Adding the Path Directly in Code** (no installation required):
|
|
133
|
+
|
|
134
|
+
```python
|
|
135
|
+
import sys
|
|
136
|
+
sys.path.insert(0, '/path/to/pycstructdataparser_lib')
|
|
137
|
+
from pycstructdataparser_lib import CStructParser, pack, unpack
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
> This repository uses a flat module layout — the root directory (containing `__init__.py`) is itself the package directory. Placing this directory in the Python package search path (`sys.path`) allows normal imports via `from pycstructdataparser_lib import ...`.
|
|
141
|
+
> Known Issue⚠: Due to this flat structure, the source code may require minor adjustments before use, such as renaming the folder, removing the `pycstructdataparser_lib.py` file, and migrating its content into `__init__.py`.
|
|
142
|
+
|
|
143
|
+
### Method 3: Install via .whl Package
|
|
144
|
+
|
|
145
|
+
Download the latest `.whl` file from the [Release page](https://github.com/NGC13009/pycstructdataparser_lib/releases), then run:
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
pip install pycstructdataparser_lib-*.whl
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Method 4: Manual Build
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
pip install build # Build toolchain
|
|
155
|
+
git clone https://github.com/NGC13009/pycstructdataparser_lib.git
|
|
156
|
+
cd pycstructdataparser_lib
|
|
157
|
+
python -m build
|
|
158
|
+
pip install dist/pycstructdataparser_lib-*.whl
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Usage Guide
|
|
162
|
+
|
|
163
|
+
### Core API
|
|
164
|
+
|
|
165
|
+
| Function / Method | Description |
|
|
166
|
+
|------------------|-------------|
|
|
167
|
+
| `CStructParser()` | Creates a parser instance |
|
|
168
|
+
| `parser.parse(header_content)` | Parses a C/C++ header string, registering the struct types defined within |
|
|
169
|
+
| `parser.get_struct_class(name)` | Retrieves the corresponding `ctypes.Structure` subclass by type name |
|
|
170
|
+
| `parser.struct_classes` | Returns a dictionary of all registered struct classes (property) |
|
|
171
|
+
| `pack(parser, struct_name, data_dict, strict=True)` | Serializes a Python dictionary into a binary byte sequence |
|
|
172
|
+
| `unpack(parser, struct_name, data)` | Deserializes a binary byte sequence into a Python dictionary |
|
|
173
|
+
|
|
174
|
+
### Basic Usage: Directly Processing Header Strings and Binary Streams
|
|
175
|
+
|
|
176
|
+
The core workflow of this library is: pass the struct definition text from a C header file to `CStructParser.parse()`, then use `pack()` and `unpack()` to convert between Python dictionaries and binary bytes. See the "Quick Start" example above for details.
|
|
177
|
+
|
|
178
|
+
### Reading/Writing Local Binary Files
|
|
179
|
+
|
|
180
|
+
Using `test_roundtrip.py` as a reference, a typical workflow proceeds as follows:
|
|
181
|
+
|
|
182
|
+
1. Read a header file → Parse it with `CStructParser`
|
|
183
|
+
2. Construct a Python dictionary as test data
|
|
184
|
+
3. Call `pack()` to serialize the dictionary into binary bytes, then write to a `.bin` file
|
|
185
|
+
4. Read bytes from the `.bin` file, call `unpack()` to restore them into a dictionary
|
|
186
|
+
5. Compare the original data with the restored data to verify consistency
|
|
187
|
+
|
|
188
|
+
For detailed implementation, refer to the [`test_roundtrip.py`](test_roundtrip.py) file in this repository. This file also demonstrates operations such as saving a dictionary as a JSON file and packing after loading from JSON.
|
|
189
|
+
|
|
190
|
+
### Application in Network Communication
|
|
191
|
+
|
|
192
|
+
In TCP/UDP programming, the output of `pack()` can be sent directly to a socket, and bytes received from a socket can be passed directly to `unpack()`. Since the library's inputs and outputs are all of `bytes` type, it is naturally compatible with standard socket interfaces. Business-specific logic (such as handling message boundaries and byte order negotiation) must be implemented by the user according to the actual protocol.
|
|
193
|
+
|
|
194
|
+
## API Reference
|
|
195
|
+
|
|
196
|
+
### `CStructParser`
|
|
197
|
+
|
|
198
|
+
```python
|
|
199
|
+
class CStructParser:
|
|
200
|
+
def __init__(self) -> None: ...
|
|
201
|
+
def parse(self, header_content: str) -> None: ...
|
|
202
|
+
def get_struct_class(self, name: str) -> type: ...
|
|
203
|
+
@property
|
|
204
|
+
def struct_classes(self) -> Dict[str, type]: ...
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**`parse(header_content)`**
|
|
208
|
+
|
|
209
|
+
Parses a C/C++ header string, extracting all `typedef struct { … } Name;` blocks and dynamically creating a `ctypes.Structure` subclass for each struct.
|
|
210
|
+
|
|
211
|
+
Supported features:
|
|
212
|
+
|
|
213
|
+
- C-style comments (`//` and `/* … */`)
|
|
214
|
+
- `#pragma pack(push, N)` / `#pragma pack(pop)` alignment directives
|
|
215
|
+
- Standard C basic types, bitfields, fixed-size arrays
|
|
216
|
+
- Nested named structs (forward references are handled preferentially)
|
|
217
|
+
|
|
218
|
+
**`get_struct_class(name)`**
|
|
219
|
+
|
|
220
|
+
Returns the `ctypes.Structure` subclass corresponding to the specified name. Raises `ValueError` if the name is not registered.
|
|
221
|
+
|
|
222
|
+
**`struct_classes`**
|
|
223
|
+
|
|
224
|
+
Returns a dictionary of all registered struct classes, with struct names as keys and corresponding `ctypes.Structure` subclasses as values.
|
|
225
|
+
|
|
226
|
+
### `pack(parser, struct_name, data_dict, strict=True)`
|
|
227
|
+
|
|
228
|
+
```python
|
|
229
|
+
def pack(
|
|
230
|
+
parser: CStructParser,
|
|
231
|
+
struct_name: str,
|
|
232
|
+
data_dict: Dict[str, Any],
|
|
233
|
+
strict: bool = True,
|
|
234
|
+
) -> bytes: ...
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Serializes a Python dictionary into binary bytes corresponding to a C struct.
|
|
238
|
+
|
|
239
|
+
- `parser`: A `CStructParser` instance on which `parse()` has been called.
|
|
240
|
+
- `struct_name`: The target struct type name.
|
|
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.
|
|
243
|
+
|
|
244
|
+
### `unpack(parser, struct_name, data)`
|
|
245
|
+
|
|
246
|
+
```python
|
|
247
|
+
def unpack(
|
|
248
|
+
parser: CStructParser,
|
|
249
|
+
struct_name: str,
|
|
250
|
+
data: bytes,
|
|
251
|
+
) -> Dict[str, Any]: ...
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Deserializes raw binary bytes into a nested Python dictionary.
|
|
255
|
+
|
|
256
|
+
- `parser`: A `CStructParser` instance on which `parse()` has been called.
|
|
257
|
+
- `struct_name`: The target struct type name.
|
|
258
|
+
- `data`: The binary byte sequence to decode.
|
|
259
|
+
|
|
260
|
+
## Supported C Type Mapping
|
|
261
|
+
|
|
262
|
+
| C Type | ctypes Type |
|
|
263
|
+
|--------|-------------|
|
|
264
|
+
| `char`, `signed char` | `c_byte` |
|
|
265
|
+
| `unsigned char` | `c_ubyte` |
|
|
266
|
+
| `short`, `short int` | `c_short` |
|
|
267
|
+
| `unsigned short` | `c_ushort` |
|
|
268
|
+
| `int`, `signed int` | `c_int` |
|
|
269
|
+
| `unsigned int` | `c_uint` |
|
|
270
|
+
| `long`, `long int` | `c_long` |
|
|
271
|
+
| `unsigned long` | `c_ulong` |
|
|
272
|
+
| `long long`, `long long int` | `c_longlong` |
|
|
273
|
+
| `unsigned long long` | `c_ulonglong` |
|
|
274
|
+
| `float` | `c_float` |
|
|
275
|
+
| `double` | `c_double` |
|
|
276
|
+
| `long double` | `c_longdouble` |
|
|
277
|
+
| `int8_t` .. `uint64_t` | `c_int8` .. `c_uint64` |
|
|
278
|
+
| `float32_t` | `c_float` |
|
|
279
|
+
| `float64_t` | `c_double` |
|
|
280
|
+
|
|
281
|
+
## Comparison with Common Approaches
|
|
282
|
+
|
|
283
|
+
| Feature | `struct` module | `ctypes.Structure` | `construct` library | cStructDataParser |
|
|
284
|
+
|---------|:---:|:---:|:---:|:---:|
|
|
285
|
+
| Direct parsing from header text | ❌ | ❌ | ❌ | ✅ |
|
|
286
|
+
| Automatic `#pragma pack` alignment | ❌ | ❌ | ❌ | ✅ |
|
|
287
|
+
| Bitfield support | ❌ | ✅ | ✅ | ✅ |
|
|
288
|
+
| Nested structs | ❌ | Manual writing required | ✅ | ✅ |
|
|
289
|
+
| No external dependencies (pure Python) | ✅ | ✅ | ✅ | ✅ |
|
|
290
|
+
|
|
291
|
+
## Known Limitations
|
|
292
|
+
|
|
293
|
+
- Currently only supports **little-endian** byte order; big-endian scenarios are not yet handled
|
|
294
|
+
- If the device uses big-endian order, additional byte order processing is needed before/after calling `pack()` / `unpack()`
|
|
295
|
+
|
|
296
|
+
## License
|
|
297
|
+
|
|
298
|
+
This project is licensed under [GPLv3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
|
299
|
+
[NGC13009/pycstructdataparser_lib.git](https://github.com/NGC13009/pycstructdataparser_lib.git)
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
# pycstructdataparser-lib
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://pypi.org/project/pycstructdataparser-lib/"><img src="https://img.shields.io/pypi/v/pycstructdataparser-lib" alt="PyPI"></a>
|
|
5
|
+
<a href="https://pypi.org/project/pycstructdataparser-lib/"><img src="https://img.shields.io/pypi/pyversions/pycstructdataparser-lib" alt="Python Versions"></a>
|
|
6
|
+
<a href="LICENSE"><img src="https://img.shields.io/badge/License-GPLv3-blue.svg" alt="License: GPL v3"></a>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
[<a href="https://github.com/NGC13009/pycstructdataparser_lib/blob/main/README_CN.md">中文说明书</a>] | English
|
|
11
|
+
</p>
|
|
12
|
+
|
|
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.
|
|
14
|
+
|
|
15
|
+
## Overview
|
|
16
|
+
|
|
17
|
+
Specifically, this package uses Python's built-in `ctypes` module to parse struct definitions from C/C++ `.h` files and handle their corresponding binary data. You only need to provide the struct definition text from the header file to accomplish binary data serialization (pack) and deserialization (unpack).
|
|
18
|
+
|
|
19
|
+
With this module, users no longer need to rewrite Python CRUD code each time data field definitions change — packing/unpacking methods can be generated directly from `.h` files.
|
|
20
|
+
|
|
21
|
+
Ideal for data communication between Python and C/C++ programs, such as:
|
|
22
|
+
|
|
23
|
+
- TCP/UDP bitstream message transmission
|
|
24
|
+
- Local binary data file read/write (`.bin` files, `.dat` files, etc.)
|
|
25
|
+
- Project scenarios where data protocols change frequently
|
|
26
|
+
- Development stages with rapid protocol iteration
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
Install directly from PyPI:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install pycstructdataparser-lib
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Then it's ready to use. The following code demonstrates the complete core workflow:
|
|
37
|
+
|
|
38
|
+
> Define struct → Parse → Pack → Unpack
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from pycstructdataparser_lib import CStructParser, pack, unpack
|
|
42
|
+
|
|
43
|
+
# 1. Prepare C struct definition text
|
|
44
|
+
header = '''
|
|
45
|
+
#pragma pack(push, 1)
|
|
46
|
+
typedef struct {
|
|
47
|
+
unsigned char id;
|
|
48
|
+
unsigned int value;
|
|
49
|
+
float data[4];
|
|
50
|
+
} MyStruct;
|
|
51
|
+
#pragma pack(pop)
|
|
52
|
+
'''
|
|
53
|
+
|
|
54
|
+
# 2. Parse the header file and register the struct type
|
|
55
|
+
parser = CStructParser()
|
|
56
|
+
parser.parse(header)
|
|
57
|
+
|
|
58
|
+
# 3. Pack a Python dictionary into binary bytes (serialization)
|
|
59
|
+
original = {'id': 10, 'value': 12345, 'data': [1.0, 2.0, 3.0, 4.0]}
|
|
60
|
+
raw = pack(parser, 'MyStruct', original)
|
|
61
|
+
print(f"pack output: {len(raw)} bytes")
|
|
62
|
+
|
|
63
|
+
# 4. Unpack binary bytes back into a Python dictionary (deserialization)
|
|
64
|
+
restored = unpack(parser, 'MyStruct', raw)
|
|
65
|
+
print(restored)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Expected output:
|
|
69
|
+
|
|
70
|
+
```powershell
|
|
71
|
+
pack output: 21 bytes
|
|
72
|
+
{'id': 10, 'value': 12345, 'data': [1.0, 2.0, 3.0, 4.0]}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Features
|
|
76
|
+
|
|
77
|
+
- **Bitfield Support**: Parses bitfield declarations in the form `unsigned char field : 2;`
|
|
78
|
+
- **C Header Parsing**: Recognizes `typedef struct { … } TypeName;` blocks, automatically extracting struct definitions
|
|
79
|
+
- **`#pragma pack` Support**: Correctly handles `#pragma pack(push, N)` and `#pragma pack(pop)` alignment stack
|
|
80
|
+
- **Array Support**: Handles fixed-size arrays, e.g., `Type array[10];`
|
|
81
|
+
- **Nested Structs**: Supports struct types used as fields within other structs
|
|
82
|
+
- **Serialization (pack)**: Converts Python dictionaries into C struct binary bytes
|
|
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
|
|
85
|
+
|
|
86
|
+
## Installation Methods
|
|
87
|
+
|
|
88
|
+
### Method 1: PyPI Installation (Recommended)
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install pycstructdataparser-lib
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Method 2: Local Use from GitHub Repository
|
|
95
|
+
|
|
96
|
+
After cloning the repository, you can install it in editable mode or add the repository directory to the Python search path.
|
|
97
|
+
|
|
98
|
+
**Editable Mode Installation (Recommended)**:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
git clone https://github.com/NGC13009/pycstructdataparser_lib.git
|
|
102
|
+
cd pycstructdataparser_lib
|
|
103
|
+
pip install -e .
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Adding the Path Directly in Code** (no installation required):
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import sys
|
|
110
|
+
sys.path.insert(0, '/path/to/pycstructdataparser_lib')
|
|
111
|
+
from pycstructdataparser_lib import CStructParser, pack, unpack
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
> This repository uses a flat module layout — the root directory (containing `__init__.py`) is itself the package directory. Placing this directory in the Python package search path (`sys.path`) allows normal imports via `from pycstructdataparser_lib import ...`.
|
|
115
|
+
> Known Issue⚠: Due to this flat structure, the source code may require minor adjustments before use, such as renaming the folder, removing the `pycstructdataparser_lib.py` file, and migrating its content into `__init__.py`.
|
|
116
|
+
|
|
117
|
+
### Method 3: Install via .whl Package
|
|
118
|
+
|
|
119
|
+
Download the latest `.whl` file from the [Release page](https://github.com/NGC13009/pycstructdataparser_lib/releases), then run:
|
|
120
|
+
|
|
121
|
+
```bash
|
|
122
|
+
pip install pycstructdataparser_lib-*.whl
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Method 4: Manual Build
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install build # Build toolchain
|
|
129
|
+
git clone https://github.com/NGC13009/pycstructdataparser_lib.git
|
|
130
|
+
cd pycstructdataparser_lib
|
|
131
|
+
python -m build
|
|
132
|
+
pip install dist/pycstructdataparser_lib-*.whl
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Usage Guide
|
|
136
|
+
|
|
137
|
+
### Core API
|
|
138
|
+
|
|
139
|
+
| Function / Method | Description |
|
|
140
|
+
|------------------|-------------|
|
|
141
|
+
| `CStructParser()` | Creates a parser instance |
|
|
142
|
+
| `parser.parse(header_content)` | Parses a C/C++ header string, registering the struct types defined within |
|
|
143
|
+
| `parser.get_struct_class(name)` | Retrieves the corresponding `ctypes.Structure` subclass by type name |
|
|
144
|
+
| `parser.struct_classes` | Returns a dictionary of all registered struct classes (property) |
|
|
145
|
+
| `pack(parser, struct_name, data_dict, strict=True)` | Serializes a Python dictionary into a binary byte sequence |
|
|
146
|
+
| `unpack(parser, struct_name, data)` | Deserializes a binary byte sequence into a Python dictionary |
|
|
147
|
+
|
|
148
|
+
### Basic Usage: Directly Processing Header Strings and Binary Streams
|
|
149
|
+
|
|
150
|
+
The core workflow of this library is: pass the struct definition text from a C header file to `CStructParser.parse()`, then use `pack()` and `unpack()` to convert between Python dictionaries and binary bytes. See the "Quick Start" example above for details.
|
|
151
|
+
|
|
152
|
+
### Reading/Writing Local Binary Files
|
|
153
|
+
|
|
154
|
+
Using `test_roundtrip.py` as a reference, a typical workflow proceeds as follows:
|
|
155
|
+
|
|
156
|
+
1. Read a header file → Parse it with `CStructParser`
|
|
157
|
+
2. Construct a Python dictionary as test data
|
|
158
|
+
3. Call `pack()` to serialize the dictionary into binary bytes, then write to a `.bin` file
|
|
159
|
+
4. Read bytes from the `.bin` file, call `unpack()` to restore them into a dictionary
|
|
160
|
+
5. Compare the original data with the restored data to verify consistency
|
|
161
|
+
|
|
162
|
+
For detailed implementation, refer to the [`test_roundtrip.py`](test_roundtrip.py) file in this repository. This file also demonstrates operations such as saving a dictionary as a JSON file and packing after loading from JSON.
|
|
163
|
+
|
|
164
|
+
### Application in Network Communication
|
|
165
|
+
|
|
166
|
+
In TCP/UDP programming, the output of `pack()` can be sent directly to a socket, and bytes received from a socket can be passed directly to `unpack()`. Since the library's inputs and outputs are all of `bytes` type, it is naturally compatible with standard socket interfaces. Business-specific logic (such as handling message boundaries and byte order negotiation) must be implemented by the user according to the actual protocol.
|
|
167
|
+
|
|
168
|
+
## API Reference
|
|
169
|
+
|
|
170
|
+
### `CStructParser`
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
class CStructParser:
|
|
174
|
+
def __init__(self) -> None: ...
|
|
175
|
+
def parse(self, header_content: str) -> None: ...
|
|
176
|
+
def get_struct_class(self, name: str) -> type: ...
|
|
177
|
+
@property
|
|
178
|
+
def struct_classes(self) -> Dict[str, type]: ...
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**`parse(header_content)`**
|
|
182
|
+
|
|
183
|
+
Parses a C/C++ header string, extracting all `typedef struct { … } Name;` blocks and dynamically creating a `ctypes.Structure` subclass for each struct.
|
|
184
|
+
|
|
185
|
+
Supported features:
|
|
186
|
+
|
|
187
|
+
- C-style comments (`//` and `/* … */`)
|
|
188
|
+
- `#pragma pack(push, N)` / `#pragma pack(pop)` alignment directives
|
|
189
|
+
- Standard C basic types, bitfields, fixed-size arrays
|
|
190
|
+
- Nested named structs (forward references are handled preferentially)
|
|
191
|
+
|
|
192
|
+
**`get_struct_class(name)`**
|
|
193
|
+
|
|
194
|
+
Returns the `ctypes.Structure` subclass corresponding to the specified name. Raises `ValueError` if the name is not registered.
|
|
195
|
+
|
|
196
|
+
**`struct_classes`**
|
|
197
|
+
|
|
198
|
+
Returns a dictionary of all registered struct classes, with struct names as keys and corresponding `ctypes.Structure` subclasses as values.
|
|
199
|
+
|
|
200
|
+
### `pack(parser, struct_name, data_dict, strict=True)`
|
|
201
|
+
|
|
202
|
+
```python
|
|
203
|
+
def pack(
|
|
204
|
+
parser: CStructParser,
|
|
205
|
+
struct_name: str,
|
|
206
|
+
data_dict: Dict[str, Any],
|
|
207
|
+
strict: bool = True,
|
|
208
|
+
) -> bytes: ...
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Serializes a Python dictionary into binary bytes corresponding to a C struct.
|
|
212
|
+
|
|
213
|
+
- `parser`: A `CStructParser` instance on which `parse()` has been called.
|
|
214
|
+
- `struct_name`: The target struct type name.
|
|
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.
|
|
217
|
+
|
|
218
|
+
### `unpack(parser, struct_name, data)`
|
|
219
|
+
|
|
220
|
+
```python
|
|
221
|
+
def unpack(
|
|
222
|
+
parser: CStructParser,
|
|
223
|
+
struct_name: str,
|
|
224
|
+
data: bytes,
|
|
225
|
+
) -> Dict[str, Any]: ...
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
Deserializes raw binary bytes into a nested Python dictionary.
|
|
229
|
+
|
|
230
|
+
- `parser`: A `CStructParser` instance on which `parse()` has been called.
|
|
231
|
+
- `struct_name`: The target struct type name.
|
|
232
|
+
- `data`: The binary byte sequence to decode.
|
|
233
|
+
|
|
234
|
+
## Supported C Type Mapping
|
|
235
|
+
|
|
236
|
+
| C Type | ctypes Type |
|
|
237
|
+
|--------|-------------|
|
|
238
|
+
| `char`, `signed char` | `c_byte` |
|
|
239
|
+
| `unsigned char` | `c_ubyte` |
|
|
240
|
+
| `short`, `short int` | `c_short` |
|
|
241
|
+
| `unsigned short` | `c_ushort` |
|
|
242
|
+
| `int`, `signed int` | `c_int` |
|
|
243
|
+
| `unsigned int` | `c_uint` |
|
|
244
|
+
| `long`, `long int` | `c_long` |
|
|
245
|
+
| `unsigned long` | `c_ulong` |
|
|
246
|
+
| `long long`, `long long int` | `c_longlong` |
|
|
247
|
+
| `unsigned long long` | `c_ulonglong` |
|
|
248
|
+
| `float` | `c_float` |
|
|
249
|
+
| `double` | `c_double` |
|
|
250
|
+
| `long double` | `c_longdouble` |
|
|
251
|
+
| `int8_t` .. `uint64_t` | `c_int8` .. `c_uint64` |
|
|
252
|
+
| `float32_t` | `c_float` |
|
|
253
|
+
| `float64_t` | `c_double` |
|
|
254
|
+
|
|
255
|
+
## Comparison with Common Approaches
|
|
256
|
+
|
|
257
|
+
| Feature | `struct` module | `ctypes.Structure` | `construct` library | cStructDataParser |
|
|
258
|
+
|---------|:---:|:---:|:---:|:---:|
|
|
259
|
+
| Direct parsing from header text | ❌ | ❌ | ❌ | ✅ |
|
|
260
|
+
| Automatic `#pragma pack` alignment | ❌ | ❌ | ❌ | ✅ |
|
|
261
|
+
| Bitfield support | ❌ | ✅ | ✅ | ✅ |
|
|
262
|
+
| Nested structs | ❌ | Manual writing required | ✅ | ✅ |
|
|
263
|
+
| No external dependencies (pure Python) | ✅ | ✅ | ✅ | ✅ |
|
|
264
|
+
|
|
265
|
+
## Known Limitations
|
|
266
|
+
|
|
267
|
+
- Currently only supports **little-endian** byte order; big-endian scenarios are not yet handled
|
|
268
|
+
- If the device uses big-endian order, additional byte order processing is needed before/after calling `pack()` / `unpack()`
|
|
269
|
+
|
|
270
|
+
## License
|
|
271
|
+
|
|
272
|
+
This project is licensed under [GPLv3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
|
|
273
|
+
[NGC13009/pycstructdataparser_lib.git](https://github.com/NGC13009/pycstructdataparser_lib.git)
|
|
@@ -10,10 +10,6 @@
|
|
|
10
10
|
import ctypes
|
|
11
11
|
from typing import Any, Dict, List, Optional, Tuple
|
|
12
12
|
|
|
13
|
-
# ============================================================================
|
|
14
|
-
# Type mapping — C type name → ctypes type
|
|
15
|
-
# ============================================================================
|
|
16
|
-
|
|
17
13
|
# Character types
|
|
18
14
|
_CHAR_TYPES = {
|
|
19
15
|
'char': ctypes.c_byte,
|