summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlya Etingof <etingof@gmail.com>2017-09-08 20:52:36 +0200
committerIlya Etingof <etingof@gmail.com>2017-09-10 14:29:29 +0200
commitd35f412398a54a93ac141d88c41dac411bd05453 (patch)
treec36ccc6dfe8e34909922f9cc6e999378a69956f5
parentc324055b318fa2c01249819c44836214efdfb90e (diff)
downloadpyasn1-git-d35f412398a54a93ac141d88c41dac411bd05453.tar.gz
WIP: hole types implementation
-rw-r--r--pyasn1/codec/ber/decoder.py84
-rw-r--r--pyasn1/type/definedby.py54
-rw-r--r--pyasn1/type/namedtype.py31
-rw-r--r--tests/codec/ber/test_decoder.py57
4 files changed, 167 insertions, 59 deletions
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
index c080b96..f8f0587 100644
--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -514,6 +514,33 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
if namedTypes:
if not namedTypes.requiredComponents.issubset(seenIndices):
raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+
+ for idx, namedType in enumerate(namedTypes.namedTypes):
+ if not namedType.definedBy:
+ continue
+
+ governingValue = asn1Object.getComponentByName(
+ namedType.definedBy.name
+ )
+
+ try:
+ asn1Spec = namedType.definedBy[governingValue]
+
+ except KeyError:
+ continue
+
+ component, rest = decodeFun(
+ asn1Object.getComponentByPosition(idx).asOctets(),
+ asn1Spec=asn1Spec
+ )
+
+ asn1Object.setComponentByPosition(
+ idx, component,
+ matchTags=False,
+ matchConstraints=False
+ )
+
else:
asn1Object.verifySizeSpec()
@@ -529,21 +556,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
)
idx += 1
- if namedTypes:
- for holeName, governingName, typesMap in namedTypes.holes:
- holeComponent = asn1Object[holeName]
- if holeComponent.isValue:
- governingComponent = asn1Object[governingName]
- if governingComponent in typesMap:
- component, rest = decodeFun(
- holeComponent.asOctets(),
- asn1Spec=typesMap[governingComponent]
- )
- asn1Object.setComponentByName(holeName, component, matchTags=False, matchConstraints=False)
-
- else:
- asn1Object.verifySizeSpec()
-
return asn1Object, tail
def indefLenValueDecoder(self, substrate, asn1Spec,
@@ -624,6 +636,33 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
if namedTypes:
if not namedTypes.requiredComponents.issubset(seenIndices):
raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+
+ for idx, namedType in enumerate(namedTypes.namedTypes):
+ if not namedType.definedBy:
+ continue
+
+ governingValue = asn1Object.getComponentByName(
+ namedType.definedBy.name
+ )
+
+ try:
+ asn1Spec = namedType.definedBy[governingValue]
+
+ except KeyError:
+ continue
+
+ component, rest = decodeFun(
+ asn1Object.getComponentByPosition(idx).asOctets(),
+ asn1Spec=asn1Spec, allowEoo=True
+ )
+
+ if component is not eoo.endOfOctets:
+ asn1Object.setComponentByPosition(
+ idx, component,
+ matchTags=False,
+ matchConstraints=False
+ )
+
else:
asn1Object.verifySizeSpec()
@@ -645,21 +684,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
'No EOO seen before substrate ends'
)
- if namedTypes:
- for holeName, governingName, typesMap in namedTypes.holes:
- holeComponent = asn1Object[holeName]
- if holeComponent.isValue:
- governingComponent = asn1Object[governingName]
- if governingComponent in typesMap:
- component, rest = decodeFun(
- holeComponent.asOctets(),
- asn1Spec=typesMap[governingComponent]
- )
- asn1Object.setComponentByName(holeName, component, matchTags=False, matchConstraints=False)
-
- else:
- asn1Object.verifySizeSpec()
-
return asn1Object, substrate
class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder):
diff --git a/pyasn1/type/definedby.py b/pyasn1/type/definedby.py
new file mode 100644
index 0000000..e22d394
--- /dev/null
+++ b/pyasn1/type/definedby.py
@@ -0,0 +1,54 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2017, Ilya Etingof <etingof@gmail.com>
+# License: http://pyasn1.sf.net/license.html
+#
+
+__all__ = ['DefinedBy']
+
+
+class DefinedBy(object):
+ """Create ASN.1 type map indexed by a value
+
+ The *DefinedBy* object models the ASN.1 *DEFINED BY* clause which maps
+ values to ASN.1 types in the context of the ASN.1 SEQUENCE/SET type.
+
+ DefinedBy objects are immutable and duck-type Python :class:`dict` objects
+
+ Parameters
+ ----------
+ name: :py:class:`str`
+ Field name
+
+ *choices:
+ Sequence of (*value*, *type*) tuples representing the mapping
+ """
+
+ def __init__(self, name, args, **kwargs):
+ self.__name = name
+ self.__choices = dict(args, **kwargs)
+
+ @property
+ def name(self):
+ return self.__name
+
+ # Python dict protocol
+
+ def values(self):
+ return self.__choices.values()
+
+ def keys(self):
+ return self.__choices
+
+ def items(self):
+ return self.__choices.items()
+
+ def __contains__(self, key):
+ return key in self.__choices
+
+ def __getitem__(self, key):
+ return self.__choices[key]
+
+ def __iter__(self):
+ return iter(self.__choices)
diff --git a/pyasn1/type/namedtype.py b/pyasn1/type/namedtype.py
index 6bf82f4..69b4422 100644
--- a/pyasn1/type/namedtype.py
+++ b/pyasn1/type/namedtype.py
@@ -30,12 +30,11 @@ class NamedType(object):
isOptional = False
isDefaulted = False
- def __init__(self, name, asn1Object):
+ def __init__(self, name, asn1Object, definedBy=None):
self.__name = name
self.__type = asn1Object
self.__nameAndType = name, asn1Object
- self.__governingName = None
- self.__typesMap = None
+ self.__definedBy = definedBy
def __repr__(self):
return '%s(%r, %r)' % (self.__class__.__name__, self.__name, self.__type)
@@ -75,18 +74,9 @@ class NamedType(object):
def asn1Object(self):
return self.__type
- def definedBy(self, governingName, typesMap):
- self.__governingName = governingName
- self.__typesMap = typesMap
- return self
-
- @property
- def governingName(self):
- return self.__governingName
-
@property
- def typesMap(self):
- return self.__typesMap
+ def definedBy(self):
+ return self.__definedBy
# Backward compatibility
@@ -138,7 +128,6 @@ class NamedTypes(object):
self.__keys = frozenset([namedType.name for namedType in self.__namedTypes])
self.__values = tuple([namedType.asn1Object for namedType in self.__namedTypes])
self.__items = tuple([(namedType.name, namedType.asn1Object) for namedType in self.__namedTypes])
- self.__holes = self.__computeTypeHoles()
def __repr__(self):
return '%s(%s)' % (
@@ -525,15 +514,3 @@ class NamedTypes(object):
@property
def requiredComponents(self):
return self.__requiredComponents
-
- @property
- def holes(self):
- return self.__holes
-
- def __computeTypeHoles(self):
- holes = []
- for namedType in self.__namedTypes:
- if namedType.governingName:
- holes.append((namedType.name, namedType.governingName, namedType.typesMap))
-
- return holes
diff --git a/tests/codec/ber/test_decoder.py b/tests/codec/ber/test_decoder.py
index d2f02ad..45cd530 100644
--- a/tests/codec/ber/test_decoder.py
+++ b/tests/codec/ber/test_decoder.py
@@ -10,7 +10,7 @@ try:
except ImportError:
import unittest
-from pyasn1.type import tag, namedtype, univ, char
+from pyasn1.type import tag, namedtype, univ, char, definedby
from pyasn1.codec.ber import decoder, eoo
from pyasn1.compat.octets import ints2octs, str2octs, null
from pyasn1.error import PyAsn1Error
@@ -720,7 +720,7 @@ class SequenceDecoderWithSchemaTestCase(unittest.TestCase):
def testDefMode(self):
self.__init()
assert decoder.decode(
- ints2octs((48, 128, 5, 0, 0, 0)), asn1Spec=self.s
+ ints2octs((48, 2, 5, 0)), asn1Spec=self.s
) == (self.s, null)
def testIndefMode(self):
@@ -823,6 +823,59 @@ class SequenceDecoderWithSchemaTestCase(unittest.TestCase):
) == (self.s, null)
+class SequenceDecoderWithIntegerHoleTypesTestCase(unittest.TestCase):
+ def setUp(self):
+ definedBy = definedby.DefinedBy(
+ 'id',
+ [(1, univ.Integer()),
+ (2, univ.OctetString())]
+ )
+ self.s = univ.Sequence(
+ componentType=namedtype.NamedTypes(
+ namedtype.NamedType('id', univ.Integer()),
+ namedtype.NamedType('blob', univ.Any(), definedBy=definedBy)
+ )
+ )
+
+ # def testChoiceOne(self):
+ # s, r = decoder.decode(
+ # ints2octs((48, 6, 2, 1, 1, 2, 1, 12)),
+ # asn1Spec=self.s
+ # )
+ # assert not r
+ # assert s[0] == 1
+ # assert s[1] == ints2octs((2, 1, 12))
+ #
+ # def testChoiceTwo(self):
+ # s, r = decoder.decode(
+ # ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)),
+ # asn1Spec=self.s
+ # )
+ # assert not r
+ # assert s[0] == 1
+ # assert s[1] == ints2octs((4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110))
+
+ def testChoiceOneResolveHoles(self):
+ s, r = decoder.decode(
+ ints2octs((48, 6, 2, 1, 1, 2, 1, 12)), asn1Spec=self.s
+# TODO
+# resolveHoleTypes=True
+ )
+ assert not r
+ assert s[0] == 1
+ assert s[1] == 12
+
+ def testChoiceTwoResolveHoles(self):
+ s, r = decoder.decode(
+ ints2octs((48, 16, 2, 1, 2, 4, 11, 113, 117, 105, 99, 107, 32, 98, 114, 111, 119, 110)), asn1Spec=self.s
+# TODO
+# resolveHoleTypes = True
+ )
+ assert not r
+ assert s[0] == 2
+ assert s[1] == univ.OctetString('quick brown')
+
+
class SetDecoderTestCase(unittest.TestCase):
def setUp(self):
self.s = univ.Set(