2525#
2626
2727from abc import ABCMeta
28- from typing import cast , Any , BinaryIO , Optional , Type , Union
28+ from collections import OrderedDict
29+ from typing import Any , BinaryIO , List , Dict , Optional , Type , Tuple , Union
2930from .base import STRUCTS
3031import hashlib
3132from .c_parser import parse_struct , parse_def , Tokens
33+ from .field import calculate_padding , FieldType
3234
3335__all__ = ['CStructMeta' , 'AbstractCStruct' ]
3436
3537
3638class CStructMeta (ABCMeta ):
37- def __new__ (cls , name : str , bases , dct ):
38- __struct__ = dct .get ("__struct__" , None )
39- dct ['__cls__' ] = bases [0 ]
39+ __size__ : int = 0
40+
41+ # def __new__(cls: Type[type], name: str, bases: tuple, classdict: dict) -> MetaClass:
42+ def __new__ (metacls : Type [type ], name : str , bases : Tuple [str ], namespace : Dict [str , Any ]) -> Type [Any ]:
43+ __struct__ = namespace .get ('__struct__' , None )
44+ namespace ['__cls__' ] = bases [0 ] if bases else None
4045 # Parse the struct
41- if '__struct__' in dct :
42- if isinstance (dct ['__struct__' ], ("" . __class__ , u"" . __class__ , Tokens )):
43- dct .update (parse_struct (** dct ))
46+ if '__struct__' in namespace :
47+ if isinstance (namespace ['__struct__' ], (str , Tokens )):
48+ namespace .update (parse_struct (** namespace ))
4449 __struct__ = True
45- if '__def__' in dct :
46- dct .update (parse_def (** dct ))
50+ if '__def__' in namespace :
51+ namespace .update (parse_def (** namespace ))
4752 __struct__ = True
4853 # Create the new class
49- new_class = type .__new__ (cls , name , bases , dct )
54+ new_class : Type [ Any ] = super () .__new__ (metacls , name , bases , namespace )
5055 # Register the class
51- if __struct__ is not None and not dct .get ('__anonymous__' ):
56+ if __struct__ is not None and not namespace .get ('__anonymous__' ):
5257 STRUCTS [name ] = new_class
5358 return new_class
5459
@@ -57,29 +62,38 @@ def __len__(cls) -> int:
5762
5863 @property
5964 def size (cls ) -> int :
60- """ Structure size (in bytes)"" "
65+ "Structure size (in bytes)"
6166 return cls .__size__
6267
6368
64- # Workaround for Python 2.x/3.x metaclass, thanks to
65- # http://mikewatkins.ca/2008/11/29/python-2-and-3-metaclasses/#using-the-metaclass-in-python-2-x-and-3-x
66- _CStructParent = CStructMeta ('_CStructParent' , (object ,), {})
67-
68-
69- class AbstractCStruct (_CStructParent ):
70- def __init__ (self , buffer = None , ** kargs ) -> None :
69+ class AbstractCStruct (metaclass = CStructMeta ):
70+ __size__ : int = 0
71+ __fields__ : List [str ] = []
72+ __fields_types__ : Dict [str , FieldType ]
73+ __byte_order__ : Optional [str ] = None
74+ __alignment__ : int = 0
75+ __is_union__ : bool = False
76+
77+ def __init__ (
78+ self , buffer : Optional [Union [bytes , BinaryIO ]] = None , flexible_array_length : Optional [int ] = None , ** kargs : Dict [str , Any ]
79+ ) -> None :
80+ self .set_flexible_array_length (flexible_array_length )
81+ self .__fields__ = [x for x in self .__fields__ ] # Create a copy
82+ self .__fields_types__ = OrderedDict ({k : v .copy () for k , v in self .__fields_types__ .items ()}) # Create a copy
7183 if buffer is not None :
7284 self .unpack (buffer )
7385 else :
7486 try :
7587 self .unpack (buffer )
76- except :
88+ except Exception :
7789 pass
7890 for key , value in kargs .items ():
7991 setattr (self , key , value )
8092
8193 @classmethod
82- def parse (cls , __struct__ , __name__ : Optional [str ] = None , ** kargs ) -> Type [Any ]:
94+ def parse (
95+ cls , __struct__ : Union [str , Tokens , Dict [str , Any ]], __name__ : Optional [str ] = None , ** kargs : Dict [str , Any ]
96+ ) -> Type ["AbstractCStruct" ]:
8397 """
8498 Return a new class mapping a C struct/union definition.
8599
@@ -89,31 +103,51 @@ def parse(cls, __struct__, __name__: Optional[str] = None, **kargs) -> Type[Any]
89103 :param __is_union__: (optional) True for union, False for struct (default)
90104 :returns: cls subclass
91105 """
92- kargs = dict (kargs )
93- kargs ['__struct__' ] = __struct__
94- if isinstance (__struct__ , ("" .__class__ , u"" .__class__ , Tokens )):
95- del kargs ['__struct__' ]
96- kargs .update (parse_def (__struct__ , __cls__ = cls , ** kargs ))
97- kargs ['__struct__' ] = None
106+ cls_kargs : Dict [str , Any ] = dict (kargs )
107+ cls_kargs ['__struct__' ] = __struct__
108+ if isinstance (__struct__ , (str , Tokens )):
109+ del cls_kargs ['__struct__' ]
110+ cls_kargs .update (parse_def (__struct__ , __cls__ = cls , ** cls_kargs ))
111+ cls_kargs ['__struct__' ] = None
112+ elif isinstance (__struct__ , dict ):
113+ del cls_kargs ['__struct__' ]
114+ cls_kargs .update (__struct__ )
115+ cls_kargs ['__struct__' ] = None
98116 if __name__ is None : # Anonymous struct
99117 __name__ = cls .__name__ + '_' + hashlib .sha1 (str (__struct__ ).encode ('utf-8' )).hexdigest ()
100- kargs ['__anonymous__' ] = True
101- kargs ['__name__' ] = __name__
102- return type (__name__ , (cls ,), kargs )
118+ cls_kargs ['__anonymous__' ] = True
119+ cls_kargs ['__name__' ] = __name__
120+ return type (__name__ , (cls ,), cls_kargs )
103121
104- def unpack (self , buffer : Optional [Union [bytes , BinaryIO ]]) -> bool :
122+ def set_flexible_array_length (self , flexible_array_length : Optional [int ]) -> None :
123+ """
124+ Set flexible array length (i.e. number of elements)
125+
126+ :flexible_array_length: flexible array length
127+ """
128+ if flexible_array_length is not None :
129+ # Search for the flexible array
130+ flexible_array : Optional [FieldType ] = [x for x in self .__fields_types__ .values () if x .flexible_array ][0 ]
131+ if flexible_array is None :
132+ raise ValueError ("Flexible array not found in struct" )
133+ flexible_array .vlen = flexible_array_length
134+
135+ def unpack (self , buffer : Optional [Union [bytes , BinaryIO ]], flexible_array_length : Optional [int ] = None ) -> bool :
105136 """
106137 Unpack bytes containing packed C structure data
107138
108139 :param buffer: bytes or binary stream to be unpacked
109140 """
141+ self .set_flexible_array_length (flexible_array_length )
110142 if hasattr (buffer , 'read' ):
111- buffer = buffer .read (self .__size__ )
143+ buffer = buffer .read (self .size ) # type: ignore
112144 if not buffer :
113145 return False
114146 return self .unpack_from (buffer )
115147
116- def unpack_from (self , buffer : Optional [bytes ], offset : int = 0 ) -> bool : # pragma: no cover
148+ def unpack_from (
149+ self , buffer : Optional [bytes ], offset : int = 0 , flexible_array_length : Optional [int ] = None
150+ ) -> bool : # pragma: no cover
117151 """
118152 Unpack bytes containing packed C structure data
119153
@@ -123,22 +157,35 @@ def unpack_from(self, buffer: Optional[bytes], offset: int = 0) -> bool: # prag
123157 raise NotImplementedError
124158
125159 def pack (self ) -> bytes : # pragma: no cover
126- """
127- Pack the structure data into bytes
128- """
160+ "Pack the structure data into bytes"
129161 raise NotImplementedError
130162
131163 def clear (self ) -> None :
132164 self .unpack (None )
133165
134166 def __len__ (self ) -> int :
135- """Structure size (in bytes)"" "
136- return cast ( int , self .__size__ )
167+ "Actual structure size (in bytes)"
168+ return self .size
137169
138170 @property
139171 def size (self ) -> int :
140- """Structure size (in bytes)"""
141- return self .__size__
172+ "Actual structure size (in bytes)"
173+ if not self .__fields_types__ : # no fields
174+ return 0
175+ elif self .__is_union__ : # C union
176+ # Calculate the sizeof union as size of its largest element
177+ return max (x .vsize for x in self .__fields_types__ .values ())
178+ else : # C struct
179+ # Calculate the sizeof struct as last item's offset + size + padding
180+ last_field_type = list (self .__fields_types__ .values ())[- 1 ]
181+ size = last_field_type .offset + last_field_type .vsize
182+ padding = calculate_padding (self .__byte_order__ , self .__alignment__ , size )
183+ return size + padding
184+
185+ @classmethod
186+ def sizeof (cls ) -> int :
187+ "Structure size in bytes (flexible array member size is omitted)"
188+ return cls .__size__
142189
143190 def __eq__ (self , other : Any ) -> bool :
144191 return other is not None and isinstance (other , self .__class__ ) and self .__dict__ == other .__dict__
0 commit comments