from __future__ import annotations
from copy import deepcopy
from typing import Dict, AnyStr, Union
from pystructs import utils
from pystructs.fields import BytesField, Field
__all__ = [
"Struct",
]
class StructMetaclass(type):
def __new__(mcs, name, bases, attrs: dict):
attrs["fields"] = getattr(bases[0], "fields", {}).copy()
attrs = utils.filter_fields(attrs)
attrs = utils.delete_fields(attrs)
return super().__new__(mcs, name, bases, attrs)
[docs]class Struct(BytesField, metaclass=StructMetaclass):
"""
`struct` implementations that inherit BytesField
link fields and calculate offset and variable values automatically
"""
#: Reflection of fields from struct
fields: "Dict[Union[AnyStr, int], Field]" = {}
def __init__(self, _bytes: bytes = b"", auto_initialization=True):
super().__init__(0)
self.bytes = _bytes
if auto_initialization:
self.initialize()
[docs] def fetch(self) -> "Struct":
return self
def __getattr__(self, item):
try:
if item not in self.fields:
return getattr(super(), item)
return self.fields[item].fetch()
except KeyError:
raise AttributeError(item)
[docs] def initialize(self, root: "Struct" = None):
"""
link fields and set parent, initialize each fields
:param root: Struct object of root
:return:
"""
self.__link_fields()
if root is None:
root = self
self.fields = deepcopy(self.fields)
for field in self.fields.values():
field.parent = self
field.initialize(root)
def __link_fields(self):
fields = list(self.fields.values())
fields_count = len(fields)
for index in range(fields_count - 1):
fields[index + 1].prev = fields[index]
if fields_count > 0:
fields[0].prev = VirtualStruct(self)
@property
def size(self) -> int:
return sum(map(lambda x: x.size, self.fields.values()))
class VirtualStruct:
def __init__(self, struct: Struct):
self.__struct = struct
@property
def offset(self) -> int:
return self.__struct.offset
@property
def size(self) -> int:
return 0