@@ -48,8 +48,11 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
48
48
49
49
self .consts = {}
50
50
self .lookups = {}
51
+ self .types = {}
52
+ self .typedefs = {}
51
53
# fmt: off
52
- self .typedefs = {
54
+
55
+ initial_types = {
53
56
# Internal types
54
57
"int8" : self ._make_packed_type ("int8" , "b" , int ),
55
58
"uint8" : self ._make_packed_type ("uint8" , "B" , int ),
@@ -93,6 +96,21 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
93
96
"signed long long" : "long long" ,
94
97
"unsigned long long" : "uint64" ,
95
98
99
+ # Other convenience types
100
+ "u1" : "uint8" ,
101
+ "u2" : "uint16" ,
102
+ "u4" : "uint32" ,
103
+ "u8" : "uint64" ,
104
+ "u16" : "uint128" ,
105
+ "__u8" : "uint8" ,
106
+ "__u16" : "uint16" ,
107
+ "__u32" : "uint32" ,
108
+ "__u64" : "uint64" ,
109
+ "uchar" : "uint8" ,
110
+ "ushort" : "unsigned short" ,
111
+ "uint" : "unsigned int" ,
112
+ "ulong" : "unsigned long" ,
113
+
96
114
# Windows types
97
115
"BYTE" : "uint8" ,
98
116
"CHAR" : "char" ,
@@ -160,24 +178,12 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
160
178
"_DWORD" : "uint32" ,
161
179
"_QWORD" : "uint64" ,
162
180
"_OWORD" : "uint128" ,
163
-
164
- # Other convenience types
165
- "u1" : "uint8" ,
166
- "u2" : "uint16" ,
167
- "u4" : "uint32" ,
168
- "u8" : "uint64" ,
169
- "u16" : "uint128" ,
170
- "__u8" : "uint8" ,
171
- "__u16" : "uint16" ,
172
- "__u32" : "uint32" ,
173
- "__u64" : "uint64" ,
174
- "uchar" : "uint8" ,
175
- "ushort" : "unsigned short" ,
176
- "uint" : "unsigned int" ,
177
- "ulong" : "unsigned long" ,
178
181
}
179
182
# fmt: on
180
183
184
+ for name , type_ in initial_types .items ():
185
+ self .add_type (name , type_ )
186
+
181
187
pointer = pointer or ("uint64" if sys .maxsize > 2 ** 32 else "uint32" )
182
188
self .pointer = self .resolve (pointer )
183
189
self ._anonymous_count = 0
@@ -188,37 +194,71 @@ def __getattr__(self, attr: str) -> Any:
188
194
except KeyError :
189
195
pass
190
196
197
+ try :
198
+ return self .types [attr ]
199
+ except KeyError :
200
+ pass
201
+
191
202
try :
192
203
return self .resolve (self .typedefs [attr ])
193
204
except KeyError :
194
205
pass
195
206
196
- raise AttributeError ( f"Invalid attribute: { attr } " )
207
+ return super (). __getattribute__ ( attr )
197
208
198
209
def _next_anonymous (self ) -> str :
199
210
name = f"__anonymous_{ self ._anonymous_count } __"
200
211
self ._anonymous_count += 1
201
212
return name
202
213
214
+ def _add_attr (self , name : str , value : Any , replace : bool = False ) -> None :
215
+ if not replace and (name in self .__dict__ and self .__dict__ [name ] != value ):
216
+ raise ValueError (f"Attribute already exists: { name } " )
217
+ setattr (self , name , value )
218
+
203
219
def add_type (self , name : str , type_ : MetaType | str , replace : bool = False ) -> None :
204
220
"""Add a type or type reference.
205
221
206
222
Only use this method when creating type aliases or adding already bound types.
223
+ All types will be resolved to their actual type objects prior to being added.
224
+ Use :func:`add_typedef` to add type references.
207
225
208
226
Args:
209
227
name: Name of the type to be added.
210
228
type_: The type to be added. Can be a str reference to another type or a compatible type class.
229
+ If a str is given, it will be resolved to the actual type object.
211
230
212
231
Raises:
213
232
ValueError: If the type already exists.
214
233
"""
215
- if not replace and (name in self .typedefs and self .resolve (self .typedefs [name ]) != self .resolve (type_ )):
234
+ typeobj = self .resolve (type_ )
235
+ if not replace and (name in self .types and self .types [name ] != typeobj ):
216
236
raise ValueError (f"Duplicate type: { name } " )
217
237
218
- self .typedefs [name ] = type_
238
+ self .types [name ] = typeobj
239
+ self ._add_attr (name , typeobj , replace = replace )
219
240
220
241
addtype = add_type
221
242
243
+ def add_typedef (self , name : str , type_ : str , replace : bool = False ) -> None :
244
+ """Add a type reference.
245
+
246
+ Use this method to add type references to this cstruct instance. This is largely a convenience method for the
247
+ internal :func:`add_type` method.
248
+
249
+ Args:
250
+ name: Name of the type to be added.
251
+ type_: The type reference to be added.
252
+ replace: Whether to replace the type if it already exists.
253
+ """
254
+ if not isinstance (type_ , str ):
255
+ raise TypeError ("Type reference must be a string" )
256
+
257
+ if not replace and (name in self .typedefs and self .resolve (self .typedefs [name ]) != self .resolve (type_ )):
258
+ raise ValueError (f"Duplicate type: { name } " )
259
+
260
+ self .typedefs [name ] = type_
261
+
222
262
def add_custom_type (
223
263
self , name : str , type_ : MetaType , size : int | None = None , alignment : int | None = None , ** kwargs
224
264
) -> None :
@@ -236,6 +276,16 @@ def add_custom_type(
236
276
"""
237
277
self .add_type (name , self ._make_type (name , (type_ ,), size , alignment = alignment , attrs = kwargs ))
238
278
279
+ def add_const (self , name : str , value : Any ) -> None :
280
+ """Add a constant value.
281
+
282
+ Args:
283
+ name: Name of the constant to be added.
284
+ value: The value of the constant.
285
+ """
286
+ self .consts [name ] = value
287
+ self ._add_attr (name , value , replace = True )
288
+
239
289
def load (self , definition : str , deftype : int | None = None , ** kwargs ) -> cstruct :
240
290
"""Parse structures from the given definitions using the given definition type.
241
291
@@ -307,14 +357,14 @@ def resolve(self, name: str) -> MetaType:
307
357
return type_name
308
358
309
359
for _ in range (10 ):
360
+ if type_name in self .types :
361
+ return self .types [type_name ]
362
+
310
363
if type_name not in self .typedefs :
311
364
raise ResolveError (f"Unknown type { name } " )
312
365
313
366
type_name = self .typedefs [type_name ]
314
367
315
- if not isinstance (type_name , str ):
316
- return type_name
317
-
318
368
raise ResolveError (f"Recursion limit exceeded while resolving type { name } " )
319
369
320
370
def _make_type (
0 commit comments