* :ref:`allow_default_values <allow_default_values_ctx>`
* :ref:`allow_expl_oob <allow_expl_oob_ctx>`
+* :ref:`allow_unordered_set <allow_unordered_set_ctx>`
* :ref:`bered <bered_ctx>`
* :ref:`defines_by_path <defines_by_path_ctx>`
"""``SET`` structure type
Its usage is identical to :py:class:`pyderasn.Sequence`.
+
+ .. _allow_unordered_set_ctx:
+
+ DER prohibits unordered values encoding and will raise an error
+ during decode. If If :ref:`bered <bered_ctx>` context option is set,
+ then no error will occure. Also you can disable strict values
+ ordering check by setting ``"allow_unordered_set": True``
+ :ref:`context <ctx>` option.
"""
__slots__ = ()
tag_default = tag_encode(form=TagFormConstructed, num=17)
values = {}
bered = False
ctx_allow_default_values = ctx.get("allow_default_values", False)
+ ctx_allow_unordered_set = ctx.get("allow_unordered_set", False)
+ value_prev = memoryview(v[:0])
specs_items = self.specs.items
while len(v) > 0:
if lenindef and v[:EOC_LEN].tobytes() == EOC:
ctx=ctx,
)
value_len = value.fulllen
- sub_offset += value_len
- vlen += value_len
- v = v_tail
+ if value_prev.tobytes() > v[:value_len].tobytes():
+ if ctx_bered or ctx_allow_unordered_set:
+ bered = True
+ else:
+ raise DecodeError(
+ "unordered " + self.asn1_type_name,
+ klass=self.__class__,
+ decode_path=sub_decode_path,
+ offset=sub_offset,
+ )
if spec.default is None or value != spec.default:
pass
elif ctx_bered or ctx_allow_default_values:
offset=sub_offset,
)
values[name] = value
+ value_prev = v[:value_len]
+ sub_offset += value_len
+ vlen += value_len
+ v = v_tail
obj = self.__class__(
schema=self.specs,
impl=self.tag,
optional=self.optional,
_decoded=(offset, llen, vlen + (EOC_LEN if lenindef else 0)),
)
- obj._value = values
- obj.bered = bered
if lenindef:
if v[:EOC_LEN].tobytes() != EOC:
raise DecodeError(
)
tail = v[EOC_LEN:]
obj.lenindef = True
+ obj._value = values
if not obj.ready:
raise DecodeError(
"not all values are ready",
decode_path=decode_path,
offset=offset,
)
+ obj.bered = bered
return obj, tail
b"".join(sorted([seq[name].encode() for name, _ in Seq.schema])),
)
+ @settings(max_examples=LONG_TEST_MAX_EXAMPLES)
+ @given(data_strategy())
+ def test_unsorted(self, d):
+ tags = [
+ tag_encode(tag) for tag in
+ d.draw(sets(integers(min_value=1), min_size=2, max_size=5))
+ ]
+ tags = d.draw(permutations(tags))
+ assume(tags != sorted(tags))
+ encoded = b"".join(OctetString(t, impl=t).encode() for t in tags)
+ seq_encoded = b"".join((
+ Set.tag_default,
+ len_encode(len(encoded)),
+ encoded,
+ ))
+
+ class Seq(Set):
+ schema = [(str(i), OctetString(impl=t)) for i, t in enumerate(tags)]
+ seq = Seq()
+ with assertRaisesRegex(self, DecodeError, "unordered SET"):
+ seq.decode(seq_encoded)
+ for ctx in ({"bered": True}, {"allow_unordered_set": True}):
+ seq_decoded, _ = Seq().decode(seq_encoded, ctx=ctx)
+ self.assertTrue(seq_decoded.bered)
+ self.assertSequenceEqual(
+ [bytes(seq_decoded[str(i)]) for i, t in enumerate(tags)],
+ [t for t in tags],
+ )
+
@composite
def seqof_values_strategy(draw, schema=None, do_expl=False):