obstruct 0.6.0__tar.gz → 0.6.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.
Files changed (45) hide show
  1. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/workspace.xml +106 -48
  2. {obstruct-0.6.0 → obstruct-0.6.2}/PKG-INFO +1 -1
  3. obstruct-0.6.2/src/obstruct/__version__.py +1 -0
  4. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/base.py +39 -4
  5. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/field.py +5 -5
  6. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/pack.py +2 -2
  7. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/schema.py +4 -1
  8. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/unpack.py +62 -27
  9. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/schema_methods.py +10 -0
  10. obstruct-0.6.2/test/test_new.py +53 -0
  11. obstruct-0.6.2/test/test_s_and_x.py +52 -0
  12. obstruct-0.6.2/test/test_str_field.py +21 -0
  13. obstruct-0.6.2/v0.5.2-speed-log.log +137 -0
  14. obstruct-0.6.0/src/obstruct/__version__.py +0 -1
  15. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/.gitignore +0 -0
  16. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/inspectionProfiles/Project_Default.xml +0 -0
  17. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
  18. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/misc.xml +0 -0
  19. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/modules.xml +0 -0
  20. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/obstruct.iml +0 -0
  21. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/shelf/_2026_9_6_14_40____.xml +0 -0
  22. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/shelf//345/234/250/350/277/233/350/241/214/346/233/264/346/226/260/344/271/213/345/211/215/344/272/216_2026_9_6_14_40_/345/217/226/346/266/210/346/217/220/344/272/244/344/272/206/346/233/264/346/224/271_[/346/233/264/346/224/271]/shelved.patch" +0 -0
  23. {obstruct-0.6.0 → obstruct-0.6.2}/.idea/vcs.xml +0 -0
  24. {obstruct-0.6.0 → obstruct-0.6.2}/BENCHMARK_TEST-v0.5.0.md +0 -0
  25. {obstruct-0.6.0 → obstruct-0.6.2}/BENCHMARK_TEST-v0.6.0.md +0 -0
  26. {obstruct-0.6.0 → obstruct-0.6.2}/LICENSE +0 -0
  27. {obstruct-0.6.0 → obstruct-0.6.2}/README.md +0 -0
  28. {obstruct-0.6.0 → obstruct-0.6.2}/USAGE.md +0 -0
  29. {obstruct-0.6.0 → obstruct-0.6.2}/pyproject.toml +0 -0
  30. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/__init__.py +0 -0
  31. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/__init__.py +0 -0
  32. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/aot/aot.py +0 -0
  33. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/methods.py +0 -0
  34. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/struct_type.py +0 -0
  35. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/types/__init__.py +0 -0
  36. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/types/types.py +0 -0
  37. {obstruct-0.6.0 → obstruct-0.6.2}/src/obstruct/types/types.pyi +0 -0
  38. {obstruct-0.6.0 → obstruct-0.6.2}/test/test.py +0 -0
  39. {obstruct-0.6.0 → obstruct-0.6.2}/test/test_gen_from_schema.py +0 -0
  40. {obstruct-0.6.0 → obstruct-0.6.2}/test/test_speed.py +0 -0
  41. {obstruct-0.6.0 → obstruct-0.6.2}/test-unsafe-new.log +0 -0
  42. {obstruct-0.6.0 → obstruct-0.6.2}/test.log +0 -0
  43. {obstruct-0.6.0 → obstruct-0.6.2}/test1.log +0 -0
  44. {obstruct-0.6.0 → obstruct-0.6.2}/test2.log +0 -0
  45. {obstruct-0.6.0 → obstruct-0.6.2}/uv.lock +0 -0
@@ -4,29 +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: 错误的别名&#10;&#10;+ version: 0.5.2">
8
- <change afterPath="$PROJECT_DIR$/BENCHMARK_TEST-v0.6.0.md" afterDir="false" />
9
- <change afterPath="$PROJECT_DIR$/USAGE.md" afterDir="false" />
10
- <change afterPath="$PROJECT_DIR$/src/obstruct/aot/field.py" afterDir="false" />
11
- <change afterPath="$PROJECT_DIR$/src/obstruct/aot/schema.py" afterDir="false" />
12
- <change afterPath="$PROJECT_DIR$/src/obstruct/schema_methods.py" afterDir="false" />
13
- <change beforePath="$PROJECT_DIR$/BENCHMARK_TEST.md" beforeDir="false" afterPath="$PROJECT_DIR$/BENCHMARK_TEST-v0.5.0.md" afterDir="false" />
14
- <change beforePath="$PROJECT_DIR$/README.md" beforeDir="false" afterPath="$PROJECT_DIR$/README.md" afterDir="false" />
15
- <change beforePath="$PROJECT_DIR$/pyproject.toml" beforeDir="false" afterPath="$PROJECT_DIR$/pyproject.toml" afterDir="false" />
16
- <change beforePath="$PROJECT_DIR$/src/obstruct/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/__init__.py" afterDir="false" />
17
- <change beforePath="$PROJECT_DIR$/src/obstruct/aot/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/__init__.py" afterDir="false" />
18
- <change beforePath="$PROJECT_DIR$/src/obstruct/aot/aot.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/aot.py" afterDir="false" />
7
+ <list default="true" id="b482b6b4-48ce-4be1-bf3c-a72ec254fa35" name="更改" comment="fix: 未删除的调试语句&#10;&#10;+ update version to 0.6.1">
8
+ <change afterPath="$PROJECT_DIR$/test/test_str_field.py" afterDir="false" />
19
9
  <change beforePath="$PROJECT_DIR$/src/obstruct/aot/base.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/base.py" afterDir="false" />
20
- <change beforePath="$PROJECT_DIR$/src/obstruct/aot/pack.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/pack.py" afterDir="false" />
10
+ <change beforePath="$PROJECT_DIR$/src/obstruct/aot/field.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/field.py" afterDir="false" />
11
+ <change beforePath="$PROJECT_DIR$/src/obstruct/aot/schema.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/schema.py" afterDir="false" />
21
12
  <change beforePath="$PROJECT_DIR$/src/obstruct/aot/unpack.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/unpack.py" afterDir="false" />
22
- <change beforePath="$PROJECT_DIR$/src/obstruct/metadata_marks.py" beforeDir="false" />
23
- <change beforePath="$PROJECT_DIR$/src/obstruct/methods.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/methods.py" afterDir="false" />
24
- <change beforePath="$PROJECT_DIR$/src/obstruct/struct_type.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/struct_type.py" afterDir="false" />
25
- <change beforePath="$PROJECT_DIR$/src/obstruct/types/__init__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/types/__init__.py" afterDir="false" />
26
- <change beforePath="$PROJECT_DIR$/src/obstruct/types/base.py" beforeDir="false" />
27
- <change beforePath="$PROJECT_DIR$/src/obstruct/types/types.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/types/types.py" afterDir="false" />
28
- <change beforePath="$PROJECT_DIR$/test/test_speed.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/test_speed.py" afterDir="false" />
29
- <change beforePath="$PROJECT_DIR$/uv.lock" beforeDir="false" afterPath="$PROJECT_DIR$/uv.lock" afterDir="false" />
13
+ <change beforePath="$PROJECT_DIR$/src/obstruct/schema_methods.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/schema_methods.py" afterDir="false" />
30
14
  </list>
31
15
  <option name="SHOW_DIALOG" value="false" />
32
16
  <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -41,6 +25,11 @@
41
25
  </option>
42
26
  </component>
43
27
  <component name="Git.Settings">
28
+ <option name="RECENT_BRANCH_BY_REPOSITORY">
29
+ <map>
30
+ <entry key="$PROJECT_DIR$" value="cce909ac291c4d0b7cb243ecf0dc4845dd8cc422" />
31
+ </map>
32
+ </option>
44
33
  <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
45
34
  </component>
46
35
  <component name="GitHubPullRequestSearchHistory">{
@@ -64,35 +53,38 @@
64
53
  <option name="hideEmptyMiddlePackages" value="true" />
65
54
  <option name="showLibraryContents" value="true" />
66
55
  </component>
67
- <component name="PropertiesComponent">{
68
- &quot;keyToString&quot;: {
69
- &quot;ModuleVcsDetector.initialDetectionPerformed&quot;: &quot;true&quot;,
70
- &quot;Python.test.executor&quot;: &quot;Run&quot;,
71
- &quot;Python.test_gen_from_schema.executor&quot;: &quot;Run&quot;,
72
- &quot;Python.test_speed.executor&quot;: &quot;Run&quot;,
73
- &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
74
- &quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
75
- &quot;RunOnceActivity.typescript.service.memoryLimit.init&quot;: &quot;true&quot;,
76
- &quot;codeWithMe.voiceChat.enabledByDefault&quot;: &quot;false&quot;,
77
- &quot;git-widget-placeholder&quot;: &quot;main&quot;,
78
- &quot;last_opened_file_path&quot;: &quot;E:/.projects/obstruct&quot;,
79
- &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
80
- &quot;node.js.detected.package.jshint&quot;: &quot;true&quot;,
81
- &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
82
- &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
83
- &quot;node.js.selected.package.jshint&quot;: &quot;&quot;,
84
- &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
85
- &quot;nodejs_package_manager_path&quot;: &quot;npm&quot;,
86
- &quot;settings.editor.selected.configurable&quot;: &quot;editing.templates&quot;
56
+ <component name="PropertiesComponent"><![CDATA[{
57
+ "keyToString": {
58
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
59
+ "Python.test.executor": "Run",
60
+ "Python.test_gen_from_schema.executor": "Run",
61
+ "Python.test_new.executor": "Run",
62
+ "Python.test_s_and_x.executor": "Run",
63
+ "Python.test_speed.executor": "Run",
64
+ "Python.test_str_field.executor": "Run",
65
+ "RunOnceActivity.ShowReadmeOnStart": "true",
66
+ "RunOnceActivity.git.unshallow": "true",
67
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
68
+ "codeWithMe.voiceChat.enabledByDefault": "false",
69
+ "git-widget-placeholder": "main",
70
+ "last_opened_file_path": "E:/.projects/obstruct",
71
+ "node.js.detected.package.eslint": "true",
72
+ "node.js.detected.package.jshint": "true",
73
+ "node.js.detected.package.tslint": "true",
74
+ "node.js.selected.package.eslint": "(autodetect)",
75
+ "node.js.selected.package.jshint": "",
76
+ "node.js.selected.package.tslint": "(autodetect)",
77
+ "nodejs_package_manager_path": "npm",
78
+ "settings.editor.selected.configurable": "editing.templates"
87
79
  }
88
- }</component>
80
+ }]]></component>
89
81
  <component name="RecentsManager">
90
82
  <key name="MoveFile.RECENT_KEYS">
91
83
  <recent name="E:\.projects\obstruct\src\obstruct" />
92
84
  <recent name="E:\.projects\obstruct\src\obstruct\aot" />
93
85
  </key>
94
86
  </component>
95
- <component name="RunManager" selected="Python.test">
87
+ <component name="RunManager" selected="Python.test_str_field">
96
88
  <configuration name="test" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
97
89
  <module name="obstruct" />
98
90
  <option name="ENV_FILES" value="" />
@@ -151,6 +143,30 @@
151
143
  <option name="INPUT_FILE" value="" />
152
144
  <method v="2" />
153
145
  </configuration>
146
+ <configuration name="test_s_and_x" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
147
+ <module name="obstruct" />
148
+ <option name="ENV_FILES" value="" />
149
+ <option name="INTERPRETER_OPTIONS" value="" />
150
+ <option name="PARENT_ENVS" value="true" />
151
+ <envs>
152
+ <env name="PYTHONUNBUFFERED" value="1" />
153
+ </envs>
154
+ <option name="SDK_HOME" value="" />
155
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test" />
156
+ <option name="IS_MODULE_SDK" value="true" />
157
+ <option name="ADD_CONTENT_ROOTS" value="true" />
158
+ <option name="ADD_SOURCE_ROOTS" value="true" />
159
+ <option name="DEBUG_JUST_MY_CODE" value="false" />
160
+ <option name="RUN_TOOL" value="" />
161
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/test_s_and_x.py" />
162
+ <option name="PARAMETERS" value="" />
163
+ <option name="SHOW_COMMAND_LINE" value="false" />
164
+ <option name="EMULATE_TERMINAL" value="false" />
165
+ <option name="MODULE_MODE" value="false" />
166
+ <option name="REDIRECT_INPUT" value="false" />
167
+ <option name="INPUT_FILE" value="" />
168
+ <method v="2" />
169
+ </configuration>
154
170
  <configuration name="test_speed" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
155
171
  <module name="obstruct" />
156
172
  <option name="ENV_FILES" value="" />
@@ -175,11 +191,37 @@
175
191
  <option name="INPUT_FILE" value="" />
176
192
  <method v="2" />
177
193
  </configuration>
194
+ <configuration name="test_str_field" type="PythonConfigurationType" factoryName="Python" temporary="true" nameIsGenerated="true">
195
+ <module name="obstruct" />
196
+ <option name="ENV_FILES" value="" />
197
+ <option name="INTERPRETER_OPTIONS" value="" />
198
+ <option name="PARENT_ENVS" value="true" />
199
+ <envs>
200
+ <env name="PYTHONUNBUFFERED" value="1" />
201
+ </envs>
202
+ <option name="SDK_HOME" value="" />
203
+ <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/test" />
204
+ <option name="IS_MODULE_SDK" value="true" />
205
+ <option name="ADD_CONTENT_ROOTS" value="true" />
206
+ <option name="ADD_SOURCE_ROOTS" value="true" />
207
+ <option name="DEBUG_JUST_MY_CODE" value="false" />
208
+ <option name="RUN_TOOL" value="" />
209
+ <option name="SCRIPT_NAME" value="$PROJECT_DIR$/test/test_str_field.py" />
210
+ <option name="PARAMETERS" value="" />
211
+ <option name="SHOW_COMMAND_LINE" value="false" />
212
+ <option name="EMULATE_TERMINAL" value="false" />
213
+ <option name="MODULE_MODE" value="false" />
214
+ <option name="REDIRECT_INPUT" value="false" />
215
+ <option name="INPUT_FILE" value="" />
216
+ <method v="2" />
217
+ </configuration>
178
218
  <recent_temporary>
179
219
  <list>
180
- <item itemvalue="Python.test" />
220
+ <item itemvalue="Python.test_str_field" />
221
+ <item itemvalue="Python.test_s_and_x" />
181
222
  <item itemvalue="Python.test_speed" />
182
223
  <item itemvalue="Python.test_gen_from_schema" />
224
+ <item itemvalue="Python.test" />
183
225
  </list>
184
226
  </recent_temporary>
185
227
  </component>
@@ -414,7 +456,23 @@
414
456
  <option name="project" value="LOCAL" />
415
457
  <updated>1787044545927</updated>
416
458
  </task>
417
- <option name="localTasksCounter" value="28" />
459
+ <task id="LOCAL-00028" summary="refactor: 优化数组和复杂结构体的解析性能,将 AOT 重写数据驱动&#10;&#10;+ update version to 0.6.0&#10;+ 更新测速脚本,添加新的性能测试信息&#10;+ 更新 README.md&#10;- 移除了旧的 metadata_marks.py,全面转向新的 Meta 元数据类型">
460
+ <option name="closed" value="true" />
461
+ <created>1788678016647</created>
462
+ <option name="number" value="00028" />
463
+ <option name="presentableId" value="LOCAL-00028" />
464
+ <option name="project" value="LOCAL" />
465
+ <updated>1788678016647</updated>
466
+ </task>
467
+ <task id="LOCAL-00029" summary="fix: 未删除的调试语句&#10;&#10;+ update version to 0.6.1">
468
+ <option name="closed" value="true" />
469
+ <created>1788678088267</created>
470
+ <option name="number" value="00029" />
471
+ <option name="presentableId" value="LOCAL-00029" />
472
+ <option name="project" value="LOCAL" />
473
+ <updated>1788678088267</updated>
474
+ </task>
475
+ <option name="localTasksCounter" value="30" />
418
476
  <servers />
419
477
  </component>
420
478
  <component name="TypeScriptGeneratedFilesManager">
@@ -432,8 +490,6 @@
432
490
  </option>
433
491
  </component>
434
492
  <component name="VcsManagerConfiguration">
435
- <MESSAGE value="增强类型系统,撰写 README.md" />
436
- <MESSAGE value="补充:基准测试脚本" />
437
493
  <MESSAGE value="修正了一些命名" />
438
494
  <MESSAGE value="增强 array 类型的支持" />
439
495
  <MESSAGE value="增强 array 类型的支持 x2" />
@@ -457,6 +513,8 @@
457
513
  <MESSAGE value="fix: 未删除的调试语句" />
458
514
  <MESSAGE value="fix: 未删除的调试语句&#10;&#10;+ version: 0.5.1" />
459
515
  <MESSAGE value="fix: 错误的别名&#10;&#10;+ version: 0.5.2" />
460
- <option name="LAST_COMMIT_MESSAGE" value="fix: 错误的别名&#10;&#10;+ version: 0.5.2" />
516
+ <MESSAGE value="refactor: 优化数组和复杂结构体的解析性能,将 AOT 重写数据驱动&#10;&#10;+ update version to 0.6.0&#10;+ 更新测速脚本,添加新的性能测试信息&#10;+ 更新 README.md&#10;- 移除了旧的 metadata_marks.py,全面转向新的 Meta 元数据类型" />
517
+ <MESSAGE value="fix: 未删除的调试语句&#10;&#10;+ update version to 0.6.1" />
518
+ <option name="LAST_COMMIT_MESSAGE" value="fix: 未删除的调试语句&#10;&#10;+ update version to 0.6.1" />
461
519
  </component>
462
520
  </project>
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: obstruct
3
- Version: 0.6.0
3
+ Version: 0.6.2
4
4
  Author-email: LittleNightSong <LittleNightSongYO@outlook.com>
5
5
  License-File: LICENSE
6
6
  Classifier: Development Status :: 3 - Alpha
@@ -0,0 +1 @@
1
+ version = "0.6.2"
@@ -106,10 +106,45 @@ class for_stmt(_objective_node_operator):
106
106
 
107
107
 
108
108
  def name_generator(prefix='var_'):
109
- cnt = 1
110
- while True:
111
- yield f'{prefix}{cnt}'
112
- cnt += 1
109
+ return NameGenerator(prefix=prefix)
110
+
111
+
112
+ class NameGenerator:
113
+ __slots__ = ['_prefix', '_cnt']
114
+
115
+ def __init__(self, prefix='var_'):
116
+ self._prefix = prefix
117
+ self._cnt = 0
118
+
119
+ @property
120
+ def prefix(self):
121
+ return self._prefix
122
+
123
+ @property
124
+ def cnt(self):
125
+ return self._cnt
126
+
127
+ def next(self):
128
+ self._cnt += 1
129
+ return f'{self._prefix}{self._cnt}'
130
+
131
+ def peek(self):
132
+ return f'{self._prefix}{self._cnt + 1}'
133
+
134
+ def pop(self):
135
+ self._cnt -= 1
136
+
137
+ def new(self):
138
+ return self.next()
139
+
140
+ def __call__(self):
141
+ return self.next()
142
+
143
+ def __next__(self):
144
+ return self.next()
145
+
146
+ def __iter__(self):
147
+ return self
113
148
 
114
149
 
115
150
  def to_tuple_expression(names, prefix='self.'):
@@ -10,17 +10,17 @@ class Meta:
10
10
  # 基本控制标志
11
11
  not_a_field: bool = False
12
12
 
13
- # 针对字符串的控制标志
14
- encoding: str = 'utf-8'
13
+ # 针对字符串的控制参数
14
+ encoding: str | None = None
15
15
 
16
- # 针对字节串的控制标志
16
+ # 针对字节串的控制参数
17
17
  trunc_nul: bool = False
18
18
 
19
- # 针对数组类型的控制标志
19
+ # 针对数组类型的控制参数
20
20
  len: int = 0
21
21
  dtype: typing.Any = None
22
22
 
23
- # 针对结构体类型的控制标志
23
+ # 针对结构体类型的控制参数
24
24
  unsafe_create: bool = False
25
25
 
26
26
 
@@ -66,8 +66,8 @@ def create_pack_methods(schema: StructType):
66
66
 
67
67
  # pack.add_return(f"_pack({expression})")
68
68
 
69
- print("Pack\n", pack.generate_code(), sep='', end='\n\n') # Debug
70
- print("Pack Into\n", pack_into.generate_code(), sep='', end='\n\n') # Debug
69
+ # print("Pack\n", pack.generate_code(), sep='', end='\n\n') # Debug
70
+ # print("Pack Into\n", pack_into.generate_code(), sep='', end='\n\n') # Debug
71
71
 
72
72
  format = schema.full_format_string
73
73
  # print("Final Struct Format string:", format)
@@ -71,7 +71,6 @@ class BasicType:
71
71
  from obstruct.types import force_endian
72
72
  return struct.calcsize(force_endian(self.format_string, endian='native'))
73
73
 
74
-
75
74
  @dataclass(slots=True)
76
75
  class ArrayType:
77
76
  dtype: str
@@ -96,5 +95,9 @@ class ArrayType:
96
95
  def format_string(self):
97
96
  return f'{self.size}s'
98
97
 
98
+ @property
99
+ def padmode_format_string(self):
100
+ return f'{self.size}x'
101
+
99
102
 
100
103
  SchemaTypes = StructType | UnionType | BasicType | ArrayType
@@ -6,8 +6,8 @@ import struct
6
6
  import typing
7
7
 
8
8
  from .base import name_generator, function_def, for_stmt
9
- from .schema import StructType, ArrayType, BasicType
10
9
  from .field import Meta
10
+ from .schema import StructType, ArrayType, BasicType
11
11
 
12
12
  if typing.TYPE_CHECKING:
13
13
  pass
@@ -57,44 +57,49 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
57
57
 
58
58
  array_fields = []
59
59
  post_inits = []
60
+ post_patches: list[str] = []
61
+ rewrites: list[tuple[str, str]] = []
60
62
  ctx = {}
61
63
 
62
64
  def walk(
63
65
  *,
64
- schema: StructType,
65
- meta: Meta,
66
- absolute_xpath: tuple[str, ...],
67
- name: str = 'self',
68
- parent_xname=None,
69
- current_unique_type_name='cls',
70
- offset=0
66
+ schema: StructType, # 当前处理的结构体的规格对象
67
+ meta: Meta, # 附加其上的元数据
68
+ absolute_xpath: tuple[str, ...], # 未经过修饰的字段路径(至当前字段)
69
+ name: str = 'self', # 当前处理的结构体对象被分配的临时变量名称
70
+ parent_xname=None, # 其父结构体对象被分配的临时变量名称
71
+ current_unique_type_name='cls', # 结构体的类型名
72
+ offset=0 # 字段绝对偏移量(从根结构体开始)
71
73
  ):
72
74
 
73
75
  base = 'object' if meta.unsafe_create else current_unique_type_name
74
76
 
75
- new_method_alias = f'{base}.__new__({current_unique_type_name})'
77
+ new_object_call = f'{base}.__new__({current_unique_type_name})'
76
78
 
77
- # ctx[new_method_alias] = lambda tobj=current_type_obj: tobj.__new__(tobj)
79
+ # ctx[new_object_call] = lambda tobj=current_type_obj: tobj.__new__(tobj)
78
80
 
79
81
  if len(absolute_xpath) > 1: # 在处理内嵌的结构体
82
+ # 需要分配临时变量储存新对象,减少 getattr 和 setattr 开销
80
83
  current_field_name = absolute_xpath[-1]
81
84
 
82
85
  update_add_code(f"{name} = {parent_xname}.{current_field_name}")
83
86
  unpack_add_code(
84
- f"{parent_xname}.{current_field_name} = {name} = {new_method_alias}") # 组合赋值
87
+ f"{parent_xname}.{current_field_name} = {name} = {new_object_call}"
88
+ ) # 组合赋值
85
89
  else:
86
- unpack_add_code(f"{name} = {new_method_alias}") # 不需要临时变量存储根对象
90
+ unpack_add_code(f"{name} = {new_object_call}") # 不需要临时变量存储根对象
87
91
  # update 在这里不需要创建新对象
88
92
 
89
- post_inits.append((name, schema.type_obj))
93
+ post_inits.append((name, schema.type_obj)) # 对于每个结构体,都可能需要调用后处理钩子,这里只添加。
90
94
 
91
95
  for field in schema.fields: # 遍历字段
92
96
  field_xpath = f'{name}.{field.name}'
97
+ m = field.meta
93
98
 
94
99
  match field.type:
95
100
  case StructType() as s:
96
- if s.type_obj not in inner_classes:
97
- typename = next(type_vars)
101
+ if s.type_obj not in inner_classes: # 如果这个类型不在类型映射中,那么添加到里面,同时分配类名变量
102
+ typename = type_vars.new()
98
103
  inner_classes[s.type_obj] = typename
99
104
  else:
100
105
  typename = inner_classes[s.type_obj]
@@ -116,10 +121,10 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
116
121
  dtype = a.dtype
117
122
  nbytes = a.size
118
123
 
119
- array_name = next(tmp_vars)
124
+ array_name = tmp_vars.new()
120
125
 
121
126
  array_fields.append(
122
- (array_name, next(tmp_vars), field_xpath, dtype)
127
+ (array_name, tmp_vars.new(), field_xpath, dtype)
123
128
  )
124
129
 
125
130
  assigns.append(array_name)
@@ -127,15 +132,33 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
127
132
  offset += nbytes
128
133
 
129
134
  case BasicType() as b:
130
- assigns.append(field_xpath)
131
135
  offset += b.size
132
136
 
137
+ if m.trunc_nul:
138
+ assigns.append(var := tmp_vars.new())
139
+
140
+ if not m.encoding:
141
+ post_patches.append(fr'{var} = {var}.rstrip(b"\x00")')
142
+ else:
143
+ post_patches.append(fr'{var} = {var}.rstrip(b"\x00").decode({m.encoding!r})')
144
+
145
+ rewrites.append((var, field_xpath))
146
+
147
+ elif m.encoding:
148
+ assigns.append(var := tmp_vars.new())
149
+
150
+ post_patches.append(fr'{var} = {var}.decode({m.encoding!r})')
151
+ rewrites.append((var, field_xpath))
152
+
153
+ else:
154
+ assigns.append(field_xpath)
155
+
133
156
  case x:
134
157
  raise TypeError(f"Unexpected schema {x}")
135
158
 
136
159
  walk(
137
160
  schema=schema, meta=meta,
138
- absolute_xpath=('self', ),
161
+ absolute_xpath=('self',),
139
162
  )
140
163
 
141
164
  assigns_expr = ', '.join(assigns)
@@ -148,7 +171,7 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
148
171
  iter_unpack_inner.add_code(assign_code)
149
172
  iter_update_inner.add_code(assign_code)
150
173
 
151
- assign_code = f"{assigns_expr} = _unpack_from(buffer, offset)" # 生成 xxx_from 的赋值语句
174
+ assign_code = f"{assigns_expr} = _unpack_from(buffer, offset)" # 生成 unpack_from 的赋值语句
152
175
  unpack_from.add_code(assign_code)
153
176
  update_from.add_code(assign_code)
154
177
 
@@ -204,6 +227,18 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
204
227
  )
205
228
  node.target = ast.Name('values', ast.Store())
206
229
 
230
+ for i in post_patches:
231
+ batch_add_code(
232
+ [unpack, unpack_from, iter_unpack_inner, iter_update_inner, update, update_from],
233
+ i
234
+ )
235
+
236
+ for src, dst in rewrites:
237
+ batch_add_code(
238
+ [unpack, unpack_from, iter_unpack_inner, iter_update_inner, update, update_from],
239
+ f'{dst} = {src}'
240
+ )
241
+
207
242
  for field_xpath, type_obj in reversed(post_inits):
208
243
  if hasattr(type_obj, '__post_unpack__'):
209
244
  batch_add_code(
@@ -224,15 +259,15 @@ def create_unpack_methods(schema: StructType, meta: Meta = Meta()):
224
259
  iter_unpack_inner.add_code('yield self')
225
260
  iter_update_inner.add_code('yield self')
226
261
 
227
- for _, array_obj_var, *_ in array_fields:
262
+ for _, array_obj_var, *_ in array_fields: # 对于 iter update,每次返回完需要清理已有的数据,才能进行下一次的填充
228
263
  iter_update_inner.add_code(f'{array_obj_var}.clear()')
229
264
 
230
- print("Unpack\n", unpack.generate_code(), sep='', end='\n\n')
231
- print("Update\n", update.generate_code(), sep='', end='\n\n')
232
- print("Unpack_from\n", unpack_from.generate_code(), sep='', end='\n\n')
233
- print("Update_from\n", update_from.generate_code(), sep='', end='\n\n')
234
- print("Iter_unpack\n", iter_unpack.generate_code(), sep='', end='\n\n')
235
- print("Iter_update\n", iter_update.generate_code(), sep='', end='\n\n')
265
+ # print("Unpack\n", unpack.generate_code(), sep='', end='\n\n')
266
+ # print("Update\n", update.generate_code(), sep='', end='\n\n')
267
+ # print("Unpack_from\n", unpack_from.generate_code(), sep='', end='\n\n')
268
+ # print("Update_from\n", update_from.generate_code(), sep='', end='\n\n')
269
+ # print("Iter_unpack\n", iter_unpack.generate_code(), sep='', end='\n\n')
270
+ # print("Iter_update\n", iter_update.generate_code(), sep='', end='\n\n')
236
271
 
237
272
  generated_struct = struct.Struct(schema.full_format_string)
238
273
 
@@ -1,4 +1,5 @@
1
1
  import array
2
+ import builtins
2
3
  import sys
3
4
  from typing import get_origin, Annotated, get_args
4
5
 
@@ -34,6 +35,7 @@ def parse_annotation(anno, disallow_custom_type=False) -> tuple[ArrayType | Basi
34
35
 
35
36
  if hasattr(dtype, '__obstruct_schema__'):
36
37
  dt = dtype.__obstruct_schema__
38
+ assert isinstance(dt, BasicType)
37
39
 
38
40
  elif dtype in builtins_type_mapping:
39
41
  dt = builtins_type_mapping[dtype]
@@ -48,6 +50,14 @@ def parse_annotation(anno, disallow_custom_type=False) -> tuple[ArrayType | Basi
48
50
  meta.len
49
51
  ), meta
50
52
 
53
+ case builtins.str:
54
+ meta.encoding = 'utf-8'
55
+ return BasicType(f'{meta.len}s'), meta
56
+
57
+
58
+ case builtins.bytes:
59
+ return BasicType(f'{meta.len}s'), meta
60
+
51
61
  case t if hasattr(t, '__obstruct_schema__'):
52
62
  schema = t.__obstruct_schema__
53
63
 
@@ -0,0 +1,53 @@
1
+ def test_timeit(name, code, extra=''):
2
+ import timeit
3
+ import time
4
+ import dis
5
+
6
+ print()
7
+ print("Test: ", name)
8
+
9
+ code_obj = compile(code, '<string>', 'exec')
10
+
11
+ print("ByteCode for this Test")
12
+ dis.dis(code_obj)
13
+
14
+ res = timeit.repeat(
15
+ code,
16
+ number=NUMBER,
17
+ repeat=REPEAT,
18
+ timer=time.perf_counter_ns,
19
+ globals=globals(),
20
+
21
+ )
22
+
23
+
24
+ original_samples_len = len(res)
25
+
26
+ for i in range(TRIM_COUNT):
27
+ res.remove(max(res))
28
+ res.remove(min(res))
29
+
30
+ print("Original samples: ", original_samples_len)
31
+ print("Final samples:", len(res))
32
+
33
+ print(f'{NUMBER} steps finished in {sum(res) / (len(res)):.3f} ns in average. ({sum(res) / (len(res)) / NUMBER:.3f} ns per step)')
34
+
35
+ exec(extra, globals=globals()) if extra else None
36
+
37
+
38
+
39
+ NUMBER = 100000
40
+ TRIM_COUNT = 10
41
+ REPEAT = 100
42
+
43
+
44
+ new = object.__new__
45
+
46
+
47
+ class MyObject: ...
48
+
49
+
50
+ test_timeit('Call Directly', 'new(MyObject)')
51
+ test_timeit('Call from This Type', 'MyObject.__new__(MyObject)')
52
+ test_timeit('Call from object base', 'object.__new__(MyObject)')
53
+
@@ -0,0 +1,52 @@
1
+ import time
2
+ from array import array
3
+
4
+
5
+ def test_timeit(name, code, extra=''):
6
+ import timeit
7
+
8
+ res = timeit.repeat(
9
+ code,
10
+ number=NUMBER,
11
+ repeat=REPEAT,
12
+ timer=time.perf_counter_ns,
13
+ globals=globals()
14
+ )
15
+
16
+ print()
17
+
18
+ original_samples_len = len(res)
19
+
20
+ for i in range(TRIM_COUNT):
21
+ res.remove(max(res))
22
+ res.remove(min(res))
23
+
24
+ print("Test: ", name)
25
+ print("Original samples: ", original_samples_len)
26
+ print("Final samples:", len(res))
27
+
28
+ print(f'{NUMBER} steps finished in {sum(res) / (len(res)):.3f} ns in average. ({sum(res) / (len(res)) / NUMBER:.3f} ns per step)')
29
+
30
+ exec(extra, globals=globals()) if extra else None
31
+
32
+
33
+ # print("\n\nMyStruct:", MyStruct.__cstruct__.size)
34
+
35
+ NUMBER = 1000
36
+ REPEAT = 256
37
+ TRIM_COUNT = 32
38
+
39
+ import struct
40
+
41
+ s1 = struct.Struct('i400si')
42
+ s2 = struct.Struct('i400xi')
43
+
44
+ data1 = memoryview(b'\x01' * s1.size)
45
+ data2 = memoryview(b'\x02' * s2.size)
46
+
47
+
48
+ test_timeit('string', "_, a, _ = s1.unpack(data1); array('i').frombytes(a)")
49
+ test_timeit('pad', "s2.unpack(data2); array('i').frombytes(data2[4:-4])")
50
+
51
+
52
+
@@ -0,0 +1,21 @@
1
+ import dataclasses
2
+ from typing import Annotated
3
+
4
+ from obstruct import Struct, Meta
5
+
6
+
7
+ @dataclasses.dataclass()
8
+ class SomeStruct(Struct):
9
+ a: Annotated[bytes, Meta(len=10, trunc_nul=True)]
10
+ b: Annotated[str, Meta(len=10, trunc_nul=True)]
11
+
12
+
13
+ SomeStruct.__compile_struct__()
14
+
15
+
16
+
17
+ data = b'12345'.ljust(10, b'\x00') + '你好'.encode().ljust(10, b'\x00')
18
+ print(len(data), data)
19
+
20
+ print(SomeStruct.unpack_from(data))
21
+
@@ -0,0 +1,137 @@
1
+ "D:\Program Files\uv\uv.exe" run E:/.projects/obstruct/.venv/Scripts/python.exe E:\.projects\obstruct\test\test_speed.py
2
+ @classmethod
3
+ def unpack(cls, buffer):
4
+ self = __new__(cls)
5
+ self.a, self.b, self.c, self.d, self.e, self.f, self.g, self.h, self.i, self.j, self.k, self.l = _unpack(buffer)
6
+ return self
7
+ @classmethod
8
+ def unpack(cls, buffer):
9
+ buffer = memoryview(buffer)
10
+ self = __new__(cls)
11
+ self.x = B = __new__(A)
12
+ self.z = D = __new__(A)
13
+ B.a, B.b, B.c, B.d, B.e, B.f, B.g, B.h, B.i, B.j, B.k, B.l, D.a, D.b, D.c, D.d, D.e, D.f, D.g, D.h, D.i, D.j, D.k, D.l = _unpack(buffer)
14
+ self.y = _ = array('i')
15
+ _.frombytes(buffer[42:442])
16
+ B.__post_unpack__()
17
+ D.__post_unpack__()
18
+ return self
19
+ MyStruct size 42
20
+ AnotherStruct size 484
21
+ AnotherStruct: 484
22
+
23
+
24
+
25
+ Test: Pack
26
+ Results Length: 64
27
+ 1000 steps finished in 241676.562 ns
28
+ Average: 241.677 ns per step
29
+
30
+
31
+ Post Hook Called: False
32
+
33
+ Test: Unpack
34
+ Results Length: 64
35
+ 1000 steps finished in 574932.812 ns
36
+ Average: 574.933 ns per step
37
+
38
+
39
+ Post Hook Called: False
40
+
41
+ Test: Complex Pack
42
+ Results Length: 64
43
+ 1000 steps finished in 1274254.688 ns
44
+ Average: 1274.255 ns per step
45
+
46
+
47
+ Post Hook Called: False
48
+
49
+ Test: Complex Unpack
50
+ Results Length: 64
51
+ 1000 steps finished in 1800114.062 ns
52
+ Average: 1800.114 ns per step
53
+
54
+
55
+ Post Hook Called: True
56
+
57
+ Test: Update
58
+ Results Length: 64
59
+ 1000 steps finished in 346839.062 ns
60
+ Average: 346.839 ns per step
61
+
62
+
63
+ Post Hook Called: False
64
+
65
+ Test: Iter Unpack (100x)
66
+ Results Length: 64
67
+ 1000 steps finished in 40875885.938 ns
68
+ Average: 40875.886 ns per step
69
+
70
+
71
+ Post Hook Called: False
72
+
73
+ Test: Iter Update (100x)
74
+ Results Length: 64
75
+ 1000 steps finished in 30352537.500 ns
76
+ Average: 30352.537 ns per step
77
+
78
+
79
+ Post Hook Called: False
80
+
81
+ Test: Unpack From
82
+ Results Length: 64
83
+ 1000 steps finished in 480523.438 ns
84
+ Average: 480.523 ns per step
85
+
86
+
87
+ Post Hook Called: False
88
+
89
+ Test: Update From
90
+ Results Length: 64
91
+ 1000 steps finished in 285021.875 ns
92
+ Average: 285.022 ns per step
93
+
94
+
95
+ Post Hook Called: False
96
+
97
+ Test: Complex Update
98
+ Results Length: 64
99
+ 1000 steps finished in 813121.875 ns
100
+ Average: 813.122 ns per step
101
+
102
+
103
+ Post Hook Called: True
104
+
105
+ Test: Complex Iter Unpack (100x)
106
+ Results Length: 64
107
+ 1000 steps finished in 131775840.625 ns
108
+ Average: 131775.841 ns per step
109
+
110
+
111
+ Post Hook Called: True
112
+
113
+ Test: Complex Iter Update (100x)
114
+ Results Length: 64
115
+ 1000 steps finished in 88616110.938 ns
116
+ Average: 88616.111 ns per step
117
+
118
+
119
+ Post Hook Called: True
120
+
121
+ Test: Complex Unpack From (100x)
122
+ Results Length: 64
123
+ 1000 steps finished in 1467882.812 ns
124
+ Average: 1467.883 ns per step
125
+
126
+
127
+ Post Hook Called: True
128
+
129
+ Test: Complex Update From (100x)
130
+ Results Length: 64
131
+ 1000 steps finished in 909548.438 ns
132
+ Average: 909.548 ns per step
133
+
134
+
135
+ Post Hook Called: True
136
+
137
+ 进程已结束,退出代码为 0
@@ -1 +0,0 @@
1
- version = "0.6.0"
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