379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478 | def parse_struct(
__struct__: Union[str, Tokens],
__cls__: Type["AbstractCStruct"],
__is_union__: bool = False,
__byte_order__: Optional[str] = None,
__name__: Optional[str] = None,
**kargs: Any,
) -> Dict[str, Any]:
"""
Parser for C-like struct syntax.
Args:
__struct__: definition of the struct/union in C syntax
__cls__: base class (MemCStruct or CStruct)
__is_union__: True for union, False for struct
__byte_order__: byte order, valid values are LITTLE_ENDIAN, BIG_ENDIAN, NATIVE_ORDER
__name__: struct/union name
Returns:
dict: the parsed definition
"""
# naive C struct parsing
from .abstract import AbstractCStruct
from .mem_cstruct import MemCStruct
if __cls__ is None or __cls__ == AbstractCStruct:
__cls__ = MemCStruct
__is_union__ = bool(__is_union__)
fields_types: Dict[str, FieldType] = OrderedDict()
flexible_array: bool = False
offset: int = 0
max_alignment: int = 0
anonymous: int = 0
if isinstance(__struct__, Tokens):
tokens = __struct__
else:
tokens = Tokens(__struct__)
while len(tokens):
if tokens.get() == "}":
tokens.pop()
break
# flexible array member must be the last member of such a struct
if flexible_array:
raise CStructException("Flexible array member must be the last member of such a struct")
field_type = parse_type(tokens, __cls__, __byte_order__, offset)
vname = tokens.pop()
if vname in fields_types:
raise ParserError(f"Duplicate member `{vname}`")
if vname in dir(__cls__):
raise ParserError(f"Invalid reserved member name `{vname}`")
# parse length
if "[" in vname:
vname, field_type.vlen, field_type.flexible_array = parse_length(tokens, vname, 1, flexible_array)
flexible_array = flexible_array or field_type.flexible_array
# anonymous nested union
if vname == ";" and field_type.ref is not None and (__is_union__ or field_type.ref.__is_union__):
# add the anonymous struct fields to the parent
for nested_field_name, nested_field_type in field_type.ref.__fields_types__.items():
if nested_field_name in fields_types:
raise ParserError(f"Duplicate member `{nested_field_name}`")
# set the corret offset
nested_field_type = nested_field_type.copy()
nested_field_type.base_offset = offset + nested_field_type.base_offset
nested_field_type.offset = offset + nested_field_type.offset
fields_types[nested_field_name] = nested_field_type
vname = f"__anonymous{anonymous}"
anonymous += 1
tokens.push(";")
fields_types[vname] = field_type
# calculate the max field size (for the alignment)
max_alignment = max(max_alignment, field_type.alignment)
# align struct if byte order is native
if not __is_union__: # C struct
field_type.align_filed_offset()
offset = field_type.offset + field_type.vsize
t = tokens.pop()
if t != ";":
raise ParserError(f"`;` expected but `{t}` found")
if __is_union__: # C union
# Calculate the sizeof union as size of its largest element
size = max([x.vsize for x in fields_types.values()])
else: # C struct
# add padding to struct if byte order is native
size = offset + calculate_padding(__byte_order__, max_alignment, offset)
# Prepare the result
result = {
"__fields__": list(fields_types.keys()),
"__fields_types__": fields_types,
"__size__": size,
"__is_struct__": not __is_union__,
"__is_union__": __is_union__,
"__is_enum__": False,
"__byte_order__": __byte_order__,
"__alignment__": max_alignment,
"__name__": __name__,
"__cls__": __cls__,
}
return result
|