@@ -48,8 +48,11 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
4848
4949 self .consts = {}
5050 self .lookups = {}
51+ self .types = {}
52+ self .typedefs = {}
5153 # fmt: off
52- self .typedefs = {
54+
55+ initial_types = {
5356 # Internal types
5457 "int8" : self ._make_packed_type ("int8" , "b" , int ),
5558 "uint8" : self ._make_packed_type ("uint8" , "B" , int ),
@@ -93,6 +96,21 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
9396 "signed long long" : "long long" ,
9497 "unsigned long long" : "uint64" ,
9598
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+
96114 # Windows types
97115 "BYTE" : "uint8" ,
98116 "CHAR" : "char" ,
@@ -160,24 +178,12 @@ def __init__(self, endian: str = "<", pointer: str | None = None):
160178 "_DWORD" : "uint32" ,
161179 "_QWORD" : "uint64" ,
162180 "_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" ,
178181 }
179182 # fmt: on
180183
184+ for name , type_ in initial_types .items ():
185+ self .add_type (name , type_ )
186+
181187 pointer = pointer or ("uint64" if sys .maxsize > 2 ** 32 else "uint32" )
182188 self .pointer = self .resolve (pointer )
183189 self ._anonymous_count = 0
@@ -188,37 +194,71 @@ def __getattr__(self, attr: str) -> Any:
188194 except KeyError :
189195 pass
190196
197+ try :
198+ return self .types [attr ]
199+ except KeyError :
200+ pass
201+
191202 try :
192203 return self .resolve (self .typedefs [attr ])
193204 except KeyError :
194205 pass
195206
196- raise AttributeError ( f"Invalid attribute: { attr } " )
207+ return super (). __getattribute__ ( attr )
197208
198209 def _next_anonymous (self ) -> str :
199210 name = f"__anonymous_{ self ._anonymous_count } __"
200211 self ._anonymous_count += 1
201212 return name
202213
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+
203219 def add_type (self , name : str , type_ : MetaType | str , replace : bool = False ) -> None :
204220 """Add a type or type reference.
205221
206222 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.
207225
208226 Args:
209227 name: Name of the type to be added.
210228 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.
211230
212231 Raises:
213232 ValueError: If the type already exists.
214233 """
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 ):
216236 raise ValueError (f"Duplicate type: { name } " )
217237
218- self .typedefs [name ] = type_
238+ self .types [name ] = typeobj
239+ self ._add_attr (name , typeobj , replace = replace )
219240
220241 addtype = add_type
221242
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+
222262 def add_custom_type (
223263 self , name : str , type_ : MetaType , size : int | None = None , alignment : int | None = None , ** kwargs
224264 ) -> None :
@@ -236,6 +276,16 @@ def add_custom_type(
236276 """
237277 self .add_type (name , self ._make_type (name , (type_ ,), size , alignment = alignment , attrs = kwargs ))
238278
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+
239289 def load (self , definition : str , deftype : int | None = None , ** kwargs ) -> cstruct :
240290 """Parse structures from the given definitions using the given definition type.
241291
@@ -307,14 +357,14 @@ def resolve(self, name: str) -> MetaType:
307357 return type_name
308358
309359 for _ in range (10 ):
360+ if type_name in self .types :
361+ return self .types [type_name ]
362+
310363 if type_name not in self .typedefs :
311364 raise ResolveError (f"Unknown type { name } " )
312365
313366 type_name = self .typedefs [type_name ]
314367
315- if not isinstance (type_name , str ):
316- return type_name
317-
318368 raise ResolveError (f"Recursion limit exceeded while resolving type { name } " )
319369
320370 def _make_type (
0 commit comments