]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
Initial support for event generated mode
[pyderasn.git] / pyderasn.py
index f1be6473130142969088c58038efc402a5995a10..24204ce8780ca8813723e4d09d715186fe1d1201 100755 (executable)
@@ -898,6 +898,19 @@ def fractions2float(fractions_raw):
     return float("0." + fractions_raw)
 
 
+def get_def_by_path(defines_by_path, sub_decode_path):
+    """Get define by decode path
+    """
+    for path, define in defines_by_path:
+        if len(path) != len(sub_decode_path):
+            continue
+        for p1, p2 in zip(path, sub_decode_path):
+            if (not p1 is any) and (p1 != p2):
+                break
+        else:
+            return define
+
+
 ########################################################################
 # Errors
 ########################################################################
@@ -1331,8 +1344,8 @@ class Obj(object):
     def _encode(self):  # pragma: no cover
         raise NotImplementedError()
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):  # pragma: no cover
-        raise NotImplementedError()
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):  # pragma: no cover
+        yield NotImplemented
 
     def encode(self):
         """Encode the structure
@@ -1358,6 +1371,32 @@ class Obj(object):
             ctx=None,
             tag_only=False,
             _ctx_immutable=True,
+    ):
+        result = next(self.decode_evgen(
+            data,
+            offset,
+            leavemm,
+            decode_path,
+            ctx,
+            tag_only,
+            _ctx_immutable,
+            _evgen_mode=False,
+        ))
+        if result is None:
+            return None
+        _, obj, tail = result
+        return obj, tail
+
+    def decode_evgen(
+            self,
+            data,
+            offset=0,
+            leavemm=False,
+            decode_path=(),
+            ctx=None,
+            tag_only=False,
+            _ctx_immutable=True,
+            _evgen_mode=True,
     ):
         """Decode the data
 
@@ -1380,17 +1419,26 @@ class Obj(object):
         elif _ctx_immutable:
             ctx = copy(ctx)
         tlv = memoryview(data)
+        if (
+                _evgen_mode and
+                get_def_by_path(ctx.get("evgen_mode_upto", ()), decode_path) is not None
+        ):
+            _evgen_mode = False
         if self._expl is None:
-            result = self._decode(
-                tlv,
-                offset,
-                decode_path=decode_path,
-                ctx=ctx,
-                tag_only=tag_only,
-            )
-            if tag_only:
-                return None
-            obj, tail = result
+            for result in self._decode(
+                    tlv,
+                    offset=offset,
+                    decode_path=decode_path,
+                    ctx=ctx,
+                    tag_only=tag_only,
+                    evgen_mode=_evgen_mode,
+            ):
+                if tag_only:
+                    yield None
+                    return
+                _decode_path, obj, tail = result
+                if not _decode_path is decode_path:
+                    yield result
         else:
             try:
                 t, tlen, lv = tag_strip(tlv)
@@ -1419,16 +1467,20 @@ class Obj(object):
                     )
                 llen, v = 1, lv[1:]
                 offset += tlen + llen
-                result = self._decode(
-                    v,
-                    offset=offset,
-                    decode_path=decode_path,
-                    ctx=ctx,
-                    tag_only=tag_only,
-                )
-                if tag_only:  # pragma: no cover
-                    return None
-                obj, tail = result
+                for result in self._decode(
+                        v,
+                        offset=offset,
+                        decode_path=decode_path,
+                        ctx=ctx,
+                        tag_only=tag_only,
+                        evgen_mode=_evgen_mode,
+                ):
+                    if tag_only:  # pragma: no cover
+                        yield None
+                        return
+                    _decode_path, obj, tail = result
+                    if not _decode_path is decode_path:
+                        yield result
                 eoc_expected, tail = tail[:EOC_LEN], tail[EOC_LEN:]
                 if eoc_expected.tobytes() != EOC:
                     raise DecodeError(
@@ -1454,16 +1506,20 @@ class Obj(object):
                         decode_path=decode_path,
                         offset=offset,
                     )
-                result = self._decode(
-                    v,
-                    offset=offset + tlen + llen,
-                    decode_path=decode_path,
-                    ctx=ctx,
-                    tag_only=tag_only,
-                )
-                if tag_only:  # pragma: no cover
-                    return None
-                obj, tail = result
+                for result in self._decode(
+                        v,
+                        offset=offset + tlen + llen,
+                        decode_path=decode_path,
+                        ctx=ctx,
+                        tag_only=tag_only,
+                        evgen_mode=_evgen_mode,
+                ):
+                    if tag_only:  # pragma: no cover
+                        yield None
+                        return
+                    _decode_path, obj, tail = result
+                    if not _decode_path is decode_path:
+                        yield result
                 if obj.tlvlen < l and not ctx.get("allow_expl_oob", False):
                     raise DecodeError(
                         "explicit tag out-of-bound, longer than data",
@@ -1471,7 +1527,7 @@ class Obj(object):
                         decode_path=decode_path,
                         offset=offset,
                     )
-        return obj, (tail if leavemm else tail.tobytes())
+        yield decode_path, obj, (tail if leavemm else tail.tobytes())
 
     def decod(self, data, offset=0, decode_path=(), ctx=None):
         """Decode the data, check that tail is empty
@@ -2014,7 +2070,7 @@ class Boolean(Obj):
             (b"\xFF" if self._value else b"\x00"),
         ))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, _, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -2031,7 +2087,8 @@ class Boolean(Obj):
                 offset=offset,
             )
         if tag_only:
-            return None
+            yield None
+            return
         try:
             l, _, v = len_decode(lv)
         except DecodeError as err:
@@ -2080,7 +2137,7 @@ class Boolean(Obj):
             _decoded=(offset, 1, 1),
         )
         obj.ber_encoded = ber_encoded
-        return obj, v[1:]
+        yield decode_path, obj, v[1:]
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -2343,7 +2400,7 @@ class Integer(Obj):
                     break
         return b"".join((self.tag, len_encode(len(octets)), octets))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, _, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -2360,7 +2417,8 @@ class Integer(Obj):
                 offset=offset,
             )
         if tag_only:
-            return None
+            yield None
+            return
         try:
             l, llen, v = len_decode(lv)
         except DecodeError as err:
@@ -2431,7 +2489,7 @@ class Integer(Obj):
                 decode_path=decode_path,
                 offset=offset,
             )
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -2726,7 +2784,7 @@ class BitString(Obj):
             octets,
         ))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -2738,7 +2796,8 @@ class BitString(Obj):
             )
         if t == self.tag:
             if tag_only:  # pragma: no cover
-                return None
+                yield None
+                return
             try:
                 l, llen, v = len_decode(lv)
             except DecodeError as err:
@@ -2785,8 +2844,9 @@ class BitString(Obj):
                     offset=offset,
                 )
             v, tail = v[:l], v[l:]
+            bit_len = (len(v) - 1) * 8 - pad_size
             obj = self.__class__(
-                value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
+                value=None if evgen_mode else (bit_len, v[1:].tobytes()),
                 impl=self.tag,
                 expl=self._expl,
                 default=self.default,
@@ -2794,7 +2854,10 @@ class BitString(Obj):
                 _specs=self.specs,
                 _decoded=(offset, llen, l),
             )
-            return obj, tail
+            if evgen_mode:
+                obj._value = (bit_len, None)
+            yield decode_path, obj, tail
+            return
         if t != self.tag_constructed:
             raise TagMismatch(
                 klass=self.__class__,
@@ -2809,7 +2872,8 @@ class BitString(Obj):
                 offset=offset,
             )
         if tag_only:  # pragma: no cover
-            return None
+            yield None
+            return
         lenindef = False
         try:
             l, llen, v = len_decode(lv)
@@ -2856,14 +2920,26 @@ class BitString(Obj):
                     )
             sub_decode_path = decode_path + (str(len(chunks)),)
             try:
-                chunk, v_tail = BitString().decode(
-                    v,
-                    offset=sub_offset,
-                    decode_path=sub_decode_path,
-                    leavemm=True,
-                    ctx=ctx,
-                    _ctx_immutable=False,
-                )
+                if evgen_mode:
+                    for _decode_path, chunk, v_tail in BitString().decode_evgen(
+                            v,
+                            offset=sub_offset,
+                            decode_path=sub_decode_path,
+                            leavemm=True,
+                            ctx=ctx,
+                            _ctx_immutable=False,
+                    ):
+                        yield _decode_path, chunk, v_tail
+                else:
+                    _, chunk, v_tail = next(BitString().decode_evgen(
+                        v,
+                        offset=sub_offset,
+                        decode_path=sub_decode_path,
+                        leavemm=True,
+                        ctx=ctx,
+                        _ctx_immutable=False,
+                        _evgen_mode=False,
+                    ))
             except TagMismatch:
                 raise DecodeError(
                     "expected BitString encoded chunk",
@@ -2892,13 +2968,15 @@ class BitString(Obj):
                     decode_path=decode_path + (str(chunk_i),),
                     offset=chunk.offset,
                 )
-            values.append(bytes(chunk))
+            if not evgen_mode:
+                values.append(bytes(chunk))
             bit_len += chunk.bit_len
         chunk_last = chunks[-1]
-        values.append(bytes(chunk_last))
+        if not evgen_mode:
+            values.append(bytes(chunk_last))
         bit_len += chunk_last.bit_len
         obj = self.__class__(
-            value=(bit_len, b"".join(values)),
+            value=None if evgen_mode else (bit_len, b"".join(values)),
             impl=self.tag,
             expl=self._expl,
             default=self.default,
@@ -2906,9 +2984,11 @@ class BitString(Obj):
             _specs=self.specs,
             _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
         )
+        if evgen_mode:
+            obj._value = (bit_len, None)
         obj.lenindef = lenindef
         obj.ber_encoded = True
-        return obj, (v[EOC_LEN:] if lenindef else v)
+        yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -2919,7 +2999,7 @@ class BitString(Obj):
         if self.ready:
             bit_len, blob = self._value
             value = "%d bits" % bit_len
-            if len(self.specs) > 0:
+            if len(self.specs) > 0 and blob is not None:
                 blob = tuple(self.named)
         yield _pp(
             obj=self,
@@ -2994,6 +3074,7 @@ class OctetString(Obj):
     __slots__ = ("tag_constructed", "_bound_min", "_bound_max", "defined")
     tag_default = tag_encode(4)
     asn1_type_name = "OCTET STRING"
+    evgen_mode_skip_value = True
 
     def __init__(
             self,
@@ -3133,7 +3214,7 @@ class OctetString(Obj):
             self._value,
         ))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -3145,7 +3226,8 @@ class OctetString(Obj):
             )
         if t == self.tag:
             if tag_only:
-                return None
+                yield None
+                return
             try:
                 l, llen, v = len_decode(lv)
             except DecodeError as err:
@@ -3163,9 +3245,19 @@ class OctetString(Obj):
                     offset=offset,
                 )
             v, tail = v[:l], v[l:]
+            if evgen_mode and not self._bound_min <= len(v) <= self._bound_max:
+                raise DecodeError(
+                    msg=str(BoundsError(self._bound_min, len(v), self._bound_max)),
+                    klass=self.__class__,
+                    decode_path=decode_path,
+                    offset=offset,
+                )
             try:
                 obj = self.__class__(
-                    value=v.tobytes(),
+                    value=(
+                        None if (evgen_mode and self.evgen_mode_skip_value)
+                        else v.tobytes()
+                    ),
                     bounds=(self._bound_min, self._bound_max),
                     impl=self.tag,
                     expl=self._expl,
@@ -3188,7 +3280,8 @@ class OctetString(Obj):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            return obj, tail
+            yield decode_path, obj, tail
+            return
         if t != self.tag_constructed:
             raise TagMismatch(
                 klass=self.__class__,
@@ -3203,7 +3296,8 @@ class OctetString(Obj):
                 offset=offset,
             )
         if tag_only:
-            return None
+            yield None
+            return
         lenindef = False
         try:
             l, llen, v = len_decode(lv)
@@ -3225,8 +3319,10 @@ class OctetString(Obj):
                 offset=offset,
             )
         chunks = []
+        chunks_count = 0
         sub_offset = offset + tlen + llen
         vlen = 0
+        payload_len = 0
         while True:
             if lenindef:
                 if v[:EOC_LEN].tobytes() == EOC:
@@ -3241,16 +3337,33 @@ class OctetString(Obj):
                         decode_path=decode_path + (str(len(chunks) - 1),),
                         offset=chunks[-1].offset,
                     )
-            sub_decode_path = decode_path + (str(len(chunks)),)
             try:
-                chunk, v_tail = OctetString().decode(
-                    v,
-                    offset=sub_offset,
-                    decode_path=sub_decode_path,
-                    leavemm=True,
-                    ctx=ctx,
-                    _ctx_immutable=False,
-                )
+                if evgen_mode:
+                    sub_decode_path = decode_path + (str(chunks_count),)
+                    for _decode_path, chunk, v_tail in OctetString().decode_evgen(
+                            v,
+                            offset=sub_offset,
+                            decode_path=sub_decode_path,
+                            leavemm=True,
+                            ctx=ctx,
+                            _ctx_immutable=False,
+                    ):
+                        yield _decode_path, chunk, v_tail
+                        if not chunk.ber_encoded:
+                            payload_len += chunk.vlen
+                    chunks_count += 1
+                else:
+                    sub_decode_path = decode_path + (str(len(chunks)),)
+                    _, chunk, v_tail = next(OctetString().decode_evgen(
+                        v,
+                        offset=sub_offset,
+                        decode_path=sub_decode_path,
+                        leavemm=True,
+                        ctx=ctx,
+                        _ctx_immutable=False,
+                        _evgen_mode=False,
+                    ))
+                    chunks.append(chunk)
             except TagMismatch:
                 raise DecodeError(
                     "expected OctetString encoded chunk",
@@ -3258,13 +3371,22 @@ class OctetString(Obj):
                     decode_path=sub_decode_path,
                     offset=sub_offset,
                 )
-            chunks.append(chunk)
             sub_offset += chunk.tlvlen
             vlen += chunk.tlvlen
             v = v_tail
+        if evgen_mode and not self._bound_min <= payload_len <= self._bound_max:
+            raise DecodeError(
+                msg=str(BoundsError(self._bound_min, payload_len, self._bound_max)),
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
         try:
             obj = self.__class__(
-                value=b"".join(bytes(chunk) for chunk in chunks),
+                value=(
+                    None if evgen_mode else
+                    b"".join(bytes(chunk) for chunk in chunks)
+                ),
                 bounds=(self._bound_min, self._bound_max),
                 impl=self.tag,
                 expl=self._expl,
@@ -3289,7 +3411,7 @@ class OctetString(Obj):
             )
         obj.lenindef = lenindef
         obj.ber_encoded = True
-        return obj, (v[EOC_LEN:] if lenindef else v)
+        yield decode_path, obj, (v[EOC_LEN:] if lenindef else v)
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -3403,7 +3525,7 @@ class Null(Obj):
     def _encode(self):
         return self.tag + len_encode(0)
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, _, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -3420,7 +3542,8 @@ class Null(Obj):
                 offset=offset,
             )
         if tag_only:  # pragma: no cover
-            return None
+            yield None
+            return
         try:
             l, _, v = len_decode(lv)
         except DecodeError as err:
@@ -3443,7 +3566,7 @@ class Null(Obj):
             optional=self.optional,
             _decoded=(offset, 1, 0),
         )
-        return obj, v
+        yield decode_path, obj, v
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -3668,7 +3791,7 @@ class ObjectIdentifier(Obj):
         v = b"".join(octets)
         return b"".join((self.tag, len_encode(len(v)), v))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, _, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -3685,7 +3808,8 @@ class ObjectIdentifier(Obj):
                 offset=offset,
             )
         if tag_only:  # pragma: no cover
-            return None
+            yield None
+            return
         try:
             l, llen, v = len_decode(lv)
         except DecodeError as err:
@@ -3755,7 +3879,7 @@ class ObjectIdentifier(Obj):
         )
         if ber_encoded:
             obj.ber_encoded = True
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -4239,6 +4363,7 @@ class UTCTime(VisibleString):
     tag_default = tag_encode(23)
     encoding = "ascii"
     asn1_type_name = "UTCTime"
+    evgen_mode_skip_value = False
 
     def __init__(
             self,
@@ -4810,7 +4935,7 @@ class Choice(Obj):
         self._assert_ready()
         return self._value[1].encode()
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         for choice, spec in iteritems(self.specs):
             sub_decode_path = decode_path + (choice,)
             try:
@@ -4833,15 +4958,28 @@ class Choice(Obj):
                 offset=offset,
             )
         if tag_only:  # pragma: no cover
-            return None
-        value, tail = spec.decode(
-            tlv,
-            offset=offset,
-            leavemm=True,
-            decode_path=sub_decode_path,
-            ctx=ctx,
-            _ctx_immutable=False,
-        )
+            yield None
+            return
+        if evgen_mode:
+            for _decode_path, value, tail in spec.decode_evgen(
+                    tlv,
+                    offset=offset,
+                    leavemm=True,
+                    decode_path=sub_decode_path,
+                    ctx=ctx,
+                    _ctx_immutable=False,
+            ):
+                yield _decode_path, value, tail
+        else:
+            _, value, tail = next(spec.decode_evgen(
+                tlv,
+                offset=offset,
+                leavemm=True,
+                decode_path=sub_decode_path,
+                ctx=ctx,
+                _ctx_immutable=False,
+                _evgen_mode=False,
+            ))
         obj = self.__class__(
             schema=self.specs,
             expl=self._expl,
@@ -4850,7 +4988,7 @@ class Choice(Obj):
             _decoded=(offset, 0, value.fulllen),
         )
         obj._value = (choice, value)
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         value = pp_console_row(next(self.pps()))
@@ -5051,7 +5189,7 @@ class Any(Obj):
         self._assert_ready()
         return self._value
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -5088,14 +5226,15 @@ class Any(Obj):
                 chunk_i += 1
             tlvlen = tlen + llen + vlen + EOC_LEN
             obj = self.__class__(
-                value=tlv[:tlvlen].tobytes(),
+                value=None if evgen_mode else tlv[:tlvlen].tobytes(),
                 expl=self._expl,
                 optional=self.optional,
                 _decoded=(offset, 0, tlvlen),
             )
             obj.lenindef = True
             obj.tag = t.tobytes()
-            return obj, v[EOC_LEN:]
+            yield decode_path, obj, v[EOC_LEN:]
+            return
         except DecodeError as err:
             raise err.__class__(
                 msg=err.msg,
@@ -5113,13 +5252,13 @@ class Any(Obj):
         tlvlen = tlen + llen + l
         v, tail = tlv[:tlvlen], v[l:]
         obj = self.__class__(
-            value=v.tobytes(),
+            value=None if evgen_mode else v.tobytes(),
             expl=self._expl,
             optional=self.optional,
             _decoded=(offset, 0, tlvlen),
         )
         obj.tag = t.tobytes()
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -5293,6 +5432,12 @@ class Sequence(Obj):
     defaulted values existence validation by setting
     ``"allow_default_values": True`` :ref:`context <ctx>` option.
 
+    .. warning::
+
+       Check for default value existence is not performed in
+       ``evgen_mode``, because previously decoded values are not stored
+       in memory, to be able to compare them.
+
     Two sequences are equal if they have equal specification (schema),
     implicit/explicit tagging and the same values.
     """
@@ -5446,7 +5591,7 @@ class Sequence(Obj):
         v = b"".join(v.encode() for v in self._values_for_encoding())
         return b"".join((self.tag, len_encode(len(v)), v))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -5463,7 +5608,8 @@ class Sequence(Obj):
                 offset=offset,
             )
         if tag_only:  # pragma: no cover
-            return None
+            yield None
+            return
         lenindef = False
         ctx_bered = ctx.get("bered", False)
         try:
@@ -5507,21 +5653,33 @@ class Sequence(Obj):
                 continue
             sub_decode_path = decode_path + (name,)
             try:
-                value, v_tail = spec.decode(
-                    v,
-                    sub_offset,
-                    leavemm=True,
-                    decode_path=sub_decode_path,
-                    ctx=ctx,
-                    _ctx_immutable=False,
-                )
+                if evgen_mode:
+                    for _decode_path, value, v_tail in spec.decode_evgen(
+                            v,
+                            sub_offset,
+                            leavemm=True,
+                            decode_path=sub_decode_path,
+                            ctx=ctx,
+                            _ctx_immutable=False,
+                    ):
+                        yield _decode_path, value, v_tail
+                else:
+                    _, value, v_tail = next(spec.decode_evgen(
+                        v,
+                        sub_offset,
+                        leavemm=True,
+                        decode_path=sub_decode_path,
+                        ctx=ctx,
+                        _ctx_immutable=False,
+                        _evgen_mode=False,
+                    ))
             except TagMismatch as err:
                 if (len(err.decode_path) == len(decode_path) + 1) and spec.optional:
                     continue
                 raise
 
             defined = get_def_by_path(ctx.get("_defines", ()), sub_decode_path)
-            if defined is not None:
+            if not evgen_mode and defined is not None:
                 defined_by, defined_spec = defined
                 if issubclass(value.__class__, SequenceOf):
                     for i, _value in enumerate(value):
@@ -5573,31 +5731,32 @@ class Sequence(Obj):
             vlen += value_len
             sub_offset += value_len
             v = v_tail
-            if spec.default is not None and value == spec.default:
-                if ctx_bered or ctx_allow_default_values:
-                    ber_encoded = True
-                else:
-                    raise DecodeError(
-                        "DEFAULT value met",
-                        klass=self.__class__,
-                        decode_path=sub_decode_path,
-                        offset=sub_offset,
-                    )
-            values[name] = value
-
-            spec_defines = getattr(spec, "defines", ())
-            if len(spec_defines) == 0:
-                defines_by_path = ctx.get("defines_by_path", ())
-                if len(defines_by_path) > 0:
-                    spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
-            if spec_defines is not None and len(spec_defines) > 0:
-                for rel_path, schema in spec_defines:
-                    defined = schema.get(value, None)
-                    if defined is not None:
-                        ctx.setdefault("_defines", []).append((
-                            abs_decode_path(sub_decode_path[:-1], rel_path),
-                            (value, defined),
-                        ))
+            if not evgen_mode:
+                if spec.default is not None and value == spec.default:
+                    # This will not work in evgen_mode
+                    if ctx_bered or ctx_allow_default_values:
+                        ber_encoded = True
+                    else:
+                        raise DecodeError(
+                            "DEFAULT value met",
+                            klass=self.__class__,
+                            decode_path=sub_decode_path,
+                            offset=sub_offset,
+                        )
+                values[name] = value
+                spec_defines = getattr(spec, "defines", ())
+                if len(spec_defines) == 0:
+                    defines_by_path = ctx.get("defines_by_path", ())
+                    if len(defines_by_path) > 0:
+                        spec_defines = get_def_by_path(defines_by_path, sub_decode_path)
+                if spec_defines is not None and len(spec_defines) > 0:
+                    for rel_path, schema in spec_defines:
+                        defined = schema.get(value, None)
+                        if defined is not None:
+                            ctx.setdefault("_defines", []).append((
+                                abs_decode_path(sub_decode_path[:-1], rel_path),
+                                (value, defined),
+                            ))
         if lenindef:
             if v[:EOC_LEN].tobytes() != EOC:
                 raise DecodeError(
@@ -5626,7 +5785,7 @@ class Sequence(Obj):
         obj._value = values
         obj.lenindef = lenindef
         obj.ber_encoded = ber_encoded
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         value = pp_console_row(next(self.pps()))
@@ -5694,7 +5853,7 @@ class Set(Sequence):
         ))
         return b"".join((self.tag, len_encode(len(v)), v))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -5711,7 +5870,8 @@ class Set(Sequence):
                 offset=offset,
             )
         if tag_only:
-            return None
+            yield None
+            return
         lenindef = False
         ctx_bered = ctx.get("bered", False)
         try:
@@ -5774,14 +5934,26 @@ class Set(Sequence):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            value, v_tail = spec.decode(
-                v,
-                sub_offset,
-                leavemm=True,
-                decode_path=sub_decode_path,
-                ctx=ctx,
-                _ctx_immutable=False,
-            )
+            if evgen_mode:
+                for _decode_path, value, v_tail in spec.decode_evgen(
+                        v,
+                        sub_offset,
+                        leavemm=True,
+                        decode_path=sub_decode_path,
+                        ctx=ctx,
+                        _ctx_immutable=False,
+                ):
+                    yield _decode_path, value, v_tail
+            else:
+                _, value, v_tail = next(spec.decode_evgen(
+                    v,
+                    sub_offset,
+                    leavemm=True,
+                    decode_path=sub_decode_path,
+                    ctx=ctx,
+                    _ctx_immutable=False,
+                    _evgen_mode=False,
+                ))
             value_tag_order = value.tag_order
             value_len = value.fulllen
             if tag_order_prev >= value_tag_order:
@@ -5830,7 +6002,6 @@ class Set(Sequence):
                 )
             tail = v[EOC_LEN:]
             obj.lenindef = True
-        obj._value = values
         for name, spec in iteritems(self.specs):
             if name not in values and not spec.optional:
                 raise DecodeError(
@@ -5839,8 +6010,10 @@ class Set(Sequence):
                     decode_path=decode_path,
                     offset=offset,
                 )
+        if not evgen_mode:
+            obj._value = values
         obj.ber_encoded = ber_encoded
-        return obj, tail
+        yield decode_path, obj, tail
 
 
 SequenceOfState = namedtuple(
@@ -6043,7 +6216,16 @@ class SequenceOf(Obj):
         v = b"".join(v.encode() for v in self._values_for_encoding())
         return b"".join((self.tag, len_encode(len(v)), v))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only, ordering_check=False):
+    def _decode(
+            self,
+            tlv,
+            offset,
+            decode_path,
+            ctx,
+            tag_only,
+            evgen_mode,
+            ordering_check=False,
+    ):
         try:
             t, tlen, lv = tag_strip(tlv)
         except DecodeError as err:
@@ -6060,7 +6242,8 @@ class SequenceOf(Obj):
                 offset=offset,
             )
         if tag_only:
-            return None
+            yield None
+            return
         lenindef = False
         ctx_bered = ctx.get("bered", False)
         try:
@@ -6094,6 +6277,7 @@ class SequenceOf(Obj):
         vlen = 0
         sub_offset = offset + tlen + llen
         _value = []
+        _value_count = 0
         ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
         value_prev = memoryview(v[:0])
         ber_encoded = False
@@ -6101,15 +6285,27 @@ class SequenceOf(Obj):
         while len(v) > 0:
             if lenindef and v[:EOC_LEN].tobytes() == EOC:
                 break
-            sub_decode_path = decode_path + (str(len(_value)),)
-            value, v_tail = spec.decode(
-                v,
-                sub_offset,
-                leavemm=True,
-                decode_path=sub_decode_path,
-                ctx=ctx,
-                _ctx_immutable=False,
-            )
+            sub_decode_path = decode_path + (str(_value_count),)
+            if evgen_mode:
+                for _decode_path, value, v_tail in spec.decode_evgen(
+                        v,
+                        sub_offset,
+                        leavemm=True,
+                        decode_path=sub_decode_path,
+                        ctx=ctx,
+                        _ctx_immutable=False,
+                ):
+                    yield _decode_path, value, v_tail
+            else:
+                _, value, v_tail = next(spec.decode_evgen(
+                    v,
+                    sub_offset,
+                    leavemm=True,
+                    decode_path=sub_decode_path,
+                    ctx=ctx,
+                    _ctx_immutable=False,
+                    _evgen_mode=False,
+                ))
             value_len = value.fulllen
             if ordering_check:
                 if value_prev.tobytes() > v[:value_len].tobytes():
@@ -6123,13 +6319,22 @@ class SequenceOf(Obj):
                             offset=sub_offset,
                         )
                 value_prev = v[:value_len]
-            _value.append(value)
+            _value_count += 1
+            if not evgen_mode:
+                _value.append(value)
             sub_offset += value_len
             vlen += value_len
             v = v_tail
+        if evgen_mode and not self._bound_min <= _value_count <= self._bound_max:
+            raise DecodeError(
+                msg=str(BoundsError(self._bound_min, _value_count, self._bound_max)),
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
         try:
             obj = self.__class__(
-                value=_value,
+                value=None if evgen_mode else _value,
                 schema=spec,
                 bounds=(self._bound_min, self._bound_max),
                 impl=self.tag,
@@ -6156,7 +6361,7 @@ class SequenceOf(Obj):
             obj.lenindef = True
             tail = v[EOC_LEN:]
         obj.ber_encoded = ber_encoded
-        return obj, tail
+        yield decode_path, obj, tail
 
     def __repr__(self):
         return "%s[%s]" % (
@@ -6206,13 +6411,14 @@ class SetOf(SequenceOf):
         v = b"".join(sorted(v.encode() for v in self._values_for_encoding()))
         return b"".join((self.tag, len_encode(len(v)), v))
 
-    def _decode(self, tlv, offset, decode_path, ctx, tag_only):
+    def _decode(self, tlv, offset, decode_path, ctx, tag_only, evgen_mode):
         return super(SetOf, self)._decode(
             tlv,
             offset,
             decode_path,
             ctx,
             tag_only,
+            evgen_mode,
             ordering_check=True,
         )