obstruct 0.2.4__tar.gz → 0.2.6__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.4 → obstruct-0.2.6}/.idea/workspace.xml +23 -9
- {obstruct-0.2.4 → obstruct-0.2.6}/PKG-INFO +1 -1
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/__init__.py +0 -1
- obstruct-0.2.6/src/obstruct/__version__.py +1 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/aot/base.py +4 -1
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/aot/pack.py +5 -6
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/aot/unpack.py +58 -24
- obstruct-0.2.6/src/obstruct/struct_type.py +132 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/test/test_speed.py +12 -4
- obstruct-0.2.4/src/obstruct/__version__.py +0 -1
- obstruct-0.2.4/src/obstruct/decorator.py +0 -34
- obstruct-0.2.4/src/obstruct/struct_type.py +0 -52
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/.gitignore +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/inspectionProfiles/Project_Default.xml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/inspectionProfiles/profiles_settings.xml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/misc.xml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/modules.xml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/obstruct.iml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/.idea/vcs.xml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/README.md +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/pyproject.toml +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/aot/__init__.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/aot/aot.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/methods.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/types/__init__.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/types/base.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/types/types.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/src/obstruct/types/types.pyi +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/test/test.py +0 -0
- {obstruct-0.2.4 → obstruct-0.2.6}/uv.lock +0 -0
|
@@ -4,13 +4,9 @@
|
|
|
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="
|
|
8
|
-
<change beforePath="$PROJECT_DIR$/
|
|
9
|
-
<change beforePath="$PROJECT_DIR$/src/obstruct/
|
|
10
|
-
<change beforePath="$PROJECT_DIR$/src/obstruct/aot/pack.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/aot/pack.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$/src/obstruct/methods.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/methods.py" afterDir="false" />
|
|
13
|
-
<change beforePath="$PROJECT_DIR$/test/test_speed.py" beforeDir="false" afterPath="$PROJECT_DIR$/test/test_speed.py" afterDir="false" />
|
|
7
|
+
<list default="true" id="b482b6b4-48ce-4be1-bf3c-a72ec254fa35" name="更改" comment="增强 array 类型的支持 x2">
|
|
8
|
+
<change beforePath="$PROJECT_DIR$/src/obstruct/__version__.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/__version__.py" afterDir="false" />
|
|
9
|
+
<change beforePath="$PROJECT_DIR$/src/obstruct/struct_type.py" beforeDir="false" afterPath="$PROJECT_DIR$/src/obstruct/struct_type.py" afterDir="false" />
|
|
14
10
|
</list>
|
|
15
11
|
<option name="SHOW_DIALOG" value="false" />
|
|
16
12
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
@@ -171,7 +167,23 @@
|
|
|
171
167
|
<option name="project" value="LOCAL" />
|
|
172
168
|
<updated>1786870314349</updated>
|
|
173
169
|
</task>
|
|
174
|
-
<
|
|
170
|
+
<task id="LOCAL-00005" summary="增强 array 类型的支持">
|
|
171
|
+
<option name="closed" value="true" />
|
|
172
|
+
<created>1786875274813</created>
|
|
173
|
+
<option name="number" value="00005" />
|
|
174
|
+
<option name="presentableId" value="LOCAL-00005" />
|
|
175
|
+
<option name="project" value="LOCAL" />
|
|
176
|
+
<updated>1786875274813</updated>
|
|
177
|
+
</task>
|
|
178
|
+
<task id="LOCAL-00006" summary="增强 array 类型的支持 x2">
|
|
179
|
+
<option name="closed" value="true" />
|
|
180
|
+
<created>1786883898686</created>
|
|
181
|
+
<option name="number" value="00006" />
|
|
182
|
+
<option name="presentableId" value="LOCAL-00006" />
|
|
183
|
+
<option name="project" value="LOCAL" />
|
|
184
|
+
<updated>1786883898686</updated>
|
|
185
|
+
</task>
|
|
186
|
+
<option name="localTasksCounter" value="7" />
|
|
175
187
|
<servers />
|
|
176
188
|
</component>
|
|
177
189
|
<component name="TypeScriptGeneratedFilesManager">
|
|
@@ -193,6 +205,8 @@
|
|
|
193
205
|
<MESSAGE value="增强类型系统,撰写 README.md" />
|
|
194
206
|
<MESSAGE value="补充:基准测试脚本" />
|
|
195
207
|
<MESSAGE value="修正了一些命名" />
|
|
196
|
-
<
|
|
208
|
+
<MESSAGE value="增强 array 类型的支持" />
|
|
209
|
+
<MESSAGE value="增强 array 类型的支持 x2" />
|
|
210
|
+
<option name="LAST_COMMIT_MESSAGE" value="增强 array 类型的支持 x2" />
|
|
197
211
|
</component>
|
|
198
212
|
</project>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
version = "0.2.6"
|
|
@@ -5,7 +5,6 @@ import itertools
|
|
|
5
5
|
import typing
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from obstruct.methods import get_ctype
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
class _objective_node_operator:
|
|
@@ -31,6 +30,9 @@ class _objective_node_operator:
|
|
|
31
30
|
self._node.body.append(tree)
|
|
32
31
|
return tree
|
|
33
32
|
|
|
33
|
+
def add_codes(self, codes: typing.Iterable[str]):
|
|
34
|
+
[self.add_code(c) for c in codes]
|
|
35
|
+
|
|
34
36
|
def insert_code(self, index, code: str):
|
|
35
37
|
tree = ast.parse(code)
|
|
36
38
|
if isinstance(tree, ast.Module):
|
|
@@ -135,6 +137,7 @@ def get_array_spec(tp):
|
|
|
135
137
|
|
|
136
138
|
|
|
137
139
|
def get_array_ctype(tp) -> str | None | Any:
|
|
140
|
+
from obstruct.methods import get_ctype
|
|
138
141
|
return get_ctype(get_array_spec(tp)[0])
|
|
139
142
|
|
|
140
143
|
|
|
@@ -6,16 +6,16 @@ import typing
|
|
|
6
6
|
|
|
7
7
|
from .base import unique_name_generator, is_struct, is_array, get_array_ctype, to_tuple_expression, function_def, \
|
|
8
8
|
get_array_length
|
|
9
|
-
from ..methods import get_ctype
|
|
10
9
|
|
|
11
10
|
if typing.TYPE_CHECKING:
|
|
12
11
|
from .. import struct_type
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
def create_pack_methods(cls: struct_type.Struct):
|
|
14
|
+
def create_pack_methods(cls: type[struct_type.Struct]):
|
|
16
15
|
"""
|
|
17
16
|
Generate `pack` and `pack_into` method
|
|
18
17
|
"""
|
|
18
|
+
from obstruct.methods import get_ctype
|
|
19
19
|
with (
|
|
20
20
|
function_def('pack(self)') as pack,
|
|
21
21
|
function_def('pack_into(self, buffer, offset=0)') as pack_into,
|
|
@@ -24,8 +24,6 @@ def create_pack_methods(cls: struct_type.Struct):
|
|
|
24
24
|
flat_fields = []
|
|
25
25
|
array_fields = []
|
|
26
26
|
|
|
27
|
-
pack_into.add_code('buffer = memoryview(buffer)')
|
|
28
|
-
|
|
29
27
|
def add_code(code):
|
|
30
28
|
pack.add_code(code)
|
|
31
29
|
pack_into.add_code(code)
|
|
@@ -62,6 +60,7 @@ def create_pack_methods(cls: struct_type.Struct):
|
|
|
62
60
|
expression = to_tuple_expression(flat_fields, prefix='')
|
|
63
61
|
|
|
64
62
|
if array_fields: # 如果有数组字段,我们需要手动操作缓冲区
|
|
63
|
+
pack_into.insert_code(0, "buffer = memoryview(buffer)")
|
|
65
64
|
pack.add_code(f"buffer = bytearray({cls.__cstruct__.size})") # 初始化缓冲区
|
|
66
65
|
for array_name, offset, size in array_fields:
|
|
67
66
|
add_code(f"buffer[{offset}:{offset + size}] = memoryview({array_name}).cast('B')")
|
|
@@ -72,8 +71,8 @@ def create_pack_methods(cls: struct_type.Struct):
|
|
|
72
71
|
pack.add_return(f"_pack({expression})")
|
|
73
72
|
|
|
74
73
|
pack_into.add_code(f"_pack_into(buffer, offset, {expression})")
|
|
75
|
-
print(pack.generate_code()) # Debug
|
|
76
|
-
print(pack_into.generate_code()) # Debug
|
|
74
|
+
# print(pack.generate_code()) # Debug
|
|
75
|
+
# print(pack_into.generate_code()) # Debug
|
|
77
76
|
return (
|
|
78
77
|
pack.exec({'_pack': cls.__cstruct__.pack, '_pack_into': cls.__cstruct__.pack_into, 'array': array.array}),
|
|
79
78
|
pack_into.exec({'_pack_into': cls.__cstruct__.pack_into, 'array': array.array})
|
|
@@ -5,13 +5,14 @@ import struct
|
|
|
5
5
|
import typing
|
|
6
6
|
|
|
7
7
|
from .base import unique_name_generator, is_struct, is_array, get_array_ctype, function_def, for_stmt, get_array_length
|
|
8
|
-
from ..methods import get_ctype
|
|
9
8
|
|
|
10
9
|
if typing.TYPE_CHECKING:
|
|
11
10
|
from .. import struct_type
|
|
12
11
|
|
|
13
12
|
|
|
14
13
|
def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
14
|
+
from ..methods import get_ctype
|
|
15
|
+
|
|
15
16
|
with (
|
|
16
17
|
function_def('unpack(cls, buffer)') as unpack,
|
|
17
18
|
function_def('update(self, buffer)') as update,
|
|
@@ -32,7 +33,7 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
32
33
|
def update_add_code(code):
|
|
33
34
|
update.add_code(code)
|
|
34
35
|
update_from.add_code(code)
|
|
35
|
-
|
|
36
|
+
iter_update.insert_code(1, code)
|
|
36
37
|
|
|
37
38
|
def unpack_add_code(code):
|
|
38
39
|
unpack.add_code(code)
|
|
@@ -42,10 +43,10 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
42
43
|
assigns = [] # 赋值目标列表
|
|
43
44
|
inner_classes = {} # 内嵌的结构体的类型映射(所有内嵌结构体的类型在生成代码时都会被代替为无意义的名称)
|
|
44
45
|
|
|
45
|
-
iter_update.add_code("self = cls
|
|
46
|
+
iter_update.add_code("self = __new__(cls) if obj is None else obj")
|
|
46
47
|
|
|
47
|
-
iter_unpack_inner = for_stmt("values in _iter_unpack(buffer)")
|
|
48
|
-
iter_update_inner = for_stmt("values in _iter_unpack(buffer)")
|
|
48
|
+
iter_unpack_inner = for_stmt("i, values in enumerate(_iter_unpack(buffer))")
|
|
49
|
+
iter_update_inner = for_stmt("i, values in enumerate(_iter_unpack(buffer))")
|
|
49
50
|
|
|
50
51
|
iter_unpack.add_node(iter_unpack_inner.node)
|
|
51
52
|
iter_update.add_node(iter_update_inner.node)
|
|
@@ -55,7 +56,6 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
55
56
|
def walk(fields, path, parent_name=None, current_class_name='cls', offset=0):
|
|
56
57
|
name = 'self'
|
|
57
58
|
|
|
58
|
-
|
|
59
59
|
if len(path) > 1: # 在处理内嵌的结构体
|
|
60
60
|
name = next(ung) # 临时变量的名称
|
|
61
61
|
update_add_code(f"{name} = {parent_name}.{path[-1]}")
|
|
@@ -68,6 +68,9 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
68
68
|
for field in fields: # 遍历字段
|
|
69
69
|
field_xpath = f'{name}.{field.name}'
|
|
70
70
|
|
|
71
|
+
# ctype = get_ctype(field.type)
|
|
72
|
+
# print(f"当前字段:{field_xpath},类型:{field.type} '{ctype}',偏移量:{offset}+{struct.calcsize(ctype)}")
|
|
73
|
+
|
|
71
74
|
if is_struct(field.type): # 是个内嵌的结构体
|
|
72
75
|
if field.type not in inner_classes: # 如果这个内嵌的结构体类型不在已知的结构体中(没有分配类名称)
|
|
73
76
|
type_name = next(ung) # 生成一个唯一的类名
|
|
@@ -76,16 +79,16 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
76
79
|
type_name = inner_classes[field.type] # 如果在,那么提取
|
|
77
80
|
|
|
78
81
|
walk(field.type.__cfields__, path + (field.name,), name, type_name, offset)
|
|
82
|
+
offset += field.type.__cstruct__.size
|
|
79
83
|
|
|
80
84
|
elif is_array(field.type):
|
|
81
85
|
dtype = get_array_ctype(field.type)
|
|
82
86
|
nbytes = get_array_length(field.type) * struct.calcsize(dtype)
|
|
83
|
-
array_fields.append((field_xpath, dtype, offset, nbytes))
|
|
84
|
-
|
|
87
|
+
array_fields.append((next(ung), field_xpath, dtype, offset, nbytes))
|
|
88
|
+
offset += nbytes
|
|
85
89
|
else:
|
|
86
90
|
assigns.append(field_xpath)
|
|
87
|
-
|
|
88
|
-
offset += struct.calcsize(get_ctype(field.type))
|
|
91
|
+
offset += struct.calcsize(get_ctype(field.type))
|
|
89
92
|
|
|
90
93
|
walk(cls.__cfields__, ('A',))
|
|
91
94
|
|
|
@@ -104,13 +107,40 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
104
107
|
unpack_from.add_code(assign_code)
|
|
105
108
|
update_from.add_code(assign_code)
|
|
106
109
|
|
|
107
|
-
for field_xpath, dtype, offset, nbytes in array_fields:
|
|
108
|
-
|
|
110
|
+
for n, field_xpath, dtype, offset, nbytes in array_fields:
|
|
111
|
+
size = cls.__cstruct__.size
|
|
112
|
+
|
|
113
|
+
update.add_codes([
|
|
114
|
+
f"{field_xpath}.clear()",
|
|
115
|
+
f"{field_xpath}.frombytes(buffer[{offset}:{offset + nbytes}])",
|
|
116
|
+
])
|
|
117
|
+
|
|
118
|
+
update_from.add_codes([
|
|
119
|
+
f"{field_xpath}.clear()",
|
|
120
|
+
f"{field_xpath}.frombytes(buffer[{offset}+offset:{offset + nbytes}+offset])",
|
|
121
|
+
])
|
|
122
|
+
|
|
123
|
+
unpack.add_codes([
|
|
124
|
+
f'{field_xpath} = _ = array({dtype!r})',
|
|
125
|
+
f'_.frombytes(buffer[{offset}:{offset + nbytes}])',
|
|
126
|
+
])
|
|
127
|
+
unpack_from.add_codes([
|
|
109
128
|
f'{field_xpath} = _ = array({dtype!r})',
|
|
110
|
-
f'_.frombytes(buffer[{offset}:{offset+nbytes}])',
|
|
111
|
-
]
|
|
112
|
-
|
|
113
|
-
|
|
129
|
+
f'_.frombytes(buffer[{offset}+offset:{offset + nbytes}+offset])',
|
|
130
|
+
])
|
|
131
|
+
|
|
132
|
+
iter_update_inner.add_codes([
|
|
133
|
+
f'offset = {size} * i',
|
|
134
|
+
f'{n}.frombytes(buffer[{offset}+offset:{offset + nbytes}+offset])'
|
|
135
|
+
])
|
|
136
|
+
|
|
137
|
+
iter_unpack_inner.add_codes([
|
|
138
|
+
f"{field_xpath} = _ = array({dtype!r})",
|
|
139
|
+
f"offset = i*{size}",
|
|
140
|
+
f"_.frombytes(buffer[{offset}+offset:{offset + nbytes}+offset])"
|
|
141
|
+
])
|
|
142
|
+
|
|
143
|
+
iter_update.insert_code(1, f"{n} = {field_xpath} = array({dtype!r})")
|
|
114
144
|
|
|
115
145
|
if array_fields:
|
|
116
146
|
unpack.insert_code(0, 'buffer = memoryview(buffer)')
|
|
@@ -120,15 +150,18 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
120
150
|
unpack_from.add_return('self')
|
|
121
151
|
update_from.add_return('self')
|
|
122
152
|
|
|
123
|
-
iter_unpack_inner.add_code('yield self')
|
|
153
|
+
iter_unpack_inner.add_code('yield self')
|
|
124
154
|
iter_update_inner.add_code('yield self')
|
|
125
155
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
# print(
|
|
130
|
-
|
|
131
|
-
|
|
156
|
+
for n, *_ in array_fields:
|
|
157
|
+
iter_update_inner.add_code(f'{n}.clear()')
|
|
158
|
+
|
|
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())
|
|
132
165
|
|
|
133
166
|
globals = {
|
|
134
167
|
v: k for k, v in inner_classes.items() # 翻转字典
|
|
@@ -139,7 +172,8 @@ def create_unpack_methods(cls: type[struct_type.Struct]):
|
|
|
139
172
|
_unpack=cls.__cstruct__.unpack,
|
|
140
173
|
_unpack_from=cls.__cstruct__.unpack_from,
|
|
141
174
|
_iter_unpack=cls.__cstruct__.iter_unpack,
|
|
142
|
-
array=array.array
|
|
175
|
+
array=array.array,
|
|
176
|
+
enumerate=enumerate
|
|
143
177
|
))
|
|
144
178
|
|
|
145
179
|
return unpack.exec(
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import struct
|
|
3
|
+
import typing
|
|
4
|
+
from collections.abc import Buffer
|
|
5
|
+
from typing import Self, Generator, Any, ClassVar
|
|
6
|
+
|
|
7
|
+
from obstruct.aot import create_pack_methods, create_unpack_methods
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class CType(typing.Protocol):
|
|
11
|
+
__ctype__: str
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class StructMeta(type):
|
|
15
|
+
__struct_methods__ = {
|
|
16
|
+
'pack',
|
|
17
|
+
'pack_into',
|
|
18
|
+
|
|
19
|
+
'unpack',
|
|
20
|
+
'unpack_from',
|
|
21
|
+
'iter_unpack',
|
|
22
|
+
|
|
23
|
+
'update',
|
|
24
|
+
'update_from',
|
|
25
|
+
'iter_update',
|
|
26
|
+
|
|
27
|
+
'__cstruct__',
|
|
28
|
+
'__ctype__',
|
|
29
|
+
'__cfields__',
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
def __new__(cls, name, bases, attrs, *, endian='little'):
|
|
33
|
+
slots = set(attrs.get('__slots__', ()))
|
|
34
|
+
|
|
35
|
+
annotations = attrs.get('__annotations__')
|
|
36
|
+
|
|
37
|
+
if annotations is None:
|
|
38
|
+
import annotationlib
|
|
39
|
+
annotate = annotationlib.get_annotate_from_class_namespace(attrs)
|
|
40
|
+
assert annotate is not None
|
|
41
|
+
|
|
42
|
+
annotations = annotationlib.call_annotate_function(
|
|
43
|
+
annotate,
|
|
44
|
+
format=annotationlib.Format.STRING
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
slots |= {k for k in annotations.keys() if not k.startswith('_')}
|
|
48
|
+
|
|
49
|
+
attrs['__slots__'] = slots
|
|
50
|
+
attrs['__cendian__'] = endian
|
|
51
|
+
|
|
52
|
+
return super().__new__(cls, name, bases, attrs)
|
|
53
|
+
|
|
54
|
+
def __compile_struct__(cls):
|
|
55
|
+
from obstruct.methods import to_format_string
|
|
56
|
+
fields = [i for i in dataclasses.fields(cls) if i.init]
|
|
57
|
+
real_annotations = typing.get_type_hints(cls)
|
|
58
|
+
|
|
59
|
+
for field in fields:
|
|
60
|
+
# print(cls, field.name, repr(field.type))
|
|
61
|
+
if isinstance(field.type, str):
|
|
62
|
+
field.type = real_annotations[field.name]
|
|
63
|
+
|
|
64
|
+
cls.__ctype__ = to_format_string([field.type for field in fields])
|
|
65
|
+
cls.__cfields__ = tuple(fields) # type: tuple[dataclasses.Field, ...]
|
|
66
|
+
cls.__cstruct__ = struct.Struct(
|
|
67
|
+
endian_mapping.get(cls.__cendian__, cls.__cendian__) + cls.__ctype__) # 构造 Struct 类型
|
|
68
|
+
|
|
69
|
+
cls.pack, cls.pack_into = create_pack_methods(cls)
|
|
70
|
+
cls.unpack, cls.update, cls.unpack_from, cls.update_from, cls.iter_unpack, cls.iter_update = (
|
|
71
|
+
create_unpack_methods(cls)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
endian_mapping = {
|
|
76
|
+
'little': '<',
|
|
77
|
+
'big': '>',
|
|
78
|
+
'machine': '=',
|
|
79
|
+
'network': '!'
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Struct(metaclass=StructMeta):
|
|
84
|
+
"""
|
|
85
|
+
代表C语言结构体的Python类。
|
|
86
|
+
提供了将结构体对象与字节序列之间互相转换的功能,
|
|
87
|
+
包括打包、解包、更新等操作。
|
|
88
|
+
"""
|
|
89
|
+
|
|
90
|
+
__cfields__: ClassVar[tuple[dataclasses.Field]]
|
|
91
|
+
__cstruct__: ClassVar[struct.Struct]
|
|
92
|
+
__cendian__: ClassVar[str]
|
|
93
|
+
|
|
94
|
+
if typing.TYPE_CHECKING:
|
|
95
|
+
def pack(self) -> bytes:
|
|
96
|
+
...
|
|
97
|
+
|
|
98
|
+
def pack_into(self, buffer: Buffer, offset=0) -> None:
|
|
99
|
+
...
|
|
100
|
+
|
|
101
|
+
@classmethod
|
|
102
|
+
def unpack(cls, buffer) -> Self:
|
|
103
|
+
...
|
|
104
|
+
|
|
105
|
+
@classmethod
|
|
106
|
+
def unpack_from(cls, buffer, offset=0) -> Self:
|
|
107
|
+
...
|
|
108
|
+
|
|
109
|
+
@classmethod
|
|
110
|
+
def iter_unpack(cls, buffer) -> Generator[Self, None, None]:
|
|
111
|
+
...
|
|
112
|
+
|
|
113
|
+
@classmethod
|
|
114
|
+
def iter_update(cls, buffer) -> Generator[Self, None, None]:
|
|
115
|
+
...
|
|
116
|
+
|
|
117
|
+
def update(self, buffer: Buffer) -> None:
|
|
118
|
+
...
|
|
119
|
+
|
|
120
|
+
def update_from(self, buffer: Buffer, offset=0) -> None:
|
|
121
|
+
...
|
|
122
|
+
|
|
123
|
+
def to_dict(self) -> dict[str, Any]:
|
|
124
|
+
return dataclasses.asdict(self)
|
|
125
|
+
|
|
126
|
+
def __getattr__(self, item):
|
|
127
|
+
ty = type(self)
|
|
128
|
+
if item in ty.__struct_methods__:
|
|
129
|
+
ty.__compile_struct__()
|
|
130
|
+
return getattr(self, item)
|
|
131
|
+
|
|
132
|
+
raise AttributeError(item)
|
|
@@ -6,8 +6,7 @@ from typing import Annotated
|
|
|
6
6
|
import obstruct as obs
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
@
|
|
10
|
-
@dataclasses.dataclass(slots=True)
|
|
9
|
+
@dataclasses.dataclass()
|
|
11
10
|
class MyStruct(obs.Struct):
|
|
12
11
|
a: int
|
|
13
12
|
b: int
|
|
@@ -23,17 +22,26 @@ class MyStruct(obs.Struct):
|
|
|
23
22
|
l: obs.long_long
|
|
24
23
|
|
|
25
24
|
|
|
26
|
-
@
|
|
27
|
-
@dataclasses.dataclass(slots=True)
|
|
25
|
+
@dataclasses.dataclass()
|
|
28
26
|
class AnotherStruct(obs.Struct):
|
|
29
27
|
x: MyStruct
|
|
30
28
|
y: Annotated[array[int], 100]
|
|
31
29
|
z: MyStruct
|
|
32
30
|
|
|
33
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
|
+
exit()
|
|
40
|
+
|
|
34
41
|
data = MyStruct(
|
|
35
42
|
1,2,1.0,4,5,6,7,8,9,b'8',1,2
|
|
36
43
|
)
|
|
44
|
+
|
|
37
45
|
packed_data = data.pack()
|
|
38
46
|
|
|
39
47
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
version = "0.2.4"
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import typing
|
|
3
|
-
|
|
4
|
-
from . import struct_type
|
|
5
|
-
from .aot import pack, unpack
|
|
6
|
-
from .methods import to_format_string
|
|
7
|
-
|
|
8
|
-
endian_control_characters = tuple('<@=!>')
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def struct(maybe_cls=None, *, endian='@'):
|
|
12
|
-
def decorator[T](cls: T) -> T:
|
|
13
|
-
import struct
|
|
14
|
-
fields = dataclasses.fields(cls)
|
|
15
|
-
real_annotations = typing.get_type_hints(cls)
|
|
16
|
-
|
|
17
|
-
for field in fields:
|
|
18
|
-
# print(cls, field.name, repr(field.type))
|
|
19
|
-
if isinstance(field.type, str):
|
|
20
|
-
field.type = real_annotations[field.name]
|
|
21
|
-
|
|
22
|
-
cls.__ctype__ = to_format_string([field.type for field in fields])
|
|
23
|
-
cls.__cfields__ = tuple(fields) # type: tuple[dataclasses.Field, ...]
|
|
24
|
-
cls.__cstruct__ = struct.Struct(endian + cls.__ctype__) # 构造 Struct 类型
|
|
25
|
-
cls.__cendian__ = endian
|
|
26
|
-
|
|
27
|
-
cls.pack, cls.pack_into = pack.create_pack_methods(cls)
|
|
28
|
-
cls.unpack, cls.update, cls.unpack_from, cls.update_from, cls.iter_unpack, cls.iter_update = (
|
|
29
|
-
unpack.create_unpack_methods(cls)
|
|
30
|
-
)
|
|
31
|
-
|
|
32
|
-
return cls
|
|
33
|
-
|
|
34
|
-
return decorator(maybe_cls) if maybe_cls is not None else decorator
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
import dataclasses
|
|
2
|
-
import struct
|
|
3
|
-
import typing
|
|
4
|
-
from collections.abc import Buffer
|
|
5
|
-
from typing import Self, Generator, Any, ClassVar
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
class CType(typing.Protocol):
|
|
9
|
-
__ctype__: str
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Struct(CType):
|
|
13
|
-
"""
|
|
14
|
-
代表C语言结构体的Python类。
|
|
15
|
-
提供了将结构体对象与字节序列之间互相转换的功能,
|
|
16
|
-
包括打包、解包、更新等操作。
|
|
17
|
-
"""
|
|
18
|
-
|
|
19
|
-
__cfields__: ClassVar[tuple[dataclasses.Field]]
|
|
20
|
-
__cstruct__: ClassVar[struct.Struct]
|
|
21
|
-
__cendian__: ClassVar[str]
|
|
22
|
-
|
|
23
|
-
def pack(self) -> bytes:
|
|
24
|
-
...
|
|
25
|
-
|
|
26
|
-
def pack_into(self, buffer: Buffer, offset=0) -> None:
|
|
27
|
-
...
|
|
28
|
-
|
|
29
|
-
@classmethod
|
|
30
|
-
def unpack(cls, buffer) -> Self:
|
|
31
|
-
...
|
|
32
|
-
|
|
33
|
-
@classmethod
|
|
34
|
-
def unpack_from(cls, buffer, offset=0) -> Self:
|
|
35
|
-
...
|
|
36
|
-
|
|
37
|
-
@classmethod
|
|
38
|
-
def iter_unpack(cls, buffer) -> Generator[Self, None, None]:
|
|
39
|
-
...
|
|
40
|
-
|
|
41
|
-
@classmethod
|
|
42
|
-
def iter_update(cls, buffer) -> Generator[Self, None, None]:
|
|
43
|
-
...
|
|
44
|
-
|
|
45
|
-
def update(self, buffer: Buffer) -> None:
|
|
46
|
-
...
|
|
47
|
-
|
|
48
|
-
def update_from(self, buffer: Buffer, offset=0) -> None:
|
|
49
|
-
...
|
|
50
|
-
|
|
51
|
-
def to_dict(self) -> dict[str, Any]:
|
|
52
|
-
return dataclasses.asdict(self)
|
|
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
|