obstruct 0.2.15__tar.gz → 0.4.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.
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/workspace.xml +18 -3
- obstruct-0.4.0/BENCHMARK_TEST.md +183 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/PKG-INFO +1 -1
- obstruct-0.4.0/src/obstruct/__version__.py +1 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/base.py +5 -2
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/unpack.py +62 -35
- obstruct-0.4.0/test/test_speed.py +162 -0
- obstruct-0.2.15/src/obstruct/__version__.py +0 -1
- obstruct-0.2.15/test/test_speed.py +0 -101
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/.gitignore +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/misc.xml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/modules.xml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/obstruct.iml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/.idea/vcs.xml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/README.md +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/pyproject.toml +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/__init__.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/__init__.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/aot.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/pack.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/metadata_marks.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/methods.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/struct_type.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/__init__.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/base.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/types.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/types.pyi +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/test/test.py +0 -0
- {obstruct-0.2.15 → obstruct-0.4.0}/uv.lock +0 -0
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
<option name="autoReloadType" value="SELECTIVE" />
|
|
5
5
|
</component>
|
|
6
6
|
<component name="ChangeListManager">
|
|
7
|
-
<list default="true" id="b482b6b4-48ce-4be1-bf3c-a72ec254fa35" name="更改" comment="fix: 更新版本号至 0.2.
|
|
7
|
+
<list default="true" id="b482b6b4-48ce-4be1-bf3c-a72ec254fa35" name="更改" comment="fix: 更新版本号至 0.2.15,修复延迟加载不会应用到类属性的问题">
|
|
8
|
+
<change afterPath="$PROJECT_DIR$/BENCHMARK_TEST.md" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/src/obstruct/__version__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/__version__.py" afterDir="false" />
|
|
10
|
+
<change beforePath="$PROJECT_DIR$/src/obstruct/aot/base.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/base.py" afterDir="false" />
|
|
11
|
+
<change beforePath="$PROJECT_DIR$/src/obstruct/aot/unpack.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/unpack.py" afterDir="false" />
|
|
12
|
+
<change beforePath="$PROJECT_DIR$/test/test_speed.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/test_speed.py" afterDir="false" />
|
|
13
|
+
</list>
|
|
8
14
|
<option name="SHOW_DIALOG" value="false" />
|
|
9
15
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
10
16
|
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
@@ -276,7 +282,15 @@
|
|
|
276
282
|
<option name="project" value="LOCAL" />
|
|
277
283
|
<updated>1786969988797</updated>
|
|
278
284
|
</task>
|
|
279
|
-
<
|
|
285
|
+
<task id="LOCAL-00019" summary="fix: 更新版本号至 0.2.15,修复延迟加载不会应用到类属性的问题">
|
|
286
|
+
<option name="closed" value="true" />
|
|
287
|
+
<created>1786971691114</created>
|
|
288
|
+
<option name="number" value="00019" />
|
|
289
|
+
<option name="presentableId" value="LOCAL-00019" />
|
|
290
|
+
<option name="project" value="LOCAL" />
|
|
291
|
+
<updated>1786971691114</updated>
|
|
292
|
+
</task>
|
|
293
|
+
<option name="localTasksCounter" value="20" />
|
|
280
294
|
<servers />
|
|
281
295
|
</component>
|
|
282
296
|
<component name="TypeScriptGeneratedFilesManager">
|
|
@@ -311,6 +325,7 @@
|
|
|
311
325
|
<MESSAGE value="chore: 更新项目信息和 README 文档" />
|
|
312
326
|
<MESSAGE value="fix: 更新版本号至 0.2.13,修复类变量识别错误的问题" />
|
|
313
327
|
<MESSAGE value="fix: 更新版本号至 0.2.14,修复 __cendian__ 类变量识别错误的问题" />
|
|
314
|
-
<
|
|
328
|
+
<MESSAGE value="fix: 更新版本号至 0.2.15,修复延迟加载不会应用到类属性的问题" />
|
|
329
|
+
<option name="LAST_COMMIT_MESSAGE" value="fix: 更新版本号至 0.2.15,修复延迟加载不会应用到类属性的问题" />
|
|
315
330
|
</component>
|
|
316
331
|
</project>
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
## 基准测试性能分析报告
|
|
2
|
+
|
|
3
|
+
> 此报告由 **DeepSeek (offical)** 生成
|
|
4
|
+
|
|
5
|
+
> 测试配置:1000 steps/轮,128 轮采样,32 次去极值。
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## 一、测试结构与参数
|
|
10
|
+
|
|
11
|
+
| 项目 | 值 | 补充 |
|
|
12
|
+
|----------------|----------------|--------------------------------------------------|
|
|
13
|
+
| 简单结构大小 | 42 bytes | 12 个基本类型字段
|
|
14
|
+
| 复杂结构大小 | 484 bytes | 包含两个简单数据结构以及一个长度 100 的 int 数组
|
|
15
|
+
| 每轮迭代步数 | **1,000** | 无
|
|
16
|
+
| 结果实际采样数 | **64** | 无
|
|
17
|
+
| 总操作次数 | 64,000 次/方法 | 无
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 二、按耗时排序(从快到慢)
|
|
22
|
+
|
|
23
|
+
| 排名 | 测试名称 | 平均耗时 (ns) | 相对 Pack 倍数 |
|
|
24
|
+
|:----:|--------------------------------|:-------------:|:--------------:|
|
|
25
|
+
| 1 | **Pack** | **261.955** | 1.00x |
|
|
26
|
+
| 2 | **Update From** | 331.223 | 1.26x |
|
|
27
|
+
| 3 | **Update** | 377.706 | 1.44x |
|
|
28
|
+
| 4 | **Unpack From** | 501.742 | 1.92x |
|
|
29
|
+
| 5 | **Unpack** | 601.889 | 2.30x |
|
|
30
|
+
| 6 | **Complex Update** | 825.573 | 3.15x |
|
|
31
|
+
| 7 | **Complex Update From (100x)** | 965.261 | 3.68x |
|
|
32
|
+
| 8 | **Complex Unpack From (100x)** | 1,366.177 | 5.22x |
|
|
33
|
+
| 9 | **Complex Pack** | 1,469.102 | 5.61x |
|
|
34
|
+
| 10 | **Complex Unpack** | 1,986.633 | 7.58x |
|
|
35
|
+
| 11 | **Iter Update (100x)** | 29,666.533 | 113.25x |
|
|
36
|
+
| 12 | **Iter Unpack (100x)** | 42,771.392 | 163.28x |
|
|
37
|
+
| 13 | **Complex Iter Update (100x)** | 95,885.616 | 366.03x |
|
|
38
|
+
| 14 | **Complex Iter Unpack (100x)** | 144,415.439 | 551.30x |
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 三、按方法类型分组对比
|
|
43
|
+
|
|
44
|
+
### 3.1 简单方法
|
|
45
|
+
|
|
46
|
+
| 方法 | 耗时 (ns) | 相对 Pack | 备注 |
|
|
47
|
+
|-------------|:---------:|:---------:|-------------------------|
|
|
48
|
+
| Pack | 261.955 | 1.00x | 基准 |
|
|
49
|
+
| Update From | 331.223 | 1.26x | 复用 + 偏移,最快更新类 |
|
|
50
|
+
| Update | 377.706 | 1.44x | 复用对象 |
|
|
51
|
+
| Unpack From | 501.742 | 1.92x | 带偏移 |
|
|
52
|
+
| Unpack | 601.889 | 2.30x | 新建对象,最慢简单方法 |
|
|
53
|
+
|
|
54
|
+
### 3.2 复杂方法
|
|
55
|
+
|
|
56
|
+
| 方法 | 耗时 (ns) | 相对 Pack | 备注 |
|
|
57
|
+
|---------------------|:---------:|:---------:|---------------------------|
|
|
58
|
+
| Complex Update | 825.573 | 3.15x | 复用对象,钩子触发 |
|
|
59
|
+
| Complex Update From | 965.261 | 3.68x | 复用 + 偏移,钩子触发 |
|
|
60
|
+
| Complex Unpack From | 1,366.177 | 5.22x | 带偏移,钩子触发 |
|
|
61
|
+
| Complex Pack | 1,469.102 | 5.61x | 打包 |
|
|
62
|
+
| Complex Unpack | 1,986.633 | 7.58x | 新建对象 + 钩子,最慢单次 |
|
|
63
|
+
|
|
64
|
+
### 3.3 迭代方法
|
|
65
|
+
|
|
66
|
+
| 方法 | 耗时 (ns) | 相对 Pack | 每元素耗时 |
|
|
67
|
+
|----------------------------|:-----------:|:---------:|:-----------:|
|
|
68
|
+
| Iter Update (100x) | 29,666.533 | 113.25x | 296.67 ns |
|
|
69
|
+
| Iter Unpack (100x) | 42,771.392 | 163.28x | 427.71 ns |
|
|
70
|
+
| Complex Iter Update (100x) | 95,885.616 | 366.03x | 958.86 ns |
|
|
71
|
+
| Complex Iter Unpack (100x) | 144,415.439 | 551.30x | 1,444.15 ns |
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## 四、关键性能对比
|
|
76
|
+
|
|
77
|
+
### 4.1 复用 vs 新建对象
|
|
78
|
+
|
|
79
|
+
| 对比组 | 新建 (unpack) | 复用 (update) | 性能提升 |
|
|
80
|
+
|-------------|:--------------:|:-------------:|:----------:|
|
|
81
|
+
| 简单 | 601.889 ns | 377.706 ns | **+37.3%** |
|
|
82
|
+
| 简单 + 偏移 | 501.742 ns | 331.223 ns | **+34.0%** |
|
|
83
|
+
| 复杂 | 1,986.633 ns | 825.573 ns | **+58.5%** |
|
|
84
|
+
| 复杂 + 偏移 | 1,366.177 ns | 965.261 ns | **+29.3%** |
|
|
85
|
+
| 迭代 | 42,771.392 ns | 29,666.533 ns | **+30.6%** |
|
|
86
|
+
| 复杂迭代 | 144,415.439 ns | 95,885.616 ns | **+33.6%** |
|
|
87
|
+
|
|
88
|
+
### 4.2 简单 vs 复杂(同类型对比)
|
|
89
|
+
|
|
90
|
+
| 对比组 | 简单耗时 | 复杂耗时 | 倍数 |
|
|
91
|
+
|-------------|:-------------:|:--------------:|:---------:|
|
|
92
|
+
| Pack | 261.955 ns | 1,469.102 ns | **5.61x** |
|
|
93
|
+
| Unpack | 601.889 ns | 1,986.633 ns | **3.30x** |
|
|
94
|
+
| Update | 377.706 ns | 825.573 ns | **2.19x** |
|
|
95
|
+
| Update From | 331.223 ns | 965.261 ns | **2.91x** |
|
|
96
|
+
| Iter Unpack | 42,771.392 ns | 144,415.439 ns | **3.38x** |
|
|
97
|
+
| Iter Update | 29,666.533 ns | 95,885.616 ns | **3.23x** |
|
|
98
|
+
|
|
99
|
+
### 4.3 单次 vs 迭代(每元素对比)
|
|
100
|
+
|
|
101
|
+
迭代方法将 100 次操作合并为 1 次调用,按每元素耗时对比:
|
|
102
|
+
|
|
103
|
+
| 方法 | 单次调用 (ns) | 每元素 (ns) | 单次 vs 迭代每元素 |
|
|
104
|
+
|----------------------------|:-------------:|:-----------:|:----------------------:|
|
|
105
|
+
| Unpack | 601.889 | — | 迭代每元素快 **28.9%** |
|
|
106
|
+
| Iter Unpack (100x) | 42,771.392 | 427.714 | |
|
|
107
|
+
| Update | 377.706 | — | 迭代每元素快 **21.5%** |
|
|
108
|
+
| Iter Update (100x) | 29,666.533 | 296.665 | |
|
|
109
|
+
| Complex Unpack | 1,986.633 | — | 迭代每元素快 **27.3%** |
|
|
110
|
+
| Complex Iter Unpack (100x) | 144,415.439 | 1,444.154 | |
|
|
111
|
+
| Complex Update | 825.573 | — | 迭代每元素快 **16.2%** |
|
|
112
|
+
| Complex Iter Update (100x) | 95,885.616 | 958.856 | |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 五、基准测试汇总表
|
|
117
|
+
|
|
118
|
+
| # | 测试名称 | 耗时 (ns) | 耗时 (µs) | 相对 Pack | 相对同组最优 | 钩子 |
|
|
119
|
+
|:--:|--------------------------------|:-----------:|:---------:|:---------:|:------------:|:----:|
|
|
120
|
+
| 1 | **Pack** | 261.955 | 0.262 | 1.00x | 基准 | ❌ |
|
|
121
|
+
| 2 | **Update From** | 331.223 | 0.331 | 1.26x | 最快更新 | ❌ |
|
|
122
|
+
| 3 | **Update** | 377.706 | 0.378 | 1.44x | — | ❌ |
|
|
123
|
+
| 4 | **Unpack From** | 501.742 | 0.502 | 1.92x | 最快解包 | ❌ |
|
|
124
|
+
| 5 | **Unpack** | 601.889 | 0.602 | 2.30x | — | ❌ |
|
|
125
|
+
| 6 | **Complex Update** | 825.573 | 0.826 | 3.15x | 最快复杂 | ✅ |
|
|
126
|
+
| 7 | **Complex Update From** | 965.261 | 0.965 | 3.68x | — | ✅ |
|
|
127
|
+
| 8 | **Complex Unpack From** | 1,366.177 | 1.366 | 5.22x | — | ✅ |
|
|
128
|
+
| 9 | **Complex Pack** | 1,469.102 | 1.469 | 5.61x | — | ❌ |
|
|
129
|
+
| 10 | **Complex Unpack** | 1,986.633 | 1.987 | 7.58x | 最慢单次 | ✅ |
|
|
130
|
+
| 11 | **Iter Update (100x)** | 29,666.533 | 29.667 | 113.25x | 最快迭代 | ❌ |
|
|
131
|
+
| 12 | **Iter Unpack (100x)** | 42,771.392 | 42.771 | 163.28x | — | ❌ |
|
|
132
|
+
| 13 | **Complex Iter Update (100x)** | 95,885.616 | 95.886 | 366.03x | — | ✅ |
|
|
133
|
+
| 14 | **Complex Iter Unpack (100x)** | 144,415.439 | 144.415 | 551.30x | 最慢总榜 | ✅ |
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## 六、可视化性能分布
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Pack ████████████████████████████████████████████████████████████████████ 261.955 ns
|
|
141
|
+
Update From ██████████████████████████████████████████████████████████████████ 331.223 ns
|
|
142
|
+
Update █████████████████████████████████████████████████████████████████ 377.706 ns
|
|
143
|
+
Unpack From ████████████████████████████████████████████████████████████████████ 501.742 ns
|
|
144
|
+
Unpack ███████████████████████████████████████████████████████████████████ 601.889 ns
|
|
145
|
+
Complex Upd ███████████████████████████████████████████████████████████████████ 825.573 ns
|
|
146
|
+
Complex UpF ███████████████████████████████████████████████████████████████████ 965.261 ns
|
|
147
|
+
Complex UnF ███████████████████████████████████████████████████████████████████ 1,366 ns
|
|
148
|
+
Complex Pack ███████████████████████████████████████████████████████████████████ 1,469 ns
|
|
149
|
+
Complex Unp ███████████████████████████████████████████████████████████████████ 1,987 ns
|
|
150
|
+
─────────────────────────────────────────────────────────────────────────────────────────
|
|
151
|
+
Iter Update ███████████████████████████████████████████████████████████████████ 29,667 ns
|
|
152
|
+
Iter Unpack ███████████████████████████████████████████████████████████████████ 42,771 ns
|
|
153
|
+
Cplx Iter Up ███████████████████████████████████████████████████████████████████ 95,886 ns
|
|
154
|
+
Cplx Iter Un ███████████████████████████████████████████████████████████████████ 144,415 ns
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## 七、核心结论
|
|
160
|
+
|
|
161
|
+
| 维度 | 结论 |
|
|
162
|
+
|------------------|-----------------------------------------------------|
|
|
163
|
+
| **最快单次操作** | `Pack` (261.96 ns) |
|
|
164
|
+
| **最快解析操作** | `Update From` (331.22 ns) |
|
|
165
|
+
| **最慢单次操作** | `Complex Unpack` (1,986.63 ns),比最快慢 **7.58x** |
|
|
166
|
+
| **复用提升** | 复杂场景下 `update` 比 `unpack` 快 **58.5%** |
|
|
167
|
+
| **偏移影响** | `unpack_from` 比 `unpack` 快 **16.6%**(简单) |
|
|
168
|
+
| **迭代效率** | `iter_update` 比 `iter_unpack` 快 **30.6%**(简单) |
|
|
169
|
+
| **复杂度代价** | 复杂 Pack 比简单 Pack 慢 **5.61x** |
|
|
170
|
+
| **钩子代价** | `__post_unpack__` 增加约 **35%** 开销 |
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## 八、最终建议
|
|
175
|
+
|
|
176
|
+
| 场景 | 推荐 | 理由 |
|
|
177
|
+
|-----------------------|------------------------------------------|--------------------------------------------|
|
|
178
|
+
| **高频单次解析** | `update_from` | 最快解析方法,复用 + 偏移 |
|
|
179
|
+
| **需要新建对象** | `unpack_from` | 比 `unpack` 快 16.6% |
|
|
180
|
+
| **批量解析(100次)** | `iter_update` | 比 `iter_unpack` 快 30.6%,每元素仅 296 ns |
|
|
181
|
+
| **复杂结构批量解析** | `complex_iter_update` | 比 `complex_iter_unpack` 快 33.6% |
|
|
182
|
+
| **避免使用** | `complex_unpack` | 单次最慢,新建对象 + 钩子双重开销 |
|
|
183
|
+
| **极致性能** | 手动 `memoryview` + `struct.unpack_from` | 避免切片拷贝和对象分配 |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "0.4.0"
|
|
@@ -5,6 +5,8 @@ import itertools
|
|
|
5
5
|
import typing
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from obstruct import Struct
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
class _objective_node_operator:
|
|
@@ -121,8 +123,9 @@ def unique_name_generator():
|
|
|
121
123
|
itertools.combinations(available_characters, cnt))
|
|
122
124
|
|
|
123
125
|
|
|
124
|
-
def is_struct(tp):
|
|
125
|
-
|
|
126
|
+
def is_struct(tp) -> typing.TypeIs[type[Struct]]:
|
|
127
|
+
from obstruct.struct_type import Struct
|
|
128
|
+
return isinstance(tp, type) and issubclass(tp, Struct)
|
|
126
129
|
|
|
127
130
|
|
|
128
131
|
def is_array(tp):
|
|
@@ -7,10 +7,14 @@ import typing
|
|
|
7
7
|
from .base import unique_name_generator, is_struct, is_array, get_array_ctype, function_def, for_stmt, get_array_length
|
|
8
8
|
|
|
9
9
|
if typing.TYPE_CHECKING:
|
|
10
|
-
from
|
|
10
|
+
from obstruct.struct_type import Struct
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
def
|
|
13
|
+
def batch_add_code(objs, code: str):
|
|
14
|
+
for obj in objs:
|
|
15
|
+
obj.add_code(code)
|
|
16
|
+
|
|
17
|
+
def create_unpack_methods(cls: type[Struct]):
|
|
14
18
|
from ..methods import get_ctype
|
|
15
19
|
|
|
16
20
|
with (
|
|
@@ -52,58 +56,74 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
52
56
|
iter_update.add_node(iter_update_inner.node)
|
|
53
57
|
|
|
54
58
|
array_fields = []
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
59
|
+
post_inits = []
|
|
60
|
+
|
|
61
|
+
def walk(
|
|
62
|
+
struct_type: type[Struct],
|
|
63
|
+
absolute_xpath: tuple[str, ...],
|
|
64
|
+
current_xname: str = 'self',
|
|
65
|
+
parent_xname=None,
|
|
66
|
+
current_type_name='cls',
|
|
67
|
+
offset=0
|
|
68
|
+
):
|
|
69
|
+
name = current_xname
|
|
70
|
+
|
|
71
|
+
if len(absolute_xpath) > 1: # 在处理内嵌的结构体
|
|
72
|
+
current_field_name = absolute_xpath[-1]
|
|
73
|
+
update_add_code(f"{name} = {parent_xname}.{current_field_name}")
|
|
74
|
+
unpack_add_code(f"{parent_xname}.{current_field_name} = {name} = __new__({current_type_name})") # 组合赋值
|
|
64
75
|
else:
|
|
65
|
-
unpack_add_code(f"{name} = __new__({
|
|
76
|
+
unpack_add_code(f"{name} = __new__({current_type_name})") # 不需要临时变量存储根对象
|
|
66
77
|
# update 在这里不需要创建新对象
|
|
67
78
|
|
|
79
|
+
fields = struct_type.__cfields__
|
|
80
|
+
|
|
68
81
|
for field in fields: # 遍历字段
|
|
69
82
|
field_xpath = f'{name}.{field.name}'
|
|
70
83
|
|
|
71
|
-
# ctype = get_ctype(field.type)
|
|
72
|
-
# print(f"当前字段:{field_xpath},类型:{field.type} '{ctype}',偏移量:{offset}+{struct.calcsize(ctype)}")
|
|
73
|
-
|
|
74
84
|
if is_struct(field.type): # 是个内嵌的结构体
|
|
75
85
|
if field.type not in inner_classes: # 如果这个内嵌的结构体类型不在已知的结构体中(没有分配类名称)
|
|
76
86
|
type_name = next(ung) # 生成一个唯一的类名
|
|
77
|
-
inner_classes[field.type] = type_name
|
|
87
|
+
inner_classes[field.type] = type_name
|
|
78
88
|
else:
|
|
79
89
|
type_name = inner_classes[field.type] # 如果在,那么提取
|
|
80
90
|
|
|
81
|
-
|
|
91
|
+
field_xname = next(ung)
|
|
92
|
+
walk(
|
|
93
|
+
field.type,
|
|
94
|
+
absolute_xpath + (field.name,),
|
|
95
|
+
field_xname,
|
|
96
|
+
name,
|
|
97
|
+
type_name,
|
|
98
|
+
offset
|
|
99
|
+
)
|
|
82
100
|
offset += field.type.__cstruct__.size
|
|
83
101
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
102
|
+
post_inits.append(field_xname)
|
|
103
|
+
|
|
104
|
+
elif is_array(field.type): # 如果是一个数组类型
|
|
105
|
+
dtype = get_array_ctype(field.type) # 获取内容数据类型
|
|
106
|
+
nbytes = get_array_length(field.type) * struct.calcsize(dtype) # 计算数组实际大小
|
|
107
|
+
array_fields.append((next(ung), field_xpath, dtype, offset, nbytes)) # 记录这个数组
|
|
108
|
+
offset += nbytes # 增加当前偏移量
|
|
109
|
+
|
|
110
|
+
else: # 不是特殊的类型,正常处理(添加 assign 目标,并增加偏移量)
|
|
90
111
|
assigns.append(field_xpath)
|
|
91
112
|
offset += struct.calcsize(get_ctype(field.type))
|
|
92
113
|
|
|
93
|
-
walk(cls
|
|
114
|
+
walk(cls, ('A',))
|
|
94
115
|
|
|
95
|
-
|
|
116
|
+
assigns_expr = ', '.join(assigns)
|
|
117
|
+
assign_code = f"{assigns_expr} = _unpack(buffer)" # 生成普通的 unpack, update 用的赋值语句
|
|
96
118
|
# {assigns} = _unpack(buffer)
|
|
97
119
|
unpack.add_code(assign_code)
|
|
98
120
|
update.add_code(assign_code)
|
|
99
121
|
|
|
100
|
-
assign_code =
|
|
101
|
-
# {assigns} = values
|
|
122
|
+
assign_code = f"{assigns_expr} = values" # 生成 iter_xxx 的赋值语句
|
|
102
123
|
iter_unpack_inner.add_code(assign_code)
|
|
103
124
|
iter_update_inner.add_code(assign_code)
|
|
104
125
|
|
|
105
|
-
assign_code =
|
|
106
|
-
# {assigns} = _unpack_from(buffer, offset)
|
|
126
|
+
assign_code = f"{assigns_expr} = _unpack_from(buffer, offset)" # 生成 xxx_from 的赋值语句
|
|
107
127
|
unpack_from.add_code(assign_code)
|
|
108
128
|
update_from.add_code(assign_code)
|
|
109
129
|
|
|
@@ -145,6 +165,13 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
145
165
|
if array_fields:
|
|
146
166
|
unpack.insert_code(0, 'buffer = memoryview(buffer)')
|
|
147
167
|
|
|
168
|
+
|
|
169
|
+
for init in post_inits:
|
|
170
|
+
batch_add_code(
|
|
171
|
+
[unpack, update, unpack_from, update_from, iter_unpack_inner, iter_update_inner],
|
|
172
|
+
f"{init}.__post_unpack__()"
|
|
173
|
+
)
|
|
174
|
+
|
|
148
175
|
unpack.add_return('self')
|
|
149
176
|
update.add_return('self')
|
|
150
177
|
unpack_from.add_return('self')
|
|
@@ -156,12 +183,12 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
156
183
|
for n, *_ in array_fields:
|
|
157
184
|
iter_update_inner.add_code(f'{n}.clear()')
|
|
158
185
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
186
|
+
print(unpack.generate_code())
|
|
187
|
+
print(update.generate_code())
|
|
188
|
+
print(unpack_from.generate_code())
|
|
189
|
+
print(update_from.generate_code())
|
|
190
|
+
print(iter_unpack.generate_code())
|
|
191
|
+
print(iter_update.generate_code())
|
|
165
192
|
|
|
166
193
|
globals = {
|
|
167
194
|
v: k for k, v in inner_classes.items() # 翻转字典
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import time
|
|
3
|
+
from array import array
|
|
4
|
+
from typing import Annotated
|
|
5
|
+
|
|
6
|
+
import obstruct as obs
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def run_iterator(iterator):
|
|
10
|
+
[i for i in iterator]
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclasses.dataclass()
|
|
14
|
+
class MyStruct(obs.Struct):
|
|
15
|
+
a: int
|
|
16
|
+
b: int
|
|
17
|
+
c: float
|
|
18
|
+
d: int
|
|
19
|
+
e: obs.short
|
|
20
|
+
f: obs.unsigned_short
|
|
21
|
+
g: obs.long
|
|
22
|
+
h: obs.unsigned_long
|
|
23
|
+
i: obs.unsigned_long
|
|
24
|
+
j: obs.char
|
|
25
|
+
k: obs.unsigned_char
|
|
26
|
+
l: obs.long_long
|
|
27
|
+
|
|
28
|
+
def __post_unpack__(self):
|
|
29
|
+
global is_post_unpack_called
|
|
30
|
+
is_post_unpack_called = True
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclasses.dataclass()
|
|
34
|
+
class AnotherStruct(obs.Struct):
|
|
35
|
+
x: MyStruct
|
|
36
|
+
y: Annotated[array[int], 100]
|
|
37
|
+
z: MyStruct
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
MyStruct.__compile_struct__()
|
|
41
|
+
AnotherStruct.__compile_struct__()
|
|
42
|
+
|
|
43
|
+
print('MyStruct size', MyStruct.__cstruct__.size)
|
|
44
|
+
print('AnotherStruct size', AnotherStruct.__cstruct__.size)
|
|
45
|
+
|
|
46
|
+
is_post_unpack_called = False
|
|
47
|
+
|
|
48
|
+
data = MyStruct(
|
|
49
|
+
1, 2, 1.0, 4, 5, 6, 7, 8, 9, b'8', 1, 2
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
packed_data = data.pack()
|
|
53
|
+
packed_data_100 = packed_data * 100
|
|
54
|
+
|
|
55
|
+
complex_data = AnotherStruct(data, array('i', [100] * 100), data)
|
|
56
|
+
packed_complex_data = complex_data.pack()
|
|
57
|
+
packed_complex_data_100 = packed_complex_data * 100
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def test_timeit(name, code, extra="print('Post Hook Called:', is_post_unpack_called); is_post_unpack_called=False"):
|
|
61
|
+
import timeit
|
|
62
|
+
|
|
63
|
+
res = timeit.repeat(
|
|
64
|
+
code,
|
|
65
|
+
number=NUMBER,
|
|
66
|
+
repeat=REPEAT,
|
|
67
|
+
timer=time.perf_counter_ns,
|
|
68
|
+
globals=globals()
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
print()
|
|
72
|
+
# print(len(res))
|
|
73
|
+
|
|
74
|
+
for i in range(SORT):
|
|
75
|
+
res.remove(max(res))
|
|
76
|
+
res.remove(min(res))
|
|
77
|
+
|
|
78
|
+
print("Test: ", name)
|
|
79
|
+
print("Results Length:", len(res))
|
|
80
|
+
|
|
81
|
+
print(f'{NUMBER} steps finished in {sum(res) / (len(res)):.3f} ns')
|
|
82
|
+
print(f'Average: {sum(res) / (len(res)) / NUMBER:.3f} ns per step', '\n\n')
|
|
83
|
+
|
|
84
|
+
exec(extra, globals=globals()) if extra else None
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# print("\n\nMyStruct:", MyStruct.__cstruct__.size)
|
|
88
|
+
print("AnotherStruct:", AnotherStruct.__cstruct__.size, '\n\n')
|
|
89
|
+
|
|
90
|
+
NUMBER = 1000
|
|
91
|
+
REPEAT = 128
|
|
92
|
+
SORT = 32
|
|
93
|
+
|
|
94
|
+
test_timeit(
|
|
95
|
+
name='Pack',
|
|
96
|
+
code="""data.pack()""",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
test_timeit(
|
|
100
|
+
name='Unpack',
|
|
101
|
+
code="""MyStruct.unpack(packed_data)""",
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
test_timeit(
|
|
105
|
+
name='Complex Pack',
|
|
106
|
+
code="""complex_data.pack()""",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
test_timeit(
|
|
110
|
+
name='Complex Unpack',
|
|
111
|
+
code="""AnotherStruct.unpack(packed_complex_data)""",
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
test_timeit(
|
|
115
|
+
name='Update',
|
|
116
|
+
code="""data.update(packed_data)"""
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
test_timeit(
|
|
120
|
+
name='Iter Unpack (100x)',
|
|
121
|
+
code="""run_iterator(MyStruct.iter_unpack(packed_data_100))"""
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
test_timeit(
|
|
125
|
+
name='Iter Update (100x)',
|
|
126
|
+
code="""run_iterator(MyStruct.iter_update(packed_data_100, obj=data))"""
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
test_timeit(
|
|
130
|
+
name='Unpack From',
|
|
131
|
+
code="""MyStruct.unpack_from(packed_data)"""
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
test_timeit(
|
|
135
|
+
name='Update From',
|
|
136
|
+
code="""data.update_from(packed_data)"""
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
test_timeit(
|
|
140
|
+
name='Complex Update',
|
|
141
|
+
code="""complex_data.update(packed_complex_data)"""
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
test_timeit(
|
|
145
|
+
name='Complex Iter Unpack (100x)',
|
|
146
|
+
code="""run_iterator(AnotherStruct.iter_unpack(packed_complex_data_100))"""
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
test_timeit(
|
|
150
|
+
name='Complex Iter Update (100x)',
|
|
151
|
+
code="""run_iterator(AnotherStruct.iter_update(packed_complex_data_100, obj=complex_data))"""
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
test_timeit(
|
|
155
|
+
name='Complex Unpack From (100x)',
|
|
156
|
+
code="""AnotherStruct.unpack_from(packed_complex_data)"""
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
test_timeit(
|
|
160
|
+
name='Complex Update From (100x)',
|
|
161
|
+
code="""complex_data.update_from(packed_complex_data)"""
|
|
162
|
+
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = "0.2.15"
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import time
|
|
3
|
-
from array import array
|
|
4
|
-
from typing import Annotated
|
|
5
|
-
|
|
6
|
-
import obstruct as obs
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
@dataclasses.dataclass()
|
|
10
|
-
class MyStruct(obs.Struct):
|
|
11
|
-
a: int
|
|
12
|
-
b: int
|
|
13
|
-
c: float
|
|
14
|
-
d: int
|
|
15
|
-
e: obs.short
|
|
16
|
-
f: obs.unsigned_short
|
|
17
|
-
g: obs.long
|
|
18
|
-
h: obs.unsigned_long
|
|
19
|
-
i: obs.unsigned_long
|
|
20
|
-
j: obs.char
|
|
21
|
-
k: obs.unsigned_char
|
|
22
|
-
l: obs.long_long
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@dataclasses.dataclass()
|
|
26
|
-
class AnotherStruct(obs.Struct):
|
|
27
|
-
x: MyStruct
|
|
28
|
-
y: Annotated[array[int], 100]
|
|
29
|
-
z: MyStruct
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
MyStruct.__compile_struct__()
|
|
33
|
-
AnotherStruct.__compile_struct__()
|
|
34
|
-
|
|
35
|
-
print('MyStruct size', MyStruct.__cstruct__.size)
|
|
36
|
-
print('AnotherStruct size', AnotherStruct.__cstruct__.size)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
data = MyStruct(
|
|
40
|
-
1,2,1.0,4,5,6,7,8,9,b'8',1,2
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
packed_data = data.pack()
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
complex_data = AnotherStruct(data, array('i', [100]*100), data)
|
|
47
|
-
packed_complex_data = complex_data.pack()
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
def test_timeit(name, code):
|
|
51
|
-
import timeit
|
|
52
|
-
|
|
53
|
-
print("Test: ", name)
|
|
54
|
-
res = timeit.repeat(
|
|
55
|
-
code,
|
|
56
|
-
number=NUMBER,
|
|
57
|
-
repeat=REPEAT,
|
|
58
|
-
timer=time.perf_counter_ns,
|
|
59
|
-
globals=globals()
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
print(len(res))
|
|
63
|
-
|
|
64
|
-
for i in range(SORT):
|
|
65
|
-
res.remove(max(res))
|
|
66
|
-
res.remove(min(res))
|
|
67
|
-
|
|
68
|
-
print("Results Length:", len(res))
|
|
69
|
-
|
|
70
|
-
print(f'{NUMBER} steps finished in {sum(res)/(len(res)):.3f} ns')
|
|
71
|
-
print(f'Average: {sum(res)/(len(res))/NUMBER:.3f} ns per step', '\n\n')
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
print("\n\nMyStruct:", MyStruct.__cstruct__.size)
|
|
75
|
-
print("AnotherStruct:", AnotherStruct.__cstruct__.size, '\n\n')
|
|
76
|
-
|
|
77
|
-
NUMBER = 10000
|
|
78
|
-
REPEAT = 128
|
|
79
|
-
SORT = 32
|
|
80
|
-
|
|
81
|
-
test_timeit(
|
|
82
|
-
name='Pack',
|
|
83
|
-
code="""data.pack()""",
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
test_timeit(
|
|
87
|
-
name='Unpack',
|
|
88
|
-
code="""MyStruct.unpack(packed_data)""",
|
|
89
|
-
)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
test_timeit(
|
|
94
|
-
name='Complex Pack',
|
|
95
|
-
code="""complex_data.pack()""",
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
test_timeit(
|
|
99
|
-
name='Complex Unpack',
|
|
100
|
-
code="""AnotherStruct.unpack(packed_complex_data)""",
|
|
101
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|