]> Cypherpunks.ru repositories - pyderasn.git/blobdiff - pyderasn.py
Faster Set readiness determining during decode
[pyderasn.git] / pyderasn.py
index 852091de22a9fe45f4b3c3f08b9f04a2ee7cca50..ed5b763ccef4123c790d52569d9b376ef2acf168 100755 (executable)
@@ -690,7 +690,7 @@ except ImportError:  # pragma: no cover
     def colored(what, *args, **kwargs):
         return what
 
-__version__ = "6.1"
+__version__ = "6.2"
 
 __all__ = (
     "Any",
@@ -765,15 +765,15 @@ EOC_LEN = len(EOC)
 LENINDEF = b"\x80"  # length indefinite mark
 LENINDEF_PP_CHAR = "I" if PY2 else "∞"
 NAMEDTUPLE_KWARGS = {} if PY2 else {"module": __name__}
-SET01 = frozenset(("0", "1"))
+SET01 = frozenset("01")
+DECIMALS = frozenset(digits)
 DECIMAL_SIGNS = ".,"
 
 
 def pureint(value):
-    i = int(value)
-    if (value[0] in "+- ") or (value[-1] == " "):
+    if not set(value) <= DECIMALS:
         raise ValueError("non-pure integer")
-    return i
+    return int(value)
 
 def fractions2float(fractions_raw):
     pureint(fractions_raw)
@@ -1644,9 +1644,9 @@ def pp_console_row(
                 with_colours,
             ))
     if with_blob:
-        if isinstance(pp.blob, binary_type):
+        if pp.blob.__class__ == binary_type:
             cols.append(hexenc(pp.blob))
-        elif isinstance(pp.blob, tuple):
+        elif pp.blob.__class__ == tuple:
             cols.append(", ".join(pp.blob))
     if pp.optional:
         cols.append(_colourize("OPTIONAL", "red", with_colours))
@@ -1666,12 +1666,12 @@ def pp_console_blob(pp, decode_path_len_decrease=0):
     decode_path_len = len(pp.decode_path) - decode_path_len_decrease
     if decode_path_len > 0:
         cols.append(" ." * (decode_path_len + 1))
-    if isinstance(pp.blob, binary_type):
+    if pp.blob.__class__ == binary_type:
         blob = hexenc(pp.blob).upper()
         for i in six_xrange(0, len(blob), 32):
             chunk = blob[i:i + 32]
             yield " ".join(cols + [colonize_hex(chunk)])
-    elif isinstance(pp.blob, tuple):
+    elif pp.blob.__class__ == tuple:
         yield " ".join(cols + [", ".join(pp.blob)])
 
 
@@ -1801,7 +1801,7 @@ class Boolean(Obj):
                 self._value = default
 
     def _value_sanitize(self, value):
-        if isinstance(value, bool):
+        if value.__class__ == bool:
             return value
         if issubclass(value.__class__, Boolean):
             return value._value
@@ -1850,7 +1850,7 @@ class Boolean(Obj):
         return self._value
 
     def __eq__(self, their):
-        if isinstance(their, bool):
+        if their.__class__ == bool:
             return self._value == their
         if not issubclass(their.__class__, Boolean):
             return False
@@ -2064,7 +2064,7 @@ class Integer(Obj):
         super(Integer, self).__init__(impl, expl, default, optional, _decoded)
         self._value = value
         specs = getattr(self, "schema", {}) if _specs is None else _specs
-        self.specs = specs if isinstance(specs, dict) else dict(specs)
+        self.specs = specs if specs.__class__ == dict else dict(specs)
         self._bound_min, self._bound_max = getattr(
             self,
             "bounds",
@@ -2088,7 +2088,7 @@ class Integer(Obj):
             pass
         elif issubclass(value.__class__, Integer):
             value = value._value
-        elif isinstance(value, str):
+        elif value.__class__ == str:
             value = self.specs.get(value)
             if value is None:
                 raise ObjUnknown("integer value: %s" % value)
@@ -2445,7 +2445,7 @@ class BitString(Obj):
         """
         super(BitString, self).__init__(impl, expl, default, optional, _decoded)
         specs = getattr(self, "schema", {}) if _specs is None else _specs
-        self.specs = specs if isinstance(specs, dict) else dict(specs)
+        self.specs = specs if specs.__class__ == dict else dict(specs)
         self._value = None if value is None else self._value_sanitize(value)
         if default is not None:
             default = self._value_sanitize(default)
@@ -2491,14 +2491,14 @@ class BitString(Obj):
                         len(value) * 4,
                         hexdec(value + ("" if len(value) % 2 == 0 else "0")),
                     )
-            if isinstance(value, binary_type):
+            if value.__class__ == binary_type:
                 return (len(value) * 8, value)
             raise InvalidValueType((self.__class__, string_types, binary_type))
-        if isinstance(value, tuple):
+        if value.__class__ == tuple:
             if (
                     len(value) == 2 and
                     isinstance(value[0], integer_types) and
-                    isinstance(value[1], binary_type)
+                    value[1].__class__ == binary_type
             ):
                 return value
             bits = []
@@ -2573,7 +2573,7 @@ class BitString(Obj):
         return self._value[1]
 
     def __eq__(self, their):
-        if isinstance(their, bytes):
+        if their.__class__ == bytes:
             return self._value[1] == their
         if not issubclass(their.__class__, BitString):
             return False
@@ -2605,7 +2605,7 @@ class BitString(Obj):
         )
 
     def __getitem__(self, key):
-        if isinstance(key, int):
+        if key.__class__ == int:
             bit_len, octets = self._value
             if key >= bit_len:
                 return False
@@ -2630,64 +2630,6 @@ class BitString(Obj):
             octets,
         ))
 
-    def _decode_chunk(self, lv, offset, decode_path):
-        try:
-            l, llen, v = len_decode(lv)
-        except DecodeError as err:
-            raise err.__class__(
-                msg=err.msg,
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        if l > len(v):
-            raise NotEnoughData(
-                "encoded length is longer than data",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        if l == 0:
-            raise NotEnoughData(
-                "zero length",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        pad_size = byte2int(v)
-        if l == 1 and pad_size != 0:
-            raise DecodeError(
-                "invalid empty value",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        if pad_size > 7:
-            raise DecodeError(
-                "too big pad",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
-            raise DecodeError(
-                "invalid pad",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        v, tail = v[:l], v[l:]
-        obj = self.__class__(
-            value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
-            impl=self.tag,
-            expl=self._expl,
-            default=self.default,
-            optional=self.optional,
-            _specs=self.specs,
-            _decoded=(offset, llen, l),
-        )
-        return obj, tail
-
     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
         try:
             t, tlen, lv = tag_strip(tlv)
@@ -2701,23 +2643,8 @@ class BitString(Obj):
         if t == self.tag:
             if tag_only:  # pragma: no cover
                 return None
-            return self._decode_chunk(lv, offset, decode_path)
-        if t == self.tag_constructed:
-            if not ctx.get("bered", False):
-                raise DecodeError(
-                    "unallowed BER constructed encoding",
-                    klass=self.__class__,
-                    decode_path=decode_path,
-                    offset=offset,
-                )
-            if tag_only:  # pragma: no cover
-                return None
-            lenindef = False
             try:
                 l, llen, v = len_decode(lv)
-            except LenIndefForm:
-                llen, l, v = 1, 0, lv[1:]
-                lenindef = True
             except DecodeError as err:
                 raise err.__class__(
                     msg=err.msg,
@@ -2732,90 +2659,160 @@ class BitString(Obj):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            if not lenindef and l == 0:
+            if l == 0:
                 raise NotEnoughData(
                     "zero length",
                     klass=self.__class__,
                     decode_path=decode_path,
                     offset=offset,
                 )
-            chunks = []
-            sub_offset = offset + tlen + llen
-            vlen = 0
-            while True:
-                if lenindef:
-                    if v[:EOC_LEN].tobytes() == EOC:
-                        break
-                else:
-                    if vlen == l:
-                        break
-                    if vlen > l:
-                        raise DecodeError(
-                            "chunk out of bounds",
-                            klass=self.__class__,
-                            decode_path=decode_path + (str(len(chunks) - 1),),
-                            offset=chunks[-1].offset,
-                        )
-                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,
-                    )
-                except TagMismatch:
-                    raise DecodeError(
-                        "expected BitString encoded chunk",
-                        klass=self.__class__,
-                        decode_path=sub_decode_path,
-                        offset=sub_offset,
-                    )
-                chunks.append(chunk)
-                sub_offset += chunk.tlvlen
-                vlen += chunk.tlvlen
-                v = v_tail
-            if len(chunks) == 0:
+            pad_size = byte2int(v)
+            if l == 1 and pad_size != 0:
                 raise DecodeError(
-                    "no chunks",
+                    "invalid empty value",
                     klass=self.__class__,
                     decode_path=decode_path,
                     offset=offset,
                 )
-            values = []
-            bit_len = 0
-            for chunk_i, chunk in enumerate(chunks[:-1]):
-                if chunk.bit_len % 8 != 0:
-                    raise DecodeError(
-                        "BitString chunk is not multiple of 8 bits",
-                        klass=self.__class__,
-                        decode_path=decode_path + (str(chunk_i),),
-                        offset=chunk.offset,
-                    )
-                values.append(bytes(chunk))
-                bit_len += chunk.bit_len
-            chunk_last = chunks[-1]
-            values.append(bytes(chunk_last))
-            bit_len += chunk_last.bit_len
+            if pad_size > 7:
+                raise DecodeError(
+                    "too big pad",
+                    klass=self.__class__,
+                    decode_path=decode_path,
+                    offset=offset,
+                )
+            if byte2int(v[l - 1:l]) & ((1 << pad_size) - 1) != 0:
+                raise DecodeError(
+                    "invalid pad",
+                    klass=self.__class__,
+                    decode_path=decode_path,
+                    offset=offset,
+                )
+            v, tail = v[:l], v[l:]
             obj = self.__class__(
-                value=(bit_len, b"".join(values)),
+                value=((len(v) - 1) * 8 - pad_size, v[1:].tobytes()),
                 impl=self.tag,
                 expl=self._expl,
                 default=self.default,
                 optional=self.optional,
                 _specs=self.specs,
-                _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
+                _decoded=(offset, llen, l),
             )
-            obj.lenindef = lenindef
-            obj.ber_encoded = True
-            return obj, (v[EOC_LEN:] if lenindef else v)
-        raise TagMismatch(
-            klass=self.__class__,
-            decode_path=decode_path,
-            offset=offset,
+            return obj, tail
+        if t != self.tag_constructed:
+            raise TagMismatch(
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if not ctx.get("bered", False):
+            raise DecodeError(
+                "unallowed BER constructed encoding",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if tag_only:  # pragma: no cover
+            return None
+        lenindef = False
+        try:
+            l, llen, v = len_decode(lv)
+        except LenIndefForm:
+            llen, l, v = 1, 0, lv[1:]
+            lenindef = True
+        except DecodeError as err:
+            raise err.__class__(
+                msg=err.msg,
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if l > len(v):
+            raise NotEnoughData(
+                "encoded length is longer than data",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if not lenindef and l == 0:
+            raise NotEnoughData(
+                "zero length",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        chunks = []
+        sub_offset = offset + tlen + llen
+        vlen = 0
+        while True:
+            if lenindef:
+                if v[:EOC_LEN].tobytes() == EOC:
+                    break
+            else:
+                if vlen == l:
+                    break
+                if vlen > l:
+                    raise DecodeError(
+                        "chunk out of bounds",
+                        klass=self.__class__,
+                        decode_path=decode_path + (str(len(chunks) - 1),),
+                        offset=chunks[-1].offset,
+                    )
+            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,
+                )
+            except TagMismatch:
+                raise DecodeError(
+                    "expected BitString encoded chunk",
+                    klass=self.__class__,
+                    decode_path=sub_decode_path,
+                    offset=sub_offset,
+                )
+            chunks.append(chunk)
+            sub_offset += chunk.tlvlen
+            vlen += chunk.tlvlen
+            v = v_tail
+        if len(chunks) == 0:
+            raise DecodeError(
+                "no chunks",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        values = []
+        bit_len = 0
+        for chunk_i, chunk in enumerate(chunks[:-1]):
+            if chunk.bit_len % 8 != 0:
+                raise DecodeError(
+                    "BitString chunk is not multiple of 8 bits",
+                    klass=self.__class__,
+                    decode_path=decode_path + (str(chunk_i),),
+                    offset=chunk.offset,
+                )
+            values.append(bytes(chunk))
+            bit_len += chunk.bit_len
+        chunk_last = chunks[-1]
+        values.append(bytes(chunk_last))
+        bit_len += chunk_last.bit_len
+        obj = self.__class__(
+            value=(bit_len, b"".join(values)),
+            impl=self.tag,
+            expl=self._expl,
+            default=self.default,
+            optional=self.optional,
+            _specs=self.specs,
+            _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
         )
+        obj.lenindef = lenindef
+        obj.ber_encoded = True
+        return obj, (v[EOC_LEN:] if lenindef else v)
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -2957,7 +2954,7 @@ class OctetString(Obj):
         )
 
     def _value_sanitize(self, value):
-        if isinstance(value, binary_type):
+        if value.__class__ == binary_type:
             pass
         elif issubclass(value.__class__, OctetString):
             value = value._value
@@ -3014,7 +3011,7 @@ class OctetString(Obj):
         return self._value
 
     def __eq__(self, their):
-        if isinstance(their, binary_type):
+        if their.__class__ == binary_type:
             return self._value == their
         if not issubclass(their.__class__, OctetString):
             return False
@@ -3056,51 +3053,6 @@ class OctetString(Obj):
             self._value,
         ))
 
-    def _decode_chunk(self, lv, offset, decode_path, ctx):
-        try:
-            l, llen, v = len_decode(lv)
-        except DecodeError as err:
-            raise err.__class__(
-                msg=err.msg,
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        if l > len(v):
-            raise NotEnoughData(
-                "encoded length is longer than data",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        v, tail = v[:l], v[l:]
-        try:
-            obj = self.__class__(
-                value=v.tobytes(),
-                bounds=(self._bound_min, self._bound_max),
-                impl=self.tag,
-                expl=self._expl,
-                default=self.default,
-                optional=self.optional,
-                _decoded=(offset, llen, l),
-                ctx=ctx,
-            )
-        except DecodeError as err:
-            raise DecodeError(
-                msg=err.msg,
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        except BoundsError as err:
-            raise DecodeError(
-                msg=str(err),
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
-        return obj, tail
-
     def _decode(self, tlv, offset, decode_path, ctx, tag_only):
         try:
             t, tlen, lv = tag_strip(tlv)
@@ -3114,23 +3066,8 @@ class OctetString(Obj):
         if t == self.tag:
             if tag_only:
                 return None
-            return self._decode_chunk(lv, offset, decode_path, ctx)
-        if t == self.tag_constructed:
-            if not ctx.get("bered", False):
-                raise DecodeError(
-                    "unallowed BER constructed encoding",
-                    klass=self.__class__,
-                    decode_path=decode_path,
-                    offset=offset,
-                )
-            if tag_only:
-                return None
-            lenindef = False
             try:
                 l, llen, v = len_decode(lv)
-            except LenIndefForm:
-                llen, l, v = 1, 0, lv[1:]
-                lenindef = True
             except DecodeError as err:
                 raise err.__class__(
                     msg=err.msg,
@@ -3145,53 +3082,16 @@ class OctetString(Obj):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            chunks = []
-            sub_offset = offset + tlen + llen
-            vlen = 0
-            while True:
-                if lenindef:
-                    if v[:EOC_LEN].tobytes() == EOC:
-                        break
-                else:
-                    if vlen == l:
-                        break
-                    if vlen > l:
-                        raise DecodeError(
-                            "chunk out of bounds",
-                            klass=self.__class__,
-                            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,
-                    )
-                except TagMismatch:
-                    raise DecodeError(
-                        "expected OctetString encoded chunk",
-                        klass=self.__class__,
-                        decode_path=sub_decode_path,
-                        offset=sub_offset,
-                    )
-                chunks.append(chunk)
-                sub_offset += chunk.tlvlen
-                vlen += chunk.tlvlen
-                v = v_tail
+            v, tail = v[:l], v[l:]
             try:
                 obj = self.__class__(
-                    value=b"".join(bytes(chunk) for chunk in chunks),
+                    value=v.tobytes(),
                     bounds=(self._bound_min, self._bound_max),
                     impl=self.tag,
                     expl=self._expl,
                     default=self.default,
                     optional=self.optional,
-                    _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
+                    _decoded=(offset, llen, l),
                     ctx=ctx,
                 )
             except DecodeError as err:
@@ -3208,14 +3108,108 @@ class OctetString(Obj):
                     decode_path=decode_path,
                     offset=offset,
                 )
-            obj.lenindef = lenindef
-            obj.ber_encoded = True
-            return obj, (v[EOC_LEN:] if lenindef else v)
-        raise TagMismatch(
-            klass=self.__class__,
-            decode_path=decode_path,
-            offset=offset,
-        )
+            return obj, tail
+        if t != self.tag_constructed:
+            raise TagMismatch(
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if not ctx.get("bered", False):
+            raise DecodeError(
+                "unallowed BER constructed encoding",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if tag_only:
+            return None
+        lenindef = False
+        try:
+            l, llen, v = len_decode(lv)
+        except LenIndefForm:
+            llen, l, v = 1, 0, lv[1:]
+            lenindef = True
+        except DecodeError as err:
+            raise err.__class__(
+                msg=err.msg,
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        if l > len(v):
+            raise NotEnoughData(
+                "encoded length is longer than data",
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        chunks = []
+        sub_offset = offset + tlen + llen
+        vlen = 0
+        while True:
+            if lenindef:
+                if v[:EOC_LEN].tobytes() == EOC:
+                    break
+            else:
+                if vlen == l:
+                    break
+                if vlen > l:
+                    raise DecodeError(
+                        "chunk out of bounds",
+                        klass=self.__class__,
+                        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,
+                )
+            except TagMismatch:
+                raise DecodeError(
+                    "expected OctetString encoded chunk",
+                    klass=self.__class__,
+                    decode_path=sub_decode_path,
+                    offset=sub_offset,
+                )
+            chunks.append(chunk)
+            sub_offset += chunk.tlvlen
+            vlen += chunk.tlvlen
+            v = v_tail
+        try:
+            obj = self.__class__(
+                value=b"".join(bytes(chunk) for chunk in chunks),
+                bounds=(self._bound_min, self._bound_max),
+                impl=self.tag,
+                expl=self._expl,
+                default=self.default,
+                optional=self.optional,
+                _decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
+                ctx=ctx,
+            )
+        except DecodeError as err:
+            raise DecodeError(
+                msg=err.msg,
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        except BoundsError as err:
+            raise DecodeError(
+                msg=str(err),
+                klass=self.__class__,
+                decode_path=decode_path,
+                offset=offset,
+            )
+        obj.lenindef = lenindef
+        obj.ber_encoded = True
+        return obj, (v[EOC_LEN:] if lenindef else v)
 
     def __repr__(self):
         return pp_console_row(next(self.pps()))
@@ -3505,10 +3499,10 @@ class ObjectIdentifier(Obj):
         self.defines = defines
 
     def __add__(self, their):
+        if their.__class__ == tuple:
+            return self.__class__(self._value + their)
         if isinstance(their, self.__class__):
             return self.__class__(self._value + their._value)
-        if isinstance(their, tuple):
-            return self.__class__(self._value + their)
         raise InvalidValueType((self.__class__, tuple))
 
     def _value_sanitize(self, value):
@@ -3519,7 +3513,7 @@ class ObjectIdentifier(Obj):
                 value = tuple(pureint(arc) for arc in value.split("."))
             except ValueError:
                 raise InvalidOID("unacceptable arcs values")
-        if isinstance(value, tuple):
+        if value.__class__ == tuple:
             if len(value) < 2:
                 raise InvalidOID("less than 2 arcs")
             first_arc = value[0]
@@ -3587,7 +3581,7 @@ class ObjectIdentifier(Obj):
         )
 
     def __eq__(self, their):
-        if isinstance(their, tuple):
+        if their.__class__ == tuple:
             return self._value == their
         if not issubclass(their.__class__, ObjectIdentifier):
             return False
@@ -3897,9 +3891,9 @@ class CommonString(OctetString):
         value_decoded = None
         if isinstance(value, self.__class__):
             value_raw = value._value
-        elif isinstance(value, text_type):
+        elif value.__class__ == text_type:
             value_decoded = value
-        elif isinstance(value, binary_type):
+        elif value.__class__ == binary_type:
             value_raw = value
         else:
             raise InvalidValueType((self.__class__, text_type, binary_type))
@@ -3923,9 +3917,9 @@ class CommonString(OctetString):
         return value_raw
 
     def __eq__(self, their):
-        if isinstance(their, binary_type):
+        if their.__class__ == binary_type:
             return self._value == their
-        if isinstance(their, text_type):
+        if their.__class__ == text_type:
             return self._value == their.encode(self.encoding)
         if not isinstance(their, self.__class__):
             return False
@@ -4188,6 +4182,9 @@ class UTCTime(VisibleString):
     >>> UTCTime(datetime(2057, 9, 30, 22, 7, 50)).todatetime()
     datetime.datetime(1957, 9, 30, 22, 7, 50)
 
+    If BER encoded value was met, then ``ber_raw`` attribute will hold
+    its raw representation.
+
     .. warning::
 
        Pay attention that UTCTime can not hold full year, so all years
@@ -4201,7 +4198,7 @@ class UTCTime(VisibleString):
        * minutes are not exceeding 60
        * offset value is not exceeding 14 hours
     """
-    __slots__ = ("_ber_raw",)
+    __slots__ = ("ber_raw",)
     tag_default = tag_encode(23)
     encoding = "ascii"
     asn1_type_name = "UTCTime"
@@ -4229,10 +4226,10 @@ class UTCTime(VisibleString):
             None, None, impl, expl, None, optional, _decoded, ctx,
         )
         self._value = value
-        self._ber_raw = None
+        self.ber_raw = None
         if value is not None:
-            self._value, self._ber_raw = self._value_sanitize(value, ctx)
-            self.ber_encoded = self._ber_raw is not None
+            self._value, self.ber_raw = self._value_sanitize(value, ctx)
+            self.ber_encoded = self.ber_raw is not None
         if default is not None:
             default, _ = self._value_sanitize(default)
             self.default = self.__class__(
@@ -4298,7 +4295,7 @@ class UTCTime(VisibleString):
         return value.replace(microsecond=0)
 
     def _value_sanitize(self, value, ctx=None):
-        if isinstance(value, binary_type):
+        if value.__class__ == binary_type:
             try:
                 value_decoded = value.decode("ascii")
             except (UnicodeEncodeError, UnicodeDecodeError) as err:
@@ -4312,7 +4309,7 @@ class UTCTime(VisibleString):
                     try:
                         offset, _value = self._strptime_bered(value_decoded)
                         _value = _value - timedelta(seconds=offset)
-                        return self._dt_sanitize(_value), value_decoded
+                        return self._dt_sanitize(_value), value
                     except (TypeError, ValueError, OverflowError) as _err:
                         err = _err
             raise DecodeError(
@@ -4321,7 +4318,7 @@ class UTCTime(VisibleString):
             )
         if isinstance(value, self.__class__):
             return value._value, None
-        if isinstance(value, datetime):
+        if value.__class__ == datetime:
             return self._dt_sanitize(value), None
         raise InvalidValueType((self.__class__, datetime))
 
@@ -4329,35 +4326,35 @@ class UTCTime(VisibleString):
         if self.ready:
             value = self._value.isoformat()
             if self.ber_encoded:
-                value += " (%s)" % self._ber_raw
+                value += " (%s)" % self.ber_raw
             return value
 
     def __unicode__(self):
         if self.ready:
             value = self._value.isoformat()
             if self.ber_encoded:
-                value += " (%s)" % self._ber_raw
+                value += " (%s)" % self.ber_raw
             return value
         return text_type(self._pp_value())
 
     def __getstate__(self):
         return UTCTimeState(
             *super(UTCTime, self).__getstate__(),
-            **{"ber_raw": self._ber_raw}
+            **{"ber_raw": self.ber_raw}
         )
 
     def __setstate__(self, state):
         super(UTCTime, self).__setstate__(state)
-        self._ber_raw = state.ber_raw
+        self.ber_raw = state.ber_raw
 
     def __bytes__(self):
         self._assert_ready()
         return self._encode_time()
 
     def __eq__(self, their):
-        if isinstance(their, binary_type):
+        if their.__class__ == binary_type:
             return self._encode_time() == their
-        if isinstance(their, datetime):
+        if their.__class__ == datetime:
             return self.todatetime() == their
         if not isinstance(their, self.__class__):
             return False
@@ -4648,7 +4645,7 @@ class Choice(Obj):
         if len(schema) == 0:
             raise ValueError("schema must be specified")
         self.specs = (
-            schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
+            schema if schema.__class__ == OrderedDict else OrderedDict(schema)
         )
         self._value = None
         if value is not None:
@@ -4663,7 +4660,7 @@ class Choice(Obj):
                 self._value = copy(default_obj._value)
 
     def _value_sanitize(self, value):
-        if isinstance(value, tuple) and len(value) == 2:
+        if (value.__class__ == tuple) and len(value) == 2:
             choice, obj = value
             spec = self.specs.get(choice)
             if spec is None:
@@ -4718,7 +4715,7 @@ class Choice(Obj):
         self.ber_encoded = state.ber_encoded
 
     def __eq__(self, their):
-        if isinstance(their, tuple) and len(their) == 2:
+        if (their.__class__ == tuple) and len(their) == 2:
             return self._value == their
         if not isinstance(their, self.__class__):
             return False
@@ -4939,7 +4936,7 @@ class Any(Obj):
         self.defined = None
 
     def _value_sanitize(self, value):
-        if isinstance(value, binary_type):
+        if value.__class__ == binary_type:
             return value
         if isinstance(value, self.__class__):
             return value._value
@@ -4990,7 +4987,7 @@ class Any(Obj):
         self.defined = state.defined
 
     def __eq__(self, their):
-        if isinstance(their, binary_type):
+        if their.__class__ == binary_type:
             return self._value == their
         if issubclass(their.__class__, Any):
             return self._value == their._value
@@ -5293,7 +5290,7 @@ class Sequence(Obj):
         if schema is None:
             schema = getattr(self, "schema", ())
         self.specs = (
-            schema if isinstance(schema, OrderedDict) else OrderedDict(schema)
+            schema if schema.__class__ == OrderedDict else OrderedDict(schema)
         )
         self._value = {}
         if value is not None:
@@ -5819,13 +5816,14 @@ class Set(Sequence):
             tail = v[EOC_LEN:]
             obj.lenindef = True
         obj._value = values
-        if not obj.ready:
-            raise DecodeError(
-                "not all values are ready",
-                klass=self.__class__,
-                decode_path=decode_path,
-                offset=offset,
-            )
+        for name, spec in iteritems(self.specs):
+            if name not in values and not spec.optional:
+                raise DecodeError(
+                    "%s value is not ready" % name,
+                    klass=self.__class__,
+                    decode_path=decode_path,
+                    offset=offset,
+                )
         obj.ber_encoded = ber_encoded
         return obj, tail