@@ -52,9 +52,12 @@ def __init__(self, load: str = "", *, endian: str = "<", pointer: str | None = N
5252
5353 self .consts = {}
5454 self .lookups = {}
55+ self .types = {}
56+ self .typedefs = {}
5557 self .includes = []
58+
5659 # fmt: off
57- self . typedefs = {
60+ initial_types = {
5861 # Internal types
5962 "int8" : self ._make_packed_type ("int8" , "b" , int ),
6063 "uint8" : self ._make_packed_type ("uint8" , "B" , int ),
@@ -98,6 +101,21 @@ def __init__(self, load: str = "", *, endian: str = "<", pointer: str | None = N
98101 "signed long long" : "int64" ,
99102 "unsigned long long" : "uint64" ,
100103
104+ # Other convenience types
105+ "u1" : "uint8" ,
106+ "u2" : "uint16" ,
107+ "u4" : "uint32" ,
108+ "u8" : "uint64" ,
109+ "u16" : "uint128" ,
110+ "__u8" : "uint8" ,
111+ "__u16" : "uint16" ,
112+ "__u32" : "uint32" ,
113+ "__u64" : "uint64" ,
114+ "uchar" : "uint8" ,
115+ "ushort" : "uint16" ,
116+ "uint" : "uint32" ,
117+ "ulong" : "uint32" ,
118+
101119 # Windows types
102120 "BYTE" : "uint8" ,
103121 "CHAR" : "char" ,
@@ -165,24 +183,12 @@ def __init__(self, load: str = "", *, endian: str = "<", pointer: str | None = N
165183 "_DWORD" : "uint32" ,
166184 "_QWORD" : "uint64" ,
167185 "_OWORD" : "uint128" ,
168-
169- # Other convenience types
170- "u1" : "uint8" ,
171- "u2" : "uint16" ,
172- "u4" : "uint32" ,
173- "u8" : "uint64" ,
174- "u16" : "uint128" ,
175- "__u8" : "uint8" ,
176- "__u16" : "uint16" ,
177- "__u32" : "uint32" ,
178- "__u64" : "uint64" ,
179- "uchar" : "uint8" ,
180- "ushort" : "uint16" ,
181- "uint" : "uint32" ,
182- "ulong" : "uint32" ,
183186 }
184187 # fmt: on
185188
189+ for name , type_ in initial_types .items ():
190+ self .add_type (name , type_ )
191+
186192 pointer = pointer or ("uint64" if sys .maxsize > 2 ** 32 else "uint32" )
187193 self .pointer : type [BaseType ] = self .resolve (pointer )
188194 self ._anonymous_count = 0
@@ -196,37 +202,71 @@ def __getattr__(self, attr: str) -> Any:
196202 except KeyError :
197203 pass
198204
205+ try :
206+ return self .types [attr ]
207+ except KeyError :
208+ pass
209+
199210 try :
200211 return self .resolve (self .typedefs [attr ])
201212 except KeyError :
202213 pass
203214
204- raise AttributeError ( f"Invalid attribute: { attr } " )
215+ return super (). __getattribute__ ( attr )
205216
206217 def _next_anonymous (self ) -> str :
207218 name = f"__anonymous_{ self ._anonymous_count } __"
208219 self ._anonymous_count += 1
209220 return name
210221
222+ def _add_attr (self , name : str , value : Any , replace : bool = False ) -> None :
223+ if not replace and (name in self .__dict__ and self .__dict__ [name ] != value ):
224+ raise ValueError (f"Attribute already exists: { name } " )
225+ setattr (self , name , value )
226+
211227 def add_type (self , name : str , type_ : type [BaseType ] | str , replace : bool = False ) -> None :
212228 """Add a type or type reference.
213229
214230 Only use this method when creating type aliases or adding already bound types.
231+ All types will be resolved to their actual type objects prior to being added.
232+ Use :func:`add_typedef` to add type references.
215233
216234 Args:
217235 name: Name of the type to be added.
218236 type_: The type to be added. Can be a str reference to another type or a compatible type class.
237+ If a str is given, it will be resolved to the actual type object.
219238
220239 Raises:
221240 ValueError: If the type already exists.
222241 """
223- if not replace and (name in self .typedefs and self .resolve (self .typedefs [name ]) != self .resolve (type_ )):
242+ typeobj = self .resolve (type_ )
243+ if not replace and (name in self .types and self .types [name ] != typeobj ):
224244 raise ValueError (f"Duplicate type: { name } " )
225245
226- self .typedefs [name ] = type_
246+ self .types [name ] = typeobj
247+ self ._add_attr (name , typeobj , replace = replace )
227248
228249 addtype = add_type
229250
251+ def add_typedef (self , name : str , type_ : str , replace : bool = False ) -> None :
252+ """Add a type reference.
253+
254+ Use this method to add type references to this cstruct instance. These are type names that can be
255+ dynamically resolved at a later stage. Use :func:`add_type` to add actual type objects.
256+
257+ Args:
258+ name: Name of the type to be added.
259+ type_: The type reference to be added.
260+ replace: Whether to replace the type if it already exists.
261+ """
262+ if not isinstance (type_ , str ):
263+ raise TypeError ("Type reference must be a string" )
264+
265+ if not replace and (name in self .typedefs and self .resolve (self .typedefs [name ]) != self .resolve (type_ )):
266+ raise ValueError (f"Duplicate type: { name } " )
267+
268+ self .typedefs [name ] = type_
269+
230270 def add_custom_type (
231271 self , name : str , type_ : type [BaseType ], size : int | None = None , alignment : int | None = None , ** kwargs
232272 ) -> None :
@@ -244,6 +284,16 @@ def add_custom_type(
244284 """
245285 self .add_type (name , self ._make_type (name , (type_ ,), size , alignment = alignment , attrs = kwargs ))
246286
287+ def add_const (self , name : str , value : Any ) -> None :
288+ """Add a constant value.
289+
290+ Args:
291+ name: Name of the constant to be added.
292+ value: The value of the constant.
293+ """
294+ self .consts [name ] = value
295+ self ._add_attr (name , value , replace = True )
296+
247297 def load (self , definition : str , deftype : int | None = None , ** kwargs ) -> cstruct :
248298 """Parse structures from the given definitions using the given definition type.
249299
@@ -315,14 +365,14 @@ def resolve(self, name: type[BaseType] | str) -> type[BaseType]:
315365 return type_name
316366
317367 for _ in range (10 ):
368+ if type_name in self .types :
369+ return self .types [type_name ]
370+
318371 if type_name not in self .typedefs :
319372 raise ResolveError (f"Unknown type { name } " )
320373
321374 type_name = self .typedefs [type_name ]
322375
323- if not isinstance (type_name , str ):
324- return type_name
325-
326376 raise ResolveError (f"Recursion limit exceeded while resolving type { name } " )
327377
328378 def _make_type (
0 commit comments