# -*- coding: utf-8 -*-
import struct as packer
from struct import Struct as Packer
from struct import error as PackerError
from io import BytesIO, StringIO
from binascii import hexlify
import sys
import collections
import codecs
from construct.lib.container import Container, FlagsContainer, ListContainer, LazyContainer, LazyRangeContainer, LazySequenceContainer
from construct.lib.binary import integer2bits, integer2bytes, onebit2integer, bits2integer, bytes2integer, bytes2bits, bits2bytes, swapbytes
from construct.lib.bitstream import RestreamedBytesIO, RebufferedBytesIO
from construct.lib.hex import HexString, hexdump, hexundump
from construct.lib.py3compat import PY2, PY3, PY26, PY32, PY33, PYPY, stringtypes, int2byte, byte2int, str2bytes, bytes2str, str2unicode, unicode2str, iteratebytes, iterateints
#===============================================================================
# exceptions
#===============================================================================
class ConstructError(Exception):
pass
class FieldError(ConstructError):
pass
class SizeofError(ConstructError):
pass
class AdaptationError(ConstructError):
pass
class ArrayError(ConstructError):
pass
class RangeError(ConstructError):
pass
class SwitchError(ConstructError):
pass
class SelectError(ConstructError):
pass
class UnionError(ConstructError):
pass
class TerminatorError(ConstructError):
pass
class OverwriteError(ConstructError):
pass
class PaddingError(ConstructError):
pass
class ConstError(ConstructError):
pass
class StringError(ConstructError):
pass
class ChecksumError(ConstructError):
pass
class ValidationError(ConstructError):
pass
class BitIntegerError(ConstructError):
pass
class MappingError(AdaptationError):
pass
#===============================================================================
# internal code
#===============================================================================
def singleton(cls):
return cls()
def singletonfunction(func):
return func()
def _read_stream(stream, length):
# if not isinstance(length, int):
# raise TypeError("expected length to be int")
if length < 0:
raise ValueError("length must be >= 0", length)
data = stream.read(length)
if len(data) != length:
raise FieldError("could not read enough bytes, expected %d, found %d" % (length, len(data)))
return data
def _write_stream(stream, length, data):
# if not isinstance(data, bytes):
# raise TypeError("expected data to be a bytes")
if length < 0:
raise ValueError("length must be >= 0", length)
if len(data) != length:
raise FieldError("could not write bytes, expected %d, found %d" % (length, len(data)))
written = stream.write(data)
if written != length:
raise FieldError("could not write bytes, written %d, should %d" % (written, length))
def _subobj(sc, obj):
if sc.flagembedded:
return obj
else:
return obj[sc.name]
def _updcon(con, sc, obj):
if sc.flagembedded:
con.update(obj)
else:
con[sc.name] = obj
#===============================================================================
# abstract constructs
#===============================================================================
class Construct(object):
r"""
The mother of all constructs.
This object is generally not directly instantiated, and it does not directly implement parsing and building, so it is largely only of interest to subclass implementors. There are also other abstract classes.
The external user API:
* ``parse()``
* ``parse_stream()``
* ``build()``
* ``build_stream()``
* ``sizeof()``
Subclass authors should not override the external methods. Instead, another API is available:
* ``_parse()``
* ``_build()``
* ``_sizeof()``
There is also a flag API:
* ``_inherit_flags()``
And stateful copying:
* ``__getstate__()``
* ``__setstate__()``
Attributes and Inheritance
==========================
All constructs have a name and flags. The name is used for naming struct members and context dictionaries. Note that the name can either be a string, or None if the name is not needed. A single underscore ("_") is a reserved name, and so are names starting with a less-than character ("<"). The name should be descriptive, short, and valid as a Python identifier, although these rules are not enforced.
The flags specify additional behavioral information about this construct. Flags are used by enclosing constructs to determine a proper course of action. Flags are inherited by default, from inner subconstructs to outer constructs. The enclosing construct may set new flags or clear existing ones, as necessary.
"""
__slots__ = ["name", "flagbuildnone", "flagembedded"]
def __init__(self):
self.name = None
self.flagbuildnone = False
self.flagembedded = False
def __repr__(self):
return "<%s: %s%s%s>" % (self.__class__.__name__, self.name, " +nonbuild" if self.flagbuildnone else "", " +embedded" if self.flagembedded else "")
def _inherit_flags(self, *subcons):
for sc in subcons:
self.flagbuildnone |= sc.flagbuildnone
self.flagembedded |= sc.flagembedded
def __getstate__(self):
# Obtain a dictionary representing this construct's state.
attrs = {}
if hasattr(self, "__dict__"):
attrs.update(self.__dict__)
slots = []
c = self.__class__
while c is not None:
if hasattr(c, "__slots__"):
slots.extend(c.__slots__)
c = c.__base__
for name in slots:
if hasattr(self, name):
attrs[name] = getattr(self, name)
return attrs
def __setstate__(self, attrs):
# Set this construct's state to a given state.
for name, value in attrs.items():
setattr(self, name, value)
def __copy__(self):
# Returns a copy of this construct.
self2 = object.__new__(self.__class__)
self2.__setstate__(self, self.__getstate__())
return self2
def parse(self, data, context=None, **kw):
"""
Parse an in-memory buffer.
Strings, buffers, memoryviews, and other complete buffers can be parsed with this method.
"""
return self.parse_stream(BytesIO(data), context, **kw)
def parse_stream(self, stream, context=None, **kw):
"""
Parse a stream.
Files, pipes, sockets, and other streaming sources of data are handled by this method.
"""
if context is None:
context = Container()
context.update(kw)
return self._parse(stream, context)
def _parse(self, stream, context):
"""
Override in your subclass.
:returns: some value, usually based on bytes read from the stream but sometimes it is computed from nothing or context
"""
raise NotImplementedError()
def build(self, obj, context=None, **kw):
"""
Build an object in memory.
:returns: bytes
"""
stream = BytesIO()
self.build_stream(obj, stream, context, **kw)
return stream.getvalue()
def build_stream(self, obj, stream, context=None, **kw):
"""
Build an object directly into a stream.
:returns: None
"""
if context is None:
context = Container()
context.update(kw)
self._build(obj, stream, context)
def _build(self, obj, stream, context):
"""
Override in your subclass.
:returns: None or a new value to put into context, few fields use this
"""
raise NotImplementedError()
def sizeof(self, context=None, **kw):
"""
Calculate the size of this object, optionally using a context.