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.
Files changed (31) hide show
  1. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/workspace.xml +18 -3
  2. obstruct-0.4.0/BENCHMARK_TEST.md +183 -0
  3. {obstruct-0.2.15 → obstruct-0.4.0}/PKG-INFO +1 -1
  4. obstruct-0.4.0/src/obstruct/__version__.py +1 -0
  5. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/base.py +5 -2
  6. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/unpack.py +62 -35
  7. obstruct-0.4.0/test/test_speed.py +162 -0
  8. obstruct-0.2.15/src/obstruct/__version__.py +0 -1
  9. obstruct-0.2.15/test/test_speed.py +0 -101
  10. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/.gitignore +0 -0
  11. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  12. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  13. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/misc.xml +0 -0
  14. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/modules.xml +0 -0
  15. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/obstruct.iml +0 -0
  16. {obstruct-0.2.15 → obstruct-0.4.0}/.idea/vcs.xml +0 -0
  17. {obstruct-0.2.15 → obstruct-0.4.0}/README.md +0 -0
  18. {obstruct-0.2.15 → obstruct-0.4.0}/pyproject.toml +0 -0
  19. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/__init__.py +0 -0
  20. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/__init__.py +0 -0
  21. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/aot.py +0 -0
  22. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/aot/pack.py +0 -0
  23. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/metadata_marks.py +0 -0
  24. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/methods.py +0 -0
  25. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/struct_type.py +0 -0
  26. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/__init__.py +0 -0
  27. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/base.py +0 -0
  28. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/types.py +0 -0
  29. {obstruct-0.2.15 → obstruct-0.4.0}/src/obstruct/types/types.pyi +0 -0
  30. {obstruct-0.2.15 → obstruct-0.4.0}/test/test.py +0 -0
  31. {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.14,修复 __cendian__ 类变量识别错误的问题" />
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
- <option name="localTasksCounter" value="19" />
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
- <option name="LAST_COMMIT_MESSAGE" value="fix: 更新版本号至 0.2.14,修复 __cendian__ 类变量识别错误的问题" />
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` | 避免切片拷贝和对象分配 |
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: obstruct
3
- Version: 0.2.15
3
+ Version: 0.4.0
4
4
  Author-email: LittleNightSong <LittleNightSongYO@outlook.com>
5
5
  Classifier: Development Status :: 3 - Alpha
6
6
  Classifier: Programming Language :: Python
@@ -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
- return hasattr(tp, '__cstruct__')
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 .. import struct_type
10
+ from obstruct.struct_type import Struct
11
11
 
12
12
 
13
- def create_unpack_methods(cls: type[struct_type.Struct]):
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
- def walk(fields, path, parent_name=None, current_class_name='cls', offset=0):
57
- name = 'self'
58
-
59
- if len(path) > 1: # 在处理内嵌的结构体
60
- name = next(ung) # 临时变量的名称
61
- update_add_code(f"{name} = {parent_name}.{path[-1]}")
62
-
63
- unpack_add_code(f"{parent_name}.{path[-1]} = {name} = __new__({current_class_name})") # 组合赋值
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__({current_class_name})") # 不需要临时变量存储根对象
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
- walk(field.type.__cfields__, path + (field.name,), name, type_name, offset)
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
- elif is_array(field.type):
85
- dtype = get_array_ctype(field.type)
86
- nbytes = get_array_length(field.type) * struct.calcsize(dtype)
87
- array_fields.append((next(ung), field_xpath, dtype, offset, nbytes))
88
- offset += nbytes
89
- else:
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.__cfields__, ('A',))
114
+ walk(cls, ('A',))
94
115
 
95
- assign_code = f"{', '.join(assigns)} = _unpack(buffer)" # 生成普通的 unpack, update 用的赋值语句
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 = assign_code.replace("_unpack(buffer)", "values") # 生成 iter_xxx 的赋值语句
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 = assign_code.replace("values", "_unpack_from(buffer, offset)") # 生成 xxx_from 的赋值语句
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
- # print(unpack.generate_code())
160
- # print(update.generate_code())
161
- # print(unpack_from.generate_code())
162
- # print(update_from.generate_code())
163
- # print(iter_unpack.generate_code())
164
- # print(iter_update.generate_code())
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