xref: /freebsd/contrib/lib9p/pytest/sequencer.py (revision 134e17798c9af53632b372348ab828e75e65bf46)
1*134e1779SJakub Wojciech Klama#! /usr/bin/env python
2*134e1779SJakub Wojciech Klama
3*134e1779SJakub Wojciech Klamafrom __future__ import print_function
4*134e1779SJakub Wojciech Klama
5*134e1779SJakub Wojciech Klama#__all__ = ['EncDec', 'EncDecSimple', 'EncDecTyped', 'EncDecA',
6*134e1779SJakub Wojciech Klama#    'SequenceError', 'Sequencer']
7*134e1779SJakub Wojciech Klama
8*134e1779SJakub Wojciech Klamaimport abc
9*134e1779SJakub Wojciech Klamaimport struct
10*134e1779SJakub Wojciech Klamaimport sys
11*134e1779SJakub Wojciech Klama
12*134e1779SJakub Wojciech Klama_ProtoStruct = {
13*134e1779SJakub Wojciech Klama    '1': struct.Struct('<B'),
14*134e1779SJakub Wojciech Klama    '2': struct.Struct('<H'),
15*134e1779SJakub Wojciech Klama    '4': struct.Struct('<I'),
16*134e1779SJakub Wojciech Klama    '8': struct.Struct('<Q'),
17*134e1779SJakub Wojciech Klama    '_string_': None,   # handled specially
18*134e1779SJakub Wojciech Klama}
19*134e1779SJakub Wojciech Klamafor _i in (1, 2, 4, 8):
20*134e1779SJakub Wojciech Klama    _ProtoStruct[_i] = _ProtoStruct[str(_i)]
21*134e1779SJakub Wojciech Klamadel _i
22*134e1779SJakub Wojciech Klama
23*134e1779SJakub Wojciech Klamaclass EncDec(object):
24*134e1779SJakub Wojciech Klama    __metaclass__ = abc.ABCMeta
25*134e1779SJakub Wojciech Klama    """
26*134e1779SJakub Wojciech Klama    Base class for en/de-coders, which are put into sequencers.
27*134e1779SJakub Wojciech Klama
28*134e1779SJakub Wojciech Klama    All have a name and arbitrary user-supplied auxiliary data
29*134e1779SJakub Wojciech Klama    (default=None).
30*134e1779SJakub Wojciech Klama
31*134e1779SJakub Wojciech Klama    All provide a pack() and unpack().  The pack() function
32*134e1779SJakub Wojciech Klama    returns a "bytes" value.  This is internally implemented as a
33*134e1779SJakub Wojciech Klama    function apack() that returns a list of struct.pack() bytes,
34*134e1779SJakub Wojciech Klama    and pack() just joins them up as needed.
35*134e1779SJakub Wojciech Klama
36*134e1779SJakub Wojciech Klama    The pack/unpack functions take a dictionary of variable names
37*134e1779SJakub Wojciech Klama    and values, and a second dictionary for conditionals, but at
38*134e1779SJakub Wojciech Klama    this level conditionals don't apply: they are just being
39*134e1779SJakub Wojciech Klama    passed through.  Variable names do apply to array encoders
40*134e1779SJakub Wojciech Klama
41*134e1779SJakub Wojciech Klama    EncDec also provide b2s() and s2b() static methods, which
42*134e1779SJakub Wojciech Klama    convert strings to bytes and vice versa, as reversibly as
43*134e1779SJakub Wojciech Klama    possible (using surrogateescape encoding). In Python2 this is
44*134e1779SJakub Wojciech Klama    a no-op since the string type *is* the bytes type (<type
45*134e1779SJakub Wojciech Klama    'unicode'>) is the unicode-ized string type).
46*134e1779SJakub Wojciech Klama
47*134e1779SJakub Wojciech Klama    EncDec also provides b2u() and u2b() to do conversion to/from
48*134e1779SJakub Wojciech Klama    Unicode.
49*134e1779SJakub Wojciech Klama
50*134e1779SJakub Wojciech Klama    These are partly for internal use (all strings get converted
51*134e1779SJakub Wojciech Klama    to UTF-8 byte sequences when coding a _string_ type) and partly
52*134e1779SJakub Wojciech Klama    for doctests, where we just want some py2k/py3k compat hacks.
53*134e1779SJakub Wojciech Klama    """
54*134e1779SJakub Wojciech Klama    def __init__(self, name, aux):
55*134e1779SJakub Wojciech Klama        self.name = name
56*134e1779SJakub Wojciech Klama        self.aux = aux
57*134e1779SJakub Wojciech Klama
58*134e1779SJakub Wojciech Klama    @staticmethod
59*134e1779SJakub Wojciech Klama    def b2u(byte_sequence):
60*134e1779SJakub Wojciech Klama        "transform bytes to unicode"
61*134e1779SJakub Wojciech Klama        return byte_sequence.decode('utf-8', 'surrogateescape')
62*134e1779SJakub Wojciech Klama
63*134e1779SJakub Wojciech Klama    @staticmethod
64*134e1779SJakub Wojciech Klama    def u2b(unicode_sequence):
65*134e1779SJakub Wojciech Klama        "transform unicode to bytes"
66*134e1779SJakub Wojciech Klama        return unicode_sequence.encode('utf-8', 'surrogateescape')
67*134e1779SJakub Wojciech Klama
68*134e1779SJakub Wojciech Klama    if sys.version_info[0] >= 3:
69*134e1779SJakub Wojciech Klama        b2s = b2u
70*134e1779SJakub Wojciech Klama        @staticmethod
71*134e1779SJakub Wojciech Klama        def s2b(string):
72*134e1779SJakub Wojciech Klama            "transform string to bytes (leaves raw byte sequence unchanged)"
73*134e1779SJakub Wojciech Klama            if isinstance(string, bytes):
74*134e1779SJakub Wojciech Klama                return string
75*134e1779SJakub Wojciech Klama            return string.encode('utf-8', 'surrogateescape')
76*134e1779SJakub Wojciech Klama    else:
77*134e1779SJakub Wojciech Klama        @staticmethod
78*134e1779SJakub Wojciech Klama        def b2s(byte_sequence):
79*134e1779SJakub Wojciech Klama            "transform bytes to string - no-op in python2.7"
80*134e1779SJakub Wojciech Klama            return byte_sequence
81*134e1779SJakub Wojciech Klama        @staticmethod
82*134e1779SJakub Wojciech Klama        def s2b(string):
83*134e1779SJakub Wojciech Klama            "transform string or unicode to bytes"
84*134e1779SJakub Wojciech Klama            if isinstance(string, unicode):
85*134e1779SJakub Wojciech Klama                return string.encode('utf-8', 'surrogateescape')
86*134e1779SJakub Wojciech Klama            return string
87*134e1779SJakub Wojciech Klama
88*134e1779SJakub Wojciech Klama    def pack(self, vdict, cdict, val):
89*134e1779SJakub Wojciech Klama        "encode value <val> into a byte-string"
90*134e1779SJakub Wojciech Klama        return b''.join(self.apack(vdict, cdict, val))
91*134e1779SJakub Wojciech Klama
92*134e1779SJakub Wojciech Klama    @abc.abstractmethod
93*134e1779SJakub Wojciech Klama    def apack(self, vdict, cdict, val):
94*134e1779SJakub Wojciech Klama        "encode value <val> into [bytes1, b2, ..., bN]"
95*134e1779SJakub Wojciech Klama
96*134e1779SJakub Wojciech Klama    @abc.abstractmethod
97*134e1779SJakub Wojciech Klama    def unpack(self, vdict, cdict, bstring, offset, noerror=False):
98*134e1779SJakub Wojciech Klama        "unpack bytes from <bstring> at <offset>"
99*134e1779SJakub Wojciech Klama
100*134e1779SJakub Wojciech Klama
101*134e1779SJakub Wojciech Klamaclass EncDecSimple(EncDec):
102*134e1779SJakub Wojciech Klama    r"""
103*134e1779SJakub Wojciech Klama    Encode/decode a simple (but named) field.  The field is not an
104*134e1779SJakub Wojciech Klama    array, which requires using EncDecA, nor a typed object
105*134e1779SJakub Wojciech Klama    like a qid or stat instance -- those require a Sequence and
106*134e1779SJakub Wojciech Klama    EncDecTyped.
107*134e1779SJakub Wojciech Klama
108*134e1779SJakub Wojciech Klama    The format is one of '1'/1, '2'/2, '4'/4, '8'/8, or '_string_'.
109*134e1779SJakub Wojciech Klama
110*134e1779SJakub Wojciech Klama    Note: using b2s here is purely a doctest/tetsmod python2/python3
111*134e1779SJakub Wojciech Klama    compat hack.  The output of e.pack is <type 'bytes'>; b2s
112*134e1779SJakub Wojciech Klama    converts it to a string, purely for display purposes.  (It might
113*134e1779SJakub Wojciech Klama    be better to map py2 output to bytes but they just print as a
114*134e1779SJakub Wojciech Klama    string anyway.)  In normal use, you should not call b2s here.
115*134e1779SJakub Wojciech Klama
116*134e1779SJakub Wojciech Klama    >>> e = EncDecSimple('eggs', 2)
117*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({}, {}, 0))
118*134e1779SJakub Wojciech Klama    '\x00\x00'
119*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({}, {}, 256))
120*134e1779SJakub Wojciech Klama    '\x00\x01'
121*134e1779SJakub Wojciech Klama
122*134e1779SJakub Wojciech Klama    Values that cannot be packed produce a SequenceError:
123*134e1779SJakub Wojciech Klama
124*134e1779SJakub Wojciech Klama    >>> e.pack({}, {}, None)
125*134e1779SJakub Wojciech Klama    Traceback (most recent call last):
126*134e1779SJakub Wojciech Klama        ...
127*134e1779SJakub Wojciech Klama    SequenceError: failed while packing 'eggs'=None
128*134e1779SJakub Wojciech Klama    >>> e.pack({}, {}, -1)
129*134e1779SJakub Wojciech Klama    Traceback (most recent call last):
130*134e1779SJakub Wojciech Klama        ...
131*134e1779SJakub Wojciech Klama    SequenceError: failed while packing 'eggs'=-1
132*134e1779SJakub Wojciech Klama
133*134e1779SJakub Wojciech Klama    Unpacking both returns a value, and tells how many bytes it
134*134e1779SJakub Wojciech Klama    used out of the bytestring or byte-array argument.  If there
135*134e1779SJakub Wojciech Klama    are not enough bytes remaining at the starting offset, it
136*134e1779SJakub Wojciech Klama    raises a SequenceError, unless noerror=True (then unset
137*134e1779SJakub Wojciech Klama    values are None)
138*134e1779SJakub Wojciech Klama
139*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {}, b'\x00\x01', 0)
140*134e1779SJakub Wojciech Klama    (256, 2)
141*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {}, b'', 0)
142*134e1779SJakub Wojciech Klama    Traceback (most recent call last):
143*134e1779SJakub Wojciech Klama        ...
144*134e1779SJakub Wojciech Klama    SequenceError: out of data while unpacking 'eggs'
145*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {}, b'', 0, noerror=True)
146*134e1779SJakub Wojciech Klama    (None, 2)
147*134e1779SJakub Wojciech Klama
148*134e1779SJakub Wojciech Klama    Note that strings can be provided as regular strings, byte
149*134e1779SJakub Wojciech Klama    strings (same as regular strings in py2k), or Unicode strings
150*134e1779SJakub Wojciech Klama    (same as regular strings in py3k).  Unicode strings will be
151*134e1779SJakub Wojciech Klama    converted to UTF-8 before being packed.  Since this leaves
152*134e1779SJakub Wojciech Klama    7-bit characters alone, these examples work in both py2k and
153*134e1779SJakub Wojciech Klama    py3k.  (Note: the UTF-8 encoding of u'\u1234' is
154*134e1779SJakub Wojciech Klama    '\0xe1\0x88\0xb4' or 225, 136, 180. The b2i trick below is
155*134e1779SJakub Wojciech Klama    another py2k vs py3k special case just for doctests: py2k
156*134e1779SJakub Wojciech Klama    tries to display the utf-8 encoded data as a string.)
157*134e1779SJakub Wojciech Klama
158*134e1779SJakub Wojciech Klama    >>> e = EncDecSimple('spam', '_string_')
159*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({}, {}, 'p3=unicode,p2=bytes'))
160*134e1779SJakub Wojciech Klama    '\x13\x00p3=unicode,p2=bytes'
161*134e1779SJakub Wojciech Klama
162*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({}, {}, b'bytes'))
163*134e1779SJakub Wojciech Klama    '\x05\x00bytes'
164*134e1779SJakub Wojciech Klama
165*134e1779SJakub Wojciech Klama    >>> import sys
166*134e1779SJakub Wojciech Klama    >>> ispy3k = sys.version_info[0] >= 3
167*134e1779SJakub Wojciech Klama
168*134e1779SJakub Wojciech Klama    >>> b2i = lambda x: x if ispy3k else ord(x)
169*134e1779SJakub Wojciech Klama    >>> [b2i(x) for x in e.pack({}, {}, u'\u1234')]
170*134e1779SJakub Wojciech Klama    [3, 0, 225, 136, 180]
171*134e1779SJakub Wojciech Klama
172*134e1779SJakub Wojciech Klama    The byte length of the utf-8 data cannot exceed 65535 since
173*134e1779SJakub Wojciech Klama    the encoding has the length as a 2-byte field (a la the
174*134e1779SJakub Wojciech Klama    encoding for 'eggs' here).  A too-long string produces
175*134e1779SJakub Wojciech Klama    a SequenceError as well.
176*134e1779SJakub Wojciech Klama
177*134e1779SJakub Wojciech Klama    >>> e.pack({}, {}, 16384 * 'spam')
178*134e1779SJakub Wojciech Klama    Traceback (most recent call last):
179*134e1779SJakub Wojciech Klama        ...
180*134e1779SJakub Wojciech Klama    SequenceError: string too long (len=65536) while packing 'spam'
181*134e1779SJakub Wojciech Klama
182*134e1779SJakub Wojciech Klama    Unpacking strings produces byte arrays.  (Of course,
183*134e1779SJakub Wojciech Klama    in py2k these are also known as <type 'str'>.)
184*134e1779SJakub Wojciech Klama
185*134e1779SJakub Wojciech Klama    >>> unpacked = e.unpack({}, {}, b'\x04\x00data', 0)
186*134e1779SJakub Wojciech Klama    >>> etype = bytes if ispy3k else str
187*134e1779SJakub Wojciech Klama    >>> print(isinstance(unpacked[0], etype))
188*134e1779SJakub Wojciech Klama    True
189*134e1779SJakub Wojciech Klama    >>> e.b2s(unpacked[0])
190*134e1779SJakub Wojciech Klama    'data'
191*134e1779SJakub Wojciech Klama    >>> unpacked[1]
192*134e1779SJakub Wojciech Klama    6
193*134e1779SJakub Wojciech Klama
194*134e1779SJakub Wojciech Klama    You may use e.b2s() to conver them to unicode strings in py3k,
195*134e1779SJakub Wojciech Klama    or you may set e.autob2s.  This still only really does
196*134e1779SJakub Wojciech Klama    anything in py3k, since py2k strings *are* bytes, so it's
197*134e1779SJakub Wojciech Klama    really just intended for doctest purposes (see EncDecA):
198*134e1779SJakub Wojciech Klama
199*134e1779SJakub Wojciech Klama    >>> e.autob2s = True
200*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {}, b'\x07\x00stringy', 0)
201*134e1779SJakub Wojciech Klama    ('stringy', 9)
202*134e1779SJakub Wojciech Klama    """
203*134e1779SJakub Wojciech Klama    def __init__(self, name, fmt, aux=None):
204*134e1779SJakub Wojciech Klama        super(EncDecSimple, self).__init__(name, aux)
205*134e1779SJakub Wojciech Klama        self.fmt = fmt
206*134e1779SJakub Wojciech Klama        self.struct = _ProtoStruct[fmt]
207*134e1779SJakub Wojciech Klama        self.autob2s = False
208*134e1779SJakub Wojciech Klama
209*134e1779SJakub Wojciech Klama    def __repr__(self):
210*134e1779SJakub Wojciech Klama        if self.aux is None:
211*134e1779SJakub Wojciech Klama            return '{0}({1!r}, {2!r})'.format(self.__class__.__name__,
212*134e1779SJakub Wojciech Klama                self.name, self.fmt)
213*134e1779SJakub Wojciech Klama        return '{0}({1!r}, {2!r}, {3!r})'.format(self.__class__.__name__,
214*134e1779SJakub Wojciech Klama            self.name, self.fmt, self.aux)
215*134e1779SJakub Wojciech Klama
216*134e1779SJakub Wojciech Klama    __str__ = __repr__
217*134e1779SJakub Wojciech Klama
218*134e1779SJakub Wojciech Klama    def apack(self, vdict, cdict, val):
219*134e1779SJakub Wojciech Klama        "encode a value"
220*134e1779SJakub Wojciech Klama        try:
221*134e1779SJakub Wojciech Klama            if self.struct:
222*134e1779SJakub Wojciech Klama                return [self.struct.pack(val)]
223*134e1779SJakub Wojciech Klama            sval = self.s2b(val)
224*134e1779SJakub Wojciech Klama            if len(sval) > 65535:
225*134e1779SJakub Wojciech Klama                raise SequenceError('string too long (len={0:d}) '
226*134e1779SJakub Wojciech Klama                    'while packing {1!r}'.format(len(sval), self.name))
227*134e1779SJakub Wojciech Klama            return [EncDecSimple.string_len.pack(len(sval)), sval]
228*134e1779SJakub Wojciech Klama        # Include AttributeError in case someone tries to, e.g.,
229*134e1779SJakub Wojciech Klama        # pack name=None and self.s2b() tries to use .encode on it.
230*134e1779SJakub Wojciech Klama        except (struct.error, AttributeError):
231*134e1779SJakub Wojciech Klama            raise SequenceError('failed '
232*134e1779SJakub Wojciech Klama                'while packing {0!r}={1!r}'.format(self.name, val))
233*134e1779SJakub Wojciech Klama
234*134e1779SJakub Wojciech Klama    def _unpack1(self, via, bstring, offset, noerror):
235*134e1779SJakub Wojciech Klama        "internal function to unpack single item"
236*134e1779SJakub Wojciech Klama        try:
237*134e1779SJakub Wojciech Klama            tup = via.unpack_from(bstring, offset)
238*134e1779SJakub Wojciech Klama        except struct.error as err:
239*134e1779SJakub Wojciech Klama            if 'unpack_from requires a buffer of at least' in str(err):
240*134e1779SJakub Wojciech Klama                if noerror:
241*134e1779SJakub Wojciech Klama                    return None, offset + via.size
242*134e1779SJakub Wojciech Klama                raise SequenceError('out of data '
243*134e1779SJakub Wojciech Klama                    'while unpacking {0!r}'.format(self.name))
244*134e1779SJakub Wojciech Klama            # not clear what to do here if noerror
245*134e1779SJakub Wojciech Klama            raise SequenceError('failed '
246*134e1779SJakub Wojciech Klama                'while unpacking {0!r}'.format(self.name))
247*134e1779SJakub Wojciech Klama        assert len(tup) == 1
248*134e1779SJakub Wojciech Klama        return tup[0], offset + via.size
249*134e1779SJakub Wojciech Klama
250*134e1779SJakub Wojciech Klama    def unpack(self, vdict, cdict, bstring, offset, noerror=False):
251*134e1779SJakub Wojciech Klama        "decode a value; return the value and the new offset"
252*134e1779SJakub Wojciech Klama        if self.struct:
253*134e1779SJakub Wojciech Klama            return self._unpack1(self.struct, bstring, offset, noerror)
254*134e1779SJakub Wojciech Klama        slen, offset = self._unpack1(EncDecSimple.string_len, bstring, offset,
255*134e1779SJakub Wojciech Klama            noerror)
256*134e1779SJakub Wojciech Klama        if slen is None:
257*134e1779SJakub Wojciech Klama            return None, offset
258*134e1779SJakub Wojciech Klama        nexto = offset + slen
259*134e1779SJakub Wojciech Klama        if len(bstring) < nexto:
260*134e1779SJakub Wojciech Klama            if noerror:
261*134e1779SJakub Wojciech Klama                val = None
262*134e1779SJakub Wojciech Klama            else:
263*134e1779SJakub Wojciech Klama                raise SequenceError('out of data '
264*134e1779SJakub Wojciech Klama                    'while unpacking {0!r}'.format(self.name))
265*134e1779SJakub Wojciech Klama        else:
266*134e1779SJakub Wojciech Klama            val = bstring[offset:nexto]
267*134e1779SJakub Wojciech Klama            if self.autob2s:
268*134e1779SJakub Wojciech Klama                val = self.b2s(val)
269*134e1779SJakub Wojciech Klama        return val, nexto
270*134e1779SJakub Wojciech Klama
271*134e1779SJakub Wojciech Klama# string length: 2 byte unsigned field
272*134e1779SJakub Wojciech KlamaEncDecSimple.string_len = _ProtoStruct[2]
273*134e1779SJakub Wojciech Klama
274*134e1779SJakub Wojciech Klamaclass EncDecTyped(EncDec):
275*134e1779SJakub Wojciech Klama    r"""
276*134e1779SJakub Wojciech Klama    EncDec for typed objects (which are build from PFODs, which are
277*134e1779SJakub Wojciech Klama    a sneaky class variant of OrderedDict similar to namedtuple).
278*134e1779SJakub Wojciech Klama
279*134e1779SJakub Wojciech Klama    Calling the klass() function with no arguments must create an
280*134e1779SJakub Wojciech Klama    instance with all-None members.
281*134e1779SJakub Wojciech Klama
282*134e1779SJakub Wojciech Klama    We also require a Sequencer to pack and unpack the members of
283*134e1779SJakub Wojciech Klama    the underlying pfod.
284*134e1779SJakub Wojciech Klama
285*134e1779SJakub Wojciech Klama    >>> qid_s = Sequencer('qid')
286*134e1779SJakub Wojciech Klama    >>> qid_s.append_encdec(None, EncDecSimple('type', 1))
287*134e1779SJakub Wojciech Klama    >>> qid_s.append_encdec(None, EncDecSimple('version', 4))
288*134e1779SJakub Wojciech Klama    >>> qid_s.append_encdec(None, EncDecSimple('path', 8))
289*134e1779SJakub Wojciech Klama    >>> len(qid_s)
290*134e1779SJakub Wojciech Klama    3
291*134e1779SJakub Wojciech Klama
292*134e1779SJakub Wojciech Klama    >>> from pfod import pfod
293*134e1779SJakub Wojciech Klama    >>> qid = pfod('qid', ['type', 'version', 'path'])
294*134e1779SJakub Wojciech Klama    >>> len(qid._fields)
295*134e1779SJakub Wojciech Klama    3
296*134e1779SJakub Wojciech Klama    >>> qid_inst = qid(1, 2, 3)
297*134e1779SJakub Wojciech Klama    >>> qid_inst
298*134e1779SJakub Wojciech Klama    qid(type=1, version=2, path=3)
299*134e1779SJakub Wojciech Klama
300*134e1779SJakub Wojciech Klama    >>> e = EncDecTyped(qid, 'aqid', qid_s)
301*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({}, {}, qid_inst))
302*134e1779SJakub Wojciech Klama    '\x01\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00'
303*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {},
304*134e1779SJakub Wojciech Klama    ... b'\x01\x02\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00', 0)
305*134e1779SJakub Wojciech Klama    (qid(type=1, version=2, path=3), 13)
306*134e1779SJakub Wojciech Klama
307*134e1779SJakub Wojciech Klama    If an EncDecTyped instance has a conditional sequencer, note
308*134e1779SJakub Wojciech Klama    that unpacking will leave un-selected items set to None (see
309*134e1779SJakub Wojciech Klama    the Sequencer example below):
310*134e1779SJakub Wojciech Klama
311*134e1779SJakub Wojciech Klama    >>> breakfast = pfod('breakfast', 'eggs spam ham')
312*134e1779SJakub Wojciech Klama    >>> breakfast()
313*134e1779SJakub Wojciech Klama    breakfast(eggs=None, spam=None, ham=None)
314*134e1779SJakub Wojciech Klama    >>> bfseq = Sequencer('breakfast')
315*134e1779SJakub Wojciech Klama    >>> bfseq.append_encdec(None, EncDecSimple('eggs', 1))
316*134e1779SJakub Wojciech Klama    >>> bfseq.append_encdec('yuck', EncDecSimple('spam', 1))
317*134e1779SJakub Wojciech Klama    >>> bfseq.append_encdec(None, EncDecSimple('ham', 1))
318*134e1779SJakub Wojciech Klama    >>> e = EncDecTyped(breakfast, 'bfname', bfseq)
319*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {'yuck': False}, b'\x02\x01\x04', 0)
320*134e1779SJakub Wojciech Klama    (breakfast(eggs=2, spam=None, ham=1), 2)
321*134e1779SJakub Wojciech Klama
322*134e1779SJakub Wojciech Klama    This used just two of the three bytes: eggs=2, ham=1.
323*134e1779SJakub Wojciech Klama
324*134e1779SJakub Wojciech Klama    >>> e.unpack({}, {'yuck': True}, b'\x02\x01\x04', 0)
325*134e1779SJakub Wojciech Klama    (breakfast(eggs=2, spam=1, ham=4), 3)
326*134e1779SJakub Wojciech Klama
327*134e1779SJakub Wojciech Klama    This used the third byte, so ham=4.
328*134e1779SJakub Wojciech Klama    """
329*134e1779SJakub Wojciech Klama    def __init__(self, klass, name, sequence, aux=None):
330*134e1779SJakub Wojciech Klama        assert len(sequence) == len(klass()._fields) # temporary
331*134e1779SJakub Wojciech Klama        super(EncDecTyped, self).__init__(name, aux)
332*134e1779SJakub Wojciech Klama        self.klass = klass
333*134e1779SJakub Wojciech Klama        self.name = name
334*134e1779SJakub Wojciech Klama        self.sequence = sequence
335*134e1779SJakub Wojciech Klama
336*134e1779SJakub Wojciech Klama    def __repr__(self):
337*134e1779SJakub Wojciech Klama        if self.aux is None:
338*134e1779SJakub Wojciech Klama            return '{0}({1!r}, {2!r}, {3!r})'.format(self.__class__.__name__,
339*134e1779SJakub Wojciech Klama                self.klass, self.name, self.sequence)
340*134e1779SJakub Wojciech Klama        return '{0}({1!r}, {2!r}, {3!r}, {4!r})'.format(self.__class__.__name__,
341*134e1779SJakub Wojciech Klama            self.klass, self.name, self.sequence, self.aux)
342*134e1779SJakub Wojciech Klama
343*134e1779SJakub Wojciech Klama    __str__ = __repr__
344*134e1779SJakub Wojciech Klama
345*134e1779SJakub Wojciech Klama    def apack(self, vdict, cdict, val):
346*134e1779SJakub Wojciech Klama        """
347*134e1779SJakub Wojciech Klama        Pack each of our instance variables.
348*134e1779SJakub Wojciech Klama
349*134e1779SJakub Wojciech Klama        Note that some packing may be conditional.
350*134e1779SJakub Wojciech Klama        """
351*134e1779SJakub Wojciech Klama        return self.sequence.apack(val, cdict)
352*134e1779SJakub Wojciech Klama
353*134e1779SJakub Wojciech Klama    def unpack(self, vdict, cdict, bstring, offset, noerror=False):
354*134e1779SJakub Wojciech Klama        """
355*134e1779SJakub Wojciech Klama        Unpack each instance variable, into a new object of
356*134e1779SJakub Wojciech Klama        self.klass.  Return the new instance and new offset.
357*134e1779SJakub Wojciech Klama
358*134e1779SJakub Wojciech Klama        Note that some unpacking may be conditional.
359*134e1779SJakub Wojciech Klama        """
360*134e1779SJakub Wojciech Klama        obj = self.klass()
361*134e1779SJakub Wojciech Klama        offset = self.sequence.unpack_from(obj, cdict, bstring, offset, noerror)
362*134e1779SJakub Wojciech Klama        return obj, offset
363*134e1779SJakub Wojciech Klama
364*134e1779SJakub Wojciech Klamaclass EncDecA(EncDec):
365*134e1779SJakub Wojciech Klama    r"""
366*134e1779SJakub Wojciech Klama    EncDec for arrays (repeated objects).
367*134e1779SJakub Wojciech Klama
368*134e1779SJakub Wojciech Klama    We take the name of repeat count variable, and a sub-coder
369*134e1779SJakub Wojciech Klama    (Sequencer instance).  For instance, we can en/de-code
370*134e1779SJakub Wojciech Klama    repeat='nwname' copies of name='wname', or nwname of
371*134e1779SJakub Wojciech Klama    name='wqid', in a Twalk en/de-code.
372*134e1779SJakub Wojciech Klama
373*134e1779SJakub Wojciech Klama    Note that we don't pack or unpack the repeat count itself --
374*134e1779SJakub Wojciech Klama    that must be done by higher level code.  We just get its value
375*134e1779SJakub Wojciech Klama    from vdict.
376*134e1779SJakub Wojciech Klama
377*134e1779SJakub Wojciech Klama    >>> subcode = EncDecSimple('wname', '_string_')
378*134e1779SJakub Wojciech Klama    >>> e = EncDecA('nwname', 'wname', subcode)
379*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({'nwname': 2}, {}, ['A', 'BC']))
380*134e1779SJakub Wojciech Klama    '\x01\x00A\x02\x00BC'
381*134e1779SJakub Wojciech Klama
382*134e1779SJakub Wojciech Klama    >>> subcode.autob2s = True # so that A and BC decode to py3k str
383*134e1779SJakub Wojciech Klama    >>> e.unpack({'nwname': 2}, {}, b'\x01\x00A\x02\x00BC', 0)
384*134e1779SJakub Wojciech Klama    (['A', 'BC'], 7)
385*134e1779SJakub Wojciech Klama
386*134e1779SJakub Wojciech Klama    When using noerror, the first sub-item that fails to decode
387*134e1779SJakub Wojciech Klama    completely starts the None-s.  Strings whose length fails to
388*134e1779SJakub Wojciech Klama    decode are assumed to be zero bytes long as well, for the
389*134e1779SJakub Wojciech Klama    purpose of showing the expected packet length:
390*134e1779SJakub Wojciech Klama
391*134e1779SJakub Wojciech Klama    >>> e.unpack({'nwname': 2}, {}, b'\x01\x00A\x02\x00', 0, noerror=True)
392*134e1779SJakub Wojciech Klama    (['A', None], 7)
393*134e1779SJakub Wojciech Klama    >>> e.unpack({'nwname': 2}, {}, b'\x01\x00A\x02', 0, noerror=True)
394*134e1779SJakub Wojciech Klama    (['A', None], 5)
395*134e1779SJakub Wojciech Klama    >>> e.unpack({'nwname': 3}, {}, b'\x01\x00A\x02', 0, noerror=True)
396*134e1779SJakub Wojciech Klama    (['A', None, None], 7)
397*134e1779SJakub Wojciech Klama
398*134e1779SJakub Wojciech Klama    As a special case, supplying None for the sub-coder
399*134e1779SJakub Wojciech Klama    makes the repeated item pack or unpack a simple byte
400*134e1779SJakub Wojciech Klama    string.  (Note that autob2s is not supported here.)
401*134e1779SJakub Wojciech Klama    A too-short byte string is simply truncated!
402*134e1779SJakub Wojciech Klama
403*134e1779SJakub Wojciech Klama    >>> e = EncDecA('count', 'data', None)
404*134e1779SJakub Wojciech Klama    >>> e.b2s(e.pack({'count': 5}, {}, b'12345'))
405*134e1779SJakub Wojciech Klama    '12345'
406*134e1779SJakub Wojciech Klama    >>> x = list(e.unpack({'count': 3}, {}, b'123', 0))
407*134e1779SJakub Wojciech Klama    >>> x[0] = e.b2s(x[0])
408*134e1779SJakub Wojciech Klama    >>> x
409*134e1779SJakub Wojciech Klama    ['123', 3]
410*134e1779SJakub Wojciech Klama    >>> x = list(e.unpack({'count': 3}, {}, b'12', 0, noerror=True))
411*134e1779SJakub Wojciech Klama    >>> x[0] = e.b2s(x[0])
412*134e1779SJakub Wojciech Klama    >>> x
413*134e1779SJakub Wojciech Klama    ['12', 3]
414*134e1779SJakub Wojciech Klama    """
415*134e1779SJakub Wojciech Klama    def __init__(self, repeat, name, sub, aux=None):
416*134e1779SJakub Wojciech Klama        super(EncDecA, self).__init__(name, aux)
417*134e1779SJakub Wojciech Klama        self.repeat = repeat
418*134e1779SJakub Wojciech Klama        self.name = name
419*134e1779SJakub Wojciech Klama        self.sub = sub
420*134e1779SJakub Wojciech Klama
421*134e1779SJakub Wojciech Klama    def __repr__(self):
422*134e1779SJakub Wojciech Klama        if self.aux is None:
423*134e1779SJakub Wojciech Klama            return '{0}({1!r}, {2!r}, {3!r})'.format(self.__class__.__name__,
424*134e1779SJakub Wojciech Klama                self.repeat, self.name, self.sub)
425*134e1779SJakub Wojciech Klama        return '{0}({1!r}, {2!r}, {3!r}, {4!r})'.format(self.__class__.__name__,
426*134e1779SJakub Wojciech Klama            self.repeat, self.name, self.sub, self.aux)
427*134e1779SJakub Wojciech Klama
428*134e1779SJakub Wojciech Klama    __str__ = __repr__
429*134e1779SJakub Wojciech Klama
430*134e1779SJakub Wojciech Klama    def apack(self, vdict, cdict, val):
431*134e1779SJakub Wojciech Klama        "pack each val[i], for i in range(vdict[self.repeat])"
432*134e1779SJakub Wojciech Klama        num = vdict[self.repeat]
433*134e1779SJakub Wojciech Klama        assert num == len(val)
434*134e1779SJakub Wojciech Klama        if self.sub is None:
435*134e1779SJakub Wojciech Klama            assert isinstance(val, bytes)
436*134e1779SJakub Wojciech Klama            return [val]
437*134e1779SJakub Wojciech Klama        parts = []
438*134e1779SJakub Wojciech Klama        for i in val:
439*134e1779SJakub Wojciech Klama            parts.extend(self.sub.apack(vdict, cdict, i))
440*134e1779SJakub Wojciech Klama        return parts
441*134e1779SJakub Wojciech Klama
442*134e1779SJakub Wojciech Klama    def unpack(self, vdict, cdict, bstring, offset, noerror=False):
443*134e1779SJakub Wojciech Klama        "unpack repeatedly, per self.repeat, into new array."
444*134e1779SJakub Wojciech Klama        num = vdict[self.repeat]
445*134e1779SJakub Wojciech Klama        if num is None and noerror:
446*134e1779SJakub Wojciech Klama            num = 0
447*134e1779SJakub Wojciech Klama        else:
448*134e1779SJakub Wojciech Klama            assert num >= 0
449*134e1779SJakub Wojciech Klama        if self.sub is None:
450*134e1779SJakub Wojciech Klama            nexto = offset + num
451*134e1779SJakub Wojciech Klama            if len(bstring) < nexto and not noerror:
452*134e1779SJakub Wojciech Klama                raise SequenceError('out of data '
453*134e1779SJakub Wojciech Klama                    'while unpacking {0!r}'.format(self.name))
454*134e1779SJakub Wojciech Klama            return bstring[offset:nexto], nexto
455*134e1779SJakub Wojciech Klama        array = []
456*134e1779SJakub Wojciech Klama        for i in range(num):
457*134e1779SJakub Wojciech Klama            obj, offset = self.sub.unpack(vdict, cdict, bstring, offset,
458*134e1779SJakub Wojciech Klama                noerror)
459*134e1779SJakub Wojciech Klama            array.append(obj)
460*134e1779SJakub Wojciech Klama        return array, offset
461*134e1779SJakub Wojciech Klama
462*134e1779SJakub Wojciech Klamaclass SequenceError(Exception):
463*134e1779SJakub Wojciech Klama    "sequence error: item too big, or ran out of data"
464*134e1779SJakub Wojciech Klama    pass
465*134e1779SJakub Wojciech Klama
466*134e1779SJakub Wojciech Klamaclass Sequencer(object):
467*134e1779SJakub Wojciech Klama    r"""
468*134e1779SJakub Wojciech Klama    A sequencer is an object that packs (marshals) or unpacks
469*134e1779SJakub Wojciech Klama    (unmarshals) a series of objects, according to their EncDec
470*134e1779SJakub Wojciech Klama    instances.
471*134e1779SJakub Wojciech Klama
472*134e1779SJakub Wojciech Klama    The objects themselves (and their values) come from, or
473*134e1779SJakub Wojciech Klama    go into, a dictionary: <vdict>, the first argument to
474*134e1779SJakub Wojciech Klama    pack/unpack.
475*134e1779SJakub Wojciech Klama
476*134e1779SJakub Wojciech Klama    Some fields may be conditional.  The conditions are in a
477*134e1779SJakub Wojciech Klama    separate dictionary (the second or <cdict> argument).
478*134e1779SJakub Wojciech Klama
479*134e1779SJakub Wojciech Klama    Some objects may be dictionaries or PFODs, e.g., they may
480*134e1779SJakub Wojciech Klama    be a Plan9 qid or stat structure.  These have their own
481*134e1779SJakub Wojciech Klama    sub-encoding.
482*134e1779SJakub Wojciech Klama
483*134e1779SJakub Wojciech Klama    As with each encoder, we have both an apack() function
484*134e1779SJakub Wojciech Klama    (returns a list of parts) and a plain pack().  Users should
485*134e1779SJakub Wojciech Klama    mostly stick with plain pack().
486*134e1779SJakub Wojciech Klama
487*134e1779SJakub Wojciech Klama    >>> s = Sequencer('monty')
488*134e1779SJakub Wojciech Klama    >>> s
489*134e1779SJakub Wojciech Klama    Sequencer('monty')
490*134e1779SJakub Wojciech Klama    >>> e = EncDecSimple('eggs', 2)
491*134e1779SJakub Wojciech Klama    >>> s.append_encdec(None, e)
492*134e1779SJakub Wojciech Klama    >>> s.append_encdec(None, EncDecSimple('spam', 1))
493*134e1779SJakub Wojciech Klama    >>> s[0]
494*134e1779SJakub Wojciech Klama    (None, EncDecSimple('eggs', 2))
495*134e1779SJakub Wojciech Klama    >>> e.b2s(s.pack({'eggs': 513, 'spam': 65}, {}))
496*134e1779SJakub Wojciech Klama    '\x01\x02A'
497*134e1779SJakub Wojciech Klama
498*134e1779SJakub Wojciech Klama    When particular fields are conditional, they appear in
499*134e1779SJakub Wojciech Klama    packed output, or are taken from the byte-string during
500*134e1779SJakub Wojciech Klama    unpacking, only if their condition is true.
501*134e1779SJakub Wojciech Klama
502*134e1779SJakub Wojciech Klama    As with struct, use unpack_from to start at an arbitrary
503*134e1779SJakub Wojciech Klama    offset and/or omit verification that the entire byte-string
504*134e1779SJakub Wojciech Klama    is consumed.
505*134e1779SJakub Wojciech Klama
506*134e1779SJakub Wojciech Klama    >>> s = Sequencer('python')
507*134e1779SJakub Wojciech Klama    >>> s.append_encdec(None, e)
508*134e1779SJakub Wojciech Klama    >>> s.append_encdec('.u', EncDecSimple('spam', 1))
509*134e1779SJakub Wojciech Klama    >>> s[1]
510*134e1779SJakub Wojciech Klama    ('.u', EncDecSimple('spam', 1))
511*134e1779SJakub Wojciech Klama    >>> e.b2s(s.pack({'eggs': 513, 'spam': 65}, {'.u': True}))
512*134e1779SJakub Wojciech Klama    '\x01\x02A'
513*134e1779SJakub Wojciech Klama    >>> e.b2s(s.pack({'eggs': 513, 'spam': 65}, {'.u': False}))
514*134e1779SJakub Wojciech Klama    '\x01\x02'
515*134e1779SJakub Wojciech Klama
516*134e1779SJakub Wojciech Klama    >>> d = {}
517*134e1779SJakub Wojciech Klama    >>> s.unpack(d, {'.u': True}, b'\x01\x02A')
518*134e1779SJakub Wojciech Klama    >>> print(d['eggs'], d['spam'])
519*134e1779SJakub Wojciech Klama    513 65
520*134e1779SJakub Wojciech Klama    >>> d = {}
521*134e1779SJakub Wojciech Klama    >>> s.unpack(d, {'.u': False}, b'\x01\x02A', 0)
522*134e1779SJakub Wojciech Klama    Traceback (most recent call last):
523*134e1779SJakub Wojciech Klama        ...
524*134e1779SJakub Wojciech Klama    SequenceError: 1 byte(s) unconsumed
525*134e1779SJakub Wojciech Klama    >>> s.unpack_from(d, {'.u': False}, b'\x01\x02A', 0)
526*134e1779SJakub Wojciech Klama    2
527*134e1779SJakub Wojciech Klama    >>> print(d)
528*134e1779SJakub Wojciech Klama    {'eggs': 513}
529*134e1779SJakub Wojciech Klama
530*134e1779SJakub Wojciech Klama    The incoming dictionary-like object may be pre-initialized
531*134e1779SJakub Wojciech Klama    if you like; only sequences that decode are filled-in:
532*134e1779SJakub Wojciech Klama
533*134e1779SJakub Wojciech Klama    >>> d = {'eggs': None, 'spam': None}
534*134e1779SJakub Wojciech Klama    >>> s.unpack_from(d, {'.u': False}, b'\x01\x02A', 0)
535*134e1779SJakub Wojciech Klama    2
536*134e1779SJakub Wojciech Klama    >>> print(d['eggs'], d['spam'])
537*134e1779SJakub Wojciech Klama    513 None
538*134e1779SJakub Wojciech Klama
539*134e1779SJakub Wojciech Klama    Some objects may be arrays; if so their EncDec is actually
540*134e1779SJakub Wojciech Klama    an EncDecA, the repeat count must be in the dictionary, and
541*134e1779SJakub Wojciech Klama    the object itself must have a len() and be index-able:
542*134e1779SJakub Wojciech Klama
543*134e1779SJakub Wojciech Klama    >>> s = Sequencer('arr')
544*134e1779SJakub Wojciech Klama    >>> s.append_encdec(None, EncDecSimple('n', 1))
545*134e1779SJakub Wojciech Klama    >>> ae = EncDecSimple('array', 2)
546*134e1779SJakub Wojciech Klama    >>> s.append_encdec(None, EncDecA('n', 'array', ae))
547*134e1779SJakub Wojciech Klama    >>> ae.b2s(s.pack({'n': 2, 'array': [257, 514]}, {}))
548*134e1779SJakub Wojciech Klama    '\x02\x01\x01\x02\x02'
549*134e1779SJakub Wojciech Klama
550*134e1779SJakub Wojciech Klama    Unpacking an array creates a list of the number of items.
551*134e1779SJakub Wojciech Klama    The EncDec encoder that decodes the number of items needs to
552*134e1779SJakub Wojciech Klama    occur first in the sequencer, so that the dictionary will have
553*134e1779SJakub Wojciech Klama    acquired the repeat-count variable's value by the time we hit
554*134e1779SJakub Wojciech Klama    the array's encdec:
555*134e1779SJakub Wojciech Klama
556*134e1779SJakub Wojciech Klama    >>> d = {}
557*134e1779SJakub Wojciech Klama    >>> s.unpack(d, {}, b'\x01\x04\x00')
558*134e1779SJakub Wojciech Klama    >>> d['n'], d['array']
559*134e1779SJakub Wojciech Klama    (1, [4])
560*134e1779SJakub Wojciech Klama    """
561*134e1779SJakub Wojciech Klama    def __init__(self, name):
562*134e1779SJakub Wojciech Klama        self.name = name
563*134e1779SJakub Wojciech Klama        self._codes = []
564*134e1779SJakub Wojciech Klama        self.debug = False # or sys.stderr
565*134e1779SJakub Wojciech Klama
566*134e1779SJakub Wojciech Klama    def __repr__(self):
567*134e1779SJakub Wojciech Klama        return '{0}({1!r})'.format(self.__class__.__name__, self.name)
568*134e1779SJakub Wojciech Klama
569*134e1779SJakub Wojciech Klama    __str__ = __repr__
570*134e1779SJakub Wojciech Klama
571*134e1779SJakub Wojciech Klama    def __len__(self):
572*134e1779SJakub Wojciech Klama        return len(self._codes)
573*134e1779SJakub Wojciech Klama
574*134e1779SJakub Wojciech Klama    def __iter__(self):
575*134e1779SJakub Wojciech Klama        return iter(self._codes)
576*134e1779SJakub Wojciech Klama
577*134e1779SJakub Wojciech Klama    def __getitem__(self, index):
578*134e1779SJakub Wojciech Klama        return self._codes[index]
579*134e1779SJakub Wojciech Klama
580*134e1779SJakub Wojciech Klama    def dprint(self, *args, **kwargs):
581*134e1779SJakub Wojciech Klama        if not self.debug:
582*134e1779SJakub Wojciech Klama            return
583*134e1779SJakub Wojciech Klama        if isinstance(self.debug, bool):
584*134e1779SJakub Wojciech Klama            dest = sys.stdout
585*134e1779SJakub Wojciech Klama        else:
586*134e1779SJakub Wojciech Klama            dest = self.debug
587*134e1779SJakub Wojciech Klama        print(*args, file=dest, **kwargs)
588*134e1779SJakub Wojciech Klama
589*134e1779SJakub Wojciech Klama    def append_encdec(self, cond, code):
590*134e1779SJakub Wojciech Klama        "add EncDec en/de-coder, conditional on cond"
591*134e1779SJakub Wojciech Klama        self._codes.append((cond, code))
592*134e1779SJakub Wojciech Klama
593*134e1779SJakub Wojciech Klama    def apack(self, vdict, cdict):
594*134e1779SJakub Wojciech Klama        """
595*134e1779SJakub Wojciech Klama        Produce packed representation of each field.
596*134e1779SJakub Wojciech Klama        """
597*134e1779SJakub Wojciech Klama        packed_data = []
598*134e1779SJakub Wojciech Klama        for cond, code in self._codes:
599*134e1779SJakub Wojciech Klama            # Skip this item if it's conditional on a false thing.
600*134e1779SJakub Wojciech Klama            if cond is not None and not cdict[cond]:
601*134e1779SJakub Wojciech Klama                self.dprint('skip %r - %r is False' % (code, cond))
602*134e1779SJakub Wojciech Klama                continue
603*134e1779SJakub Wojciech Klama
604*134e1779SJakub Wojciech Klama            # Pack the item.
605*134e1779SJakub Wojciech Klama            self.dprint('pack %r - no cond or %r is True' % (code, cond))
606*134e1779SJakub Wojciech Klama            packed_data.extend(code.apack(vdict, cdict, vdict[code.name]))
607*134e1779SJakub Wojciech Klama
608*134e1779SJakub Wojciech Klama        return packed_data
609*134e1779SJakub Wojciech Klama
610*134e1779SJakub Wojciech Klama    def pack(self, vdict, cdict):
611*134e1779SJakub Wojciech Klama        """
612*134e1779SJakub Wojciech Klama        Flatten packed data.
613*134e1779SJakub Wojciech Klama        """
614*134e1779SJakub Wojciech Klama        return b''.join(self.apack(vdict, cdict))
615*134e1779SJakub Wojciech Klama
616*134e1779SJakub Wojciech Klama    def unpack_from(self, vdict, cdict, bstring, offset=0, noerror=False):
617*134e1779SJakub Wojciech Klama        """
618*134e1779SJakub Wojciech Klama        Unpack from byte string.
619*134e1779SJakub Wojciech Klama
620*134e1779SJakub Wojciech Klama        The values are unpacked into a dictionary vdict;
621*134e1779SJakub Wojciech Klama        some of its entries may themselves be ordered
622*134e1779SJakub Wojciech Klama        dictionaries created by typedefed codes.
623*134e1779SJakub Wojciech Klama
624*134e1779SJakub Wojciech Klama        Raises SequenceError if the string is too short,
625*134e1779SJakub Wojciech Klama        unless you set noerror, in which case we assume
626*134e1779SJakub Wojciech Klama        you want see what you can get out of the data.
627*134e1779SJakub Wojciech Klama        """
628*134e1779SJakub Wojciech Klama        for cond, code in self._codes:
629*134e1779SJakub Wojciech Klama            # Skip this item if it's conditional on a false thing.
630*134e1779SJakub Wojciech Klama            if cond is not None and not cdict[cond]:
631*134e1779SJakub Wojciech Klama                self.dprint('skip %r - %r is False' % (code, cond))
632*134e1779SJakub Wojciech Klama                continue
633*134e1779SJakub Wojciech Klama
634*134e1779SJakub Wojciech Klama            # Unpack the item.
635*134e1779SJakub Wojciech Klama            self.dprint('unpack %r - no cond or %r is True' % (code, cond))
636*134e1779SJakub Wojciech Klama            obj, offset = code.unpack(vdict, cdict, bstring, offset, noerror)
637*134e1779SJakub Wojciech Klama            vdict[code.name] = obj
638*134e1779SJakub Wojciech Klama
639*134e1779SJakub Wojciech Klama        return offset
640*134e1779SJakub Wojciech Klama
641*134e1779SJakub Wojciech Klama    def unpack(self, vdict, cdict, bstring, noerror=False):
642*134e1779SJakub Wojciech Klama        """
643*134e1779SJakub Wojciech Klama        Like unpack_from but unless noerror=True, requires that
644*134e1779SJakub Wojciech Klama        we completely use up the given byte string.
645*134e1779SJakub Wojciech Klama        """
646*134e1779SJakub Wojciech Klama        offset = self.unpack_from(vdict, cdict, bstring, 0, noerror)
647*134e1779SJakub Wojciech Klama        if not noerror and offset != len(bstring):
648*134e1779SJakub Wojciech Klama            raise SequenceError('{0} byte(s) unconsumed'.format(
649*134e1779SJakub Wojciech Klama                len(bstring) - offset))
650*134e1779SJakub Wojciech Klama
651*134e1779SJakub Wojciech Klamaif __name__ == '__main__':
652*134e1779SJakub Wojciech Klama    import doctest
653*134e1779SJakub Wojciech Klama    doctest.testmod()
654