from hachoir.field import Field, FieldSet, ParserError, Bytes, MissingField from hachoir.stream import FragmentedStream class Link(Field): def __init__(self, parent, name, *args, **kw): Field.__init__(self, parent, name, 0, *args, **kw) def hasValue(self): return True def createValue(self): return self._parent[self.display] def createDisplay(self): value = self.value if value is None: return "<%s>" % MissingField.__name__ return value.path def _getField(self, name, const): target = self.value assert self != target return target._getField(name, const) class Fragments: def __init__(self, first): self.first = first def __iter__(self): fragment = self.first while fragment is not None: data = fragment.getData() yield data and data.size fragment = fragment.next class Fragment(FieldSet): _first = None def __init__(self, *args, **kw): FieldSet.__init__(self, *args, **kw) self._field_generator = self._createFields(self._field_generator) if self.__class__.createFields == Fragment.createFields: self._getData = lambda: self def getData(self): try: return self._getData() except MissingField as e: self.error(str(e)) return None def setLinks(self, first, next=None): self._first = first or self self._next = next self._feedLinks = lambda: self return self def _feedLinks(self): while self._first is None and self.readMoreFields(1): pass if self._first is None: raise ParserError("first is None") return self first = property(lambda self: self._feedLinks()._first) def _getNext(self): next = self._feedLinks()._next if callable(next): self._next = next = next() return next next = property(_getNext) def _createInputStream(self, **args): first = self.first if first is self and hasattr(first, "_getData"): return FragmentedStream(first, packets=Fragments(first), **args) return FieldSet._createInputStream(self, **args) def _createFields(self, field_generator): if self._first is None: for field in field_generator: if self._first is not None: break yield field else: raise ParserError("Fragment.setLinks not called") else: field = None if self._first is not self: link = Link(self, "first", None) link._getValue = lambda: self._first yield link if self._next: link = Link(self, "next", None) link.createValue = self._getNext yield link if field: yield field for field in field_generator: yield field def createFields(self): if self._size is None: self._size = self._getSize() yield Bytes(self, "data", self._size // 8)