summaryrefslogtreecommitdiff
path: root/pyasn1/codec
diff options
context:
space:
mode:
Diffstat (limited to 'pyasn1/codec')
-rw-r--r--pyasn1/codec/ber/decoder.py1188
-rw-r--r--pyasn1/codec/ber/encoder.py54
-rw-r--r--pyasn1/codec/cer/decoder.py72
-rw-r--r--pyasn1/codec/cer/encoder.py24
-rw-r--r--pyasn1/codec/der/decoder.py48
-rw-r--r--pyasn1/codec/der/encoder.py25
-rw-r--r--pyasn1/codec/native/decoder.py164
-rw-r--r--pyasn1/codec/native/encoder.py48
-rw-r--r--pyasn1/codec/streaming.py243
9 files changed, 1309 insertions, 557 deletions
diff --git a/pyasn1/codec/ber/decoder.py b/pyasn1/codec/ber/decoder.py
index 5ff485f..0755adc 100644
--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -4,11 +4,18 @@
# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
# License: http://snmplabs.com/pyasn1/license.html
#
+import os
+
from pyasn1 import debug
from pyasn1 import error
from pyasn1.codec.ber import eoo
+from pyasn1.codec.streaming import asSeekableStream
+from pyasn1.codec.streaming import isEndOfStream
+from pyasn1.codec.streaming import peekIntoStream
+from pyasn1.codec.streaming import readFromStream
from pyasn1.compat.integer import from_bytes
from pyasn1.compat.octets import oct2int, octs2ints, ints2octs, null
+from pyasn1.error import PyAsn1Error
from pyasn1.type import base
from pyasn1.type import char
from pyasn1.type import tag
@@ -16,33 +23,51 @@ from pyasn1.type import tagmap
from pyasn1.type import univ
from pyasn1.type import useful
-__all__ = ['decode']
+__all__ = ['StreamingDecoder', 'Decoder', 'decode']
LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
noValue = base.noValue
+SubstrateUnderrunError = error.SubstrateUnderrunError
+
-class AbstractDecoder(object):
+class AbstractPayloadDecoder(object):
protoComponent = None
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- raise error.PyAsn1Error('Decoder not implemented for %s' % (tagSet,))
+ """Decode value with fixed byte length.
+
+ The decoder is allowed to consume as many bytes as necessary.
+ """
+ raise error.PyAsn1Error('SingleItemDecoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError?
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,))
+ """Decode value with undefined length.
+
+ The decoder is allowed to consume as many bytes as necessary.
+ """
+ raise error.PyAsn1Error('Indefinite length mode decoder not implemented for %s' % (tagSet,)) # TODO: Seems more like an NotImplementedError?
+
+ @staticmethod
+ def _passAsn1Object(asn1Object, options):
+ if 'asn1Object' not in options:
+ options['asn1Object'] = asn1Object
+
+ return options
-class AbstractSimpleDecoder(AbstractDecoder):
+class AbstractSimplePayloadDecoder(AbstractPayloadDecoder):
@staticmethod
- def substrateCollector(asn1Object, substrate, length):
- return substrate[:length], substrate[length:]
+ def substrateCollector(asn1Object, substrate, length, options):
+ for chunk in readFromStream(substrate, length, options):
+ yield chunk
def _createComponent(self, asn1Spec, tagSet, value, **options):
if options.get('native'):
@@ -55,7 +80,7 @@ class AbstractSimpleDecoder(AbstractDecoder):
return asn1Spec.clone(value)
-class ExplicitTagDecoder(AbstractSimpleDecoder):
+class RawPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.Any('')
def valueDecoder(self, substrate, asn1Spec,
@@ -63,45 +88,43 @@ class ExplicitTagDecoder(AbstractSimpleDecoder):
decodeFun=None, substrateFun=None,
**options):
if substrateFun:
- return substrateFun(
- self._createComponent(asn1Spec, tagSet, '', **options),
- substrate, length
- )
-
- head, tail = substrate[:length], substrate[length:]
+ asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
- value, _ = decodeFun(head, asn1Spec, tagSet, length, **options)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
- if LOG:
- LOG('explicit tag container carries %d octets of trailing payload '
- '(will be lost!): %s' % (len(_), debug.hexdump(_)))
+ return
- return value, tail
+ for value in decodeFun(substrate, asn1Spec, tagSet, length, **options):
+ yield value
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
if substrateFun:
- return substrateFun(
- self._createComponent(asn1Spec, tagSet, '', **options),
- substrate, length
- )
+ asn1Object = self._createComponent(asn1Spec, tagSet, '', **options)
- value, substrate = decodeFun(substrate, asn1Spec, tagSet, length, **options)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
- eooMarker, substrate = decodeFun(substrate, allowEoo=True, **options)
+ return
- if eooMarker is eoo.endOfOctets:
- return value, substrate
- else:
- raise error.PyAsn1Error('Missing end-of-octets terminator')
+ while True:
+ for value in decodeFun(
+ substrate, asn1Spec, tagSet, length,
+ allowEoo=True, **options):
+
+ if value is eoo.endOfOctets:
+ return
+
+ yield value
-explicitTagDecoder = ExplicitTagDecoder()
+rawPayloadDecoder = RawPayloadDecoder()
-class IntegerDecoder(AbstractSimpleDecoder):
+class IntegerPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.Integer(0)
def valueDecoder(self, substrate, asn1Spec,
@@ -112,25 +135,28 @@ class IntegerDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
- if not head:
- return self._createComponent(asn1Spec, tagSet, 0, **options), tail
+ if chunk:
+ value = from_bytes(chunk, signed=True)
- value = from_bytes(head, signed=True)
+ else:
+ value = 0
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ yield self._createComponent(asn1Spec, tagSet, value, **options)
-class BooleanDecoder(IntegerDecoder):
+class BooleanPayloadDecoder(IntegerPayloadDecoder):
protoComponent = univ.Boolean(0)
def _createComponent(self, asn1Spec, tagSet, value, **options):
- return IntegerDecoder._createComponent(
+ return IntegerPayloadDecoder._createComponent(
self, asn1Spec, tagSet, value and 1 or 0, **options)
-class BitStringDecoder(AbstractSimpleDecoder):
+class BitStringPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.BitString(())
supportConstructedForm = True
@@ -138,27 +164,47 @@ class BitStringDecoder(AbstractSimpleDecoder):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
if substrateFun:
- return substrateFun(self._createComponent(
- asn1Spec, tagSet, noValue, **options), substrate, length)
+ asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
+
+ if not length:
+ raise error.PyAsn1Error('Empty BIT STRING substrate')
+
+ for chunk in isEndOfStream(substrate):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
- if not head:
+ if chunk:
raise error.PyAsn1Error('Empty BIT STRING substrate')
if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- trailingBits = oct2int(head[0])
+ for trailingBits in readFromStream(substrate, 1, options):
+ if isinstance(trailingBits, SubstrateUnderrunError):
+ yield trailingBits
+
+ trailingBits = ord(trailingBits)
if trailingBits > 7:
raise error.PyAsn1Error(
'Trailing bits overflow %s' % trailingBits
)
+ for chunk in readFromStream(substrate, length - 1, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+
value = self.protoComponent.fromOctetString(
- head[1:], internalFormat=True, padding=trailingBits)
+ chunk, internalFormat=True, padding=trailingBits)
+
+ yield self._createComponent(asn1Spec, tagSet, value, **options)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ return
if not self.supportConstructedForm:
raise error.PyAsn1Error('Constructed encoding form prohibited '
@@ -172,9 +218,14 @@ class BitStringDecoder(AbstractSimpleDecoder):
bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
- while head:
- component, head = decodeFun(head, self.protoComponent,
- substrateFun=substrateFun, **options)
+ current_position = substrate.tell()
+
+ while substrate.tell() - current_position < length:
+ for component in decodeFun(
+ substrate, self.protoComponent, substrateFun=substrateFun,
+ **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
trailingBits = oct2int(component[0])
if trailingBits > 7:
@@ -187,7 +238,7 @@ class BitStringDecoder(AbstractSimpleDecoder):
prepend=bitString, padding=trailingBits
)
- return self._createComponent(asn1Spec, tagSet, bitString, **options), tail
+ yield self._createComponent(asn1Spec, tagSet, bitString, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -195,17 +246,30 @@ class BitStringDecoder(AbstractSimpleDecoder):
**options):
if substrateFun:
- return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options), substrate, length)
+ asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
# All inner fragments are of the same type, treat them as octet string
substrateFun = self.substrateCollector
bitString = self.protoComponent.fromOctetString(null, internalFormat=True)
- while substrate:
- component, substrate = decodeFun(substrate, self.protoComponent,
- substrateFun=substrateFun,
- allowEoo=True, **options)
+ while True: # loop over fragments
+
+ for component in decodeFun(
+ substrate, self.protoComponent, substrateFun=substrateFun,
+ allowEoo=True, **options):
+
+ if component is eoo.endOfOctets:
+ break
+
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
if component is eoo.endOfOctets:
break
@@ -220,13 +284,10 @@ class BitStringDecoder(AbstractSimpleDecoder):
prepend=bitString, padding=trailingBits
)
- else:
- raise error.SubstrateUnderrunError('No EOO seen before substrate ends')
-
- return self._createComponent(asn1Spec, tagSet, bitString, **options), substrate
+ yield self._createComponent(asn1Spec, tagSet, bitString, **options)
-class OctetStringDecoder(AbstractSimpleDecoder):
+class OctetStringPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.OctetString('')
supportConstructedForm = True
@@ -234,14 +295,22 @@ class OctetStringDecoder(AbstractSimpleDecoder):
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
-
if substrateFun:
- return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
- substrate, length)
+ asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
+
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
if tagSet[0].tagFormat == tag.tagFormatSimple: # XXX what tag to check?
- return self._createComponent(asn1Spec, tagSet, head, **options), tail
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+
+ yield self._createComponent(asn1Spec, tagSet, chunk, **options)
+
+ return
if not self.supportConstructedForm:
raise error.PyAsn1Error('Constructed encoding form prohibited at %s' % self.__class__.__name__)
@@ -254,13 +323,18 @@ class OctetStringDecoder(AbstractSimpleDecoder):
header = null
- while head:
- component, head = decodeFun(head, self.protoComponent,
- substrateFun=substrateFun,
- **options)
+ original_position = substrate.tell()
+ # head = popSubstream(substrate, length)
+ while substrate.tell() - original_position < length:
+ for component in decodeFun(
+ substrate, self.protoComponent, substrateFun=substrateFun,
+ **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
header += component
- return self._createComponent(asn1Spec, tagSet, header, **options), tail
+ yield self._createComponent(asn1Spec, tagSet, header, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -268,32 +342,38 @@ class OctetStringDecoder(AbstractSimpleDecoder):
**options):
if substrateFun and substrateFun is not self.substrateCollector:
asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- return substrateFun(asn1Object, substrate, length)
+
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
# All inner fragments are of the same type, treat them as octet string
substrateFun = self.substrateCollector
header = null
- while substrate:
- component, substrate = decodeFun(substrate,
- self.protoComponent,
- substrateFun=substrateFun,
- allowEoo=True, **options)
+ while True: # loop over fragments
+
+ for component in decodeFun(
+ substrate, self.protoComponent, substrateFun=substrateFun,
+ allowEoo=True, **options):
+
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
+
if component is eoo.endOfOctets:
break
header += component
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
+ yield self._createComponent(asn1Spec, tagSet, header, **options)
- return self._createComponent(asn1Spec, tagSet, header, **options), substrate
-
-class NullDecoder(AbstractSimpleDecoder):
+class NullPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.Null('')
def valueDecoder(self, substrate, asn1Spec,
@@ -304,17 +384,19 @@ class NullDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
component = self._createComponent(asn1Spec, tagSet, '', **options)
- if head:
+ if chunk:
raise error.PyAsn1Error('Unexpected %d-octet substrate for Null' % length)
- return component, tail
+ yield component
-class ObjectIdentifierDecoder(AbstractSimpleDecoder):
+class ObjectIdentifierPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.ObjectIdentifier(())
def valueDecoder(self, substrate, asn1Spec,
@@ -324,17 +406,20 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
- if not head:
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+
+ if not chunk:
raise error.PyAsn1Error('Empty substrate')
- head = octs2ints(head)
+ chunk = octs2ints(chunk)
oid = ()
index = 0
- substrateLen = len(head)
+ substrateLen = len(chunk)
while index < substrateLen:
- subId = head[index]
+ subId = chunk[index]
index += 1
if subId < 128:
oid += (subId,)
@@ -348,7 +433,7 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder):
raise error.SubstrateUnderrunError(
'Short substrate for sub-OID past %s' % (oid,)
)
- nextSubId = head[index]
+ nextSubId = chunk[index]
index += 1
oid += ((subId << 7) + nextSubId,)
elif subId == 128:
@@ -366,12 +451,12 @@ class ObjectIdentifierDecoder(AbstractSimpleDecoder):
elif oid[0] >= 80:
oid = (2, oid[0] - 80) + oid[1:]
else:
- raise error.PyAsn1Error('Malformed first OID octet: %s' % head[0])
+ raise error.PyAsn1Error('Malformed first OID octet: %s' % chunk[0])
- return self._createComponent(asn1Spec, tagSet, oid, **options), tail
+ yield self._createComponent(asn1Spec, tagSet, oid, **options)
-class RealDecoder(AbstractSimpleDecoder):
+class RealPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.Real()
def valueDecoder(self, substrate, asn1Spec,
@@ -381,15 +466,18 @@ class RealDecoder(AbstractSimpleDecoder):
if tagSet[0].tagFormat != tag.tagFormatSimple:
raise error.PyAsn1Error('Simple tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
- if not head:
- return self._createComponent(asn1Spec, tagSet, 0.0, **options), tail
+ if not chunk:
+ yield self._createComponent(asn1Spec, tagSet, 0.0, **options)
+ return
- fo = oct2int(head[0])
- head = head[1:]
+ fo = oct2int(chunk[0])
+ chunk = chunk[1:]
if fo & 0x80: # binary encoding
- if not head:
+ if not chunk:
raise error.PyAsn1Error("Incomplete floating-point value")
if LOG:
@@ -398,12 +486,12 @@ class RealDecoder(AbstractSimpleDecoder):
n = (fo & 0x03) + 1
if n == 4:
- n = oct2int(head[0])
- head = head[1:]
+ n = oct2int(chunk[0])
+ chunk = chunk[1:]
- eo, head = head[:n], head[n:]
+ eo, chunk = chunk[:n], chunk[n:]
- if not eo or not head:
+ if not eo or not chunk:
raise error.PyAsn1Error('Real exponent screwed')
e = oct2int(eo[0]) & 0x80 and -1 or 0
@@ -425,10 +513,10 @@ class RealDecoder(AbstractSimpleDecoder):
e *= 4
p = 0
- while head: # value
+ while chunk: # value
p <<= 8
- p |= oct2int(head[0])
- head = head[1:]
+ p |= oct2int(chunk[0])
+ chunk = chunk[1:]
if fo & 0x40: # sign bit
p = -p
@@ -444,7 +532,7 @@ class RealDecoder(AbstractSimpleDecoder):
value = fo & 0x01 and '-inf' or 'inf'
elif fo & 0xc0 == 0: # character encoding
- if not head:
+ if not chunk:
raise error.PyAsn1Error("Incomplete floating-point value")
if LOG:
@@ -452,13 +540,13 @@ class RealDecoder(AbstractSimpleDecoder):
try:
if fo & 0x3 == 0x1: # NR1
- value = (int(head), 10, 0)
+ value = (int(chunk), 10, 0)
elif fo & 0x3 == 0x2: # NR2
- value = float(head)
+ value = float(chunk)
elif fo & 0x3 == 0x3: # NR3
- value = float(head)
+ value = float(chunk)
else:
raise error.SubstrateUnderrunError(
@@ -475,14 +563,14 @@ class RealDecoder(AbstractSimpleDecoder):
'Unknown encoding (tag %s)' % fo
)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+ yield self._createComponent(asn1Spec, tagSet, value, **options)
-class AbstractConstructedDecoder(AbstractDecoder):
+class AbstractConstructedPayloadDecoder(AbstractPayloadDecoder):
protoComponent = None
-class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
+class ConstructedPayloadDecoderBase(AbstractConstructedPayloadDecoder):
protoRecordComponent = None
protoSequenceComponent = None
@@ -492,33 +580,43 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
def _getComponentPositionByType(self, asn1Object, tagSet, idx):
raise NotImplementedError()
- def _decodeComponents(self, substrate, tagSet=None, decodeFun=None, **options):
+ def _decodeComponentsSchemaless(
+ self, substrate, tagSet=None, decodeFun=None,
+ length=None, **options):
+
+ asn1Object = None
+
components = []
componentTypes = set()
- while substrate:
- component, substrate = decodeFun(substrate, **options)
- if component is eoo.endOfOctets:
+ original_position = substrate.tell()
+
+ while length == -1 or substrate.tell() < original_position + length:
+ for component in decodeFun(substrate, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if length == -1 and component is eoo.endOfOctets:
break
components.append(component)
componentTypes.add(component.tagSet)
- # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
- # The heuristics is:
- # * 1+ components of different types -> likely SEQUENCE/SET
- # * otherwise -> likely SEQUENCE OF/SET OF
- if len(componentTypes) > 1:
- protoComponent = self.protoRecordComponent
+ # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
+ # The heuristics is:
+ # * 1+ components of different types -> likely SEQUENCE/SET
+ # * otherwise -> likely SEQUENCE OF/SET OF
+ if len(componentTypes) > 1:
+ protoComponent = self.protoRecordComponent
- else:
- protoComponent = self.protoSequenceComponent
+ else:
+ protoComponent = self.protoSequenceComponent
- asn1Object = protoComponent.clone(
- # construct tagSet from base tag from prototype ASN.1 object
- # and additional tags recovered from the substrate
- tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
- )
+ asn1Object = protoComponent.clone(
+ # construct tagSet from base tag from prototype ASN.1 object
+ # and additional tags recovered from the substrate
+ tagSet=tag.TagSet(protoComponent.tagSet.baseTag, *tagSet.superTags)
+ )
if LOG:
LOG('guessed %r container type (pass `asn1Spec` to guide the '
@@ -531,7 +629,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
matchTags=False, matchConstraints=False
)
- return asn1Object, substrate
+ yield asn1Object
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -540,9 +638,9 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
if tagSet[0].tagFormat != tag.tagFormatConstructed:
raise error.PyAsn1Error('Constructed tag format expected')
- head, tail = substrate[:length], substrate[length:]
+ original_position = substrate.tell()
- if substrateFun is not None:
+ if substrateFun:
if asn1Spec is not None:
asn1Object = asn1Spec.clone()
@@ -552,23 +650,36 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
else:
asn1Object = self.protoRecordComponent, self.protoSequenceComponent
- return substrateFun(asn1Object, substrate, length)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
if asn1Spec is None:
- asn1Object, trailing = self._decodeComponents(
- head, tagSet=tagSet, decodeFun=decodeFun, **options
- )
+ for asn1Object in self._decodeComponentsSchemaless(
+ substrate, tagSet=tagSet, decodeFun=decodeFun,
+ length=length, **options):
+ if isinstance(asn1Object, SubstrateUnderrunError):
+ yield asn1Object
- if trailing:
+ if substrate.tell() < original_position + length:
if LOG:
+ for trailing in readFromStream(substrate, context=options):
+ if isinstance(trailing, SubstrateUnderrunError):
+ yield trailing
+
LOG('Unused trailing %d octets encountered: %s' % (
len(trailing), debug.hexdump(trailing)))
- return asn1Object, tail
+ yield asn1Object
+
+ return
asn1Object = asn1Spec.clone()
asn1Object.clear()
+ options = self._passAsn1Object(asn1Object, options)
+
if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
namedTypes = asn1Spec.componentType
@@ -583,7 +694,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
seenIndices = set()
idx = 0
- while head:
+ while substrate.tell() - original_position < length:
if not namedTypes:
componentType = None
@@ -606,7 +717,9 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
'Excessive components decoded at %r' % (asn1Spec,)
)
- component, head = decodeFun(head, componentType, **options)
+ for component in decodeFun(substrate, componentType, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
if not isDeterministic and namedTypes:
if isSetType:
@@ -693,18 +806,20 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
for pos, containerElement in enumerate(
containerValue):
- component, rest = decodeFun(
- containerValue[pos].asOctets(),
- asn1Spec=openType, **options
- )
+ stream = asSeekableStream(containerValue[pos].asOctets())
+
+ for component in decodeFun(stream, asn1Spec=openType, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
containerValue[pos] = component
else:
- component, rest = decodeFun(
- asn1Object.getComponentByPosition(idx).asOctets(),
- asn1Spec=openType, **options
- )
+ stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
+
+ for component in decodeFun(stream, asn1Spec=openType, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
asn1Object.setComponentByPosition(idx, component)
@@ -714,9 +829,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
raise inconsistency
else:
- asn1Object = asn1Spec.clone()
- asn1Object.clear()
-
componentType = asn1Spec.componentType
if LOG:
@@ -724,8 +836,11 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx = 0
- while head:
- component, head = decodeFun(head, componentType, **options)
+ while substrate.tell() - original_position < length:
+ for component in decodeFun(substrate, componentType, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
asn1Object.setComponentByPosition(
idx, component,
verifyConstraints=False,
@@ -734,7 +849,7 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx += 1
- return asn1Object, tail
+ yield asn1Object
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -753,17 +868,27 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
else:
asn1Object = self.protoRecordComponent, self.protoSequenceComponent
- return substrateFun(asn1Object, substrate, length)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
if asn1Spec is None:
- return self._decodeComponents(
- substrate, tagSet=tagSet, decodeFun=decodeFun,
- **dict(options, allowEoo=True)
- )
+ for asn1Object in self._decodeComponentsSchemaless(
+ substrate, tagSet=tagSet, decodeFun=decodeFun,
+ length=length, **dict(options, allowEoo=True)):
+ if isinstance(asn1Object, SubstrateUnderrunError):
+ yield asn1Object
+
+ yield asn1Object
+
+ return
asn1Object = asn1Spec.clone()
asn1Object.clear()
+ options = self._passAsn1Object(asn1Object, options)
+
if asn1Spec.typeId in (univ.Sequence.typeId, univ.Set.typeId):
namedTypes = asn1Object.componentType
@@ -777,8 +902,10 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
asn1Spec))
seenIndices = set()
+
idx = 0
- while substrate:
+
+ while True: # loop over components
if len(namedTypes) <= idx:
asn1Spec = None
@@ -801,13 +928,21 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
'Excessive components decoded at %r' % (asn1Object,)
)
- component, substrate = decodeFun(substrate, asn1Spec, allowEoo=True, **options)
+ for component in decodeFun(substrate, asn1Spec, allowEoo=True, **options):
+
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
+
if component is eoo.endOfOctets:
break
if not isDeterministic and namedTypes:
if isSetType:
idx = namedTypes.getPositionByType(component.effectiveTagSet)
+
elif namedTypes[idx].isOptional or namedTypes[idx].isDefaulted:
idx = namedTypes.getPositionNearType(component.effectiveTagSet, idx)
@@ -820,17 +955,14 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
seenIndices.add(idx)
idx += 1
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
-
if LOG:
LOG('seen component indices %s' % seenIndices)
if namedTypes:
if not namedTypes.requiredComponents.issubset(seenIndices):
- raise error.PyAsn1Error('ASN.1 object %s has uninitialized components' % asn1Object.__class__.__name__)
+ raise error.PyAsn1Error(
+ 'ASN.1 object %s has uninitialized '
+ 'components' % asn1Object.__class__.__name__)
if namedTypes.hasOpenTypes:
@@ -892,20 +1024,28 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
for pos, containerElement in enumerate(
containerValue):
- component, rest = decodeFun(
- containerValue[pos].asOctets(),
- asn1Spec=openType, **dict(options, allowEoo=True)
- )
+ stream = asSeekableStream(containerValue[pos].asOctets())
+
+ for component in decodeFun(stream, asn1Spec=openType,
+ **dict(options, allowEoo=True)):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
containerValue[pos] = component
else:
- component, rest = decodeFun(
- asn1Object.getComponentByPosition(idx).asOctets(),
- asn1Spec=openType, **dict(options, allowEoo=True)
- )
+ stream = asSeekableStream(asn1Object.getComponentByPosition(idx).asOctets())
+ for component in decodeFun(stream, asn1Spec=openType,
+ **dict(options, allowEoo=True)):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
- if component is not eoo.endOfOctets:
asn1Object.setComponentByPosition(idx, component)
else:
@@ -914,9 +1054,6 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
raise inconsistency
else:
- asn1Object = asn1Spec.clone()
- asn1Object.clear()
-
componentType = asn1Spec.componentType
if LOG:
@@ -924,8 +1061,16 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx = 0
- while substrate:
- component, substrate = decodeFun(substrate, componentType, allowEoo=True, **options)
+ while True:
+
+ for component in decodeFun(
+ substrate, componentType, allowEoo=True, **options):
+
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
if component is eoo.endOfOctets:
break
@@ -938,50 +1083,42 @@ class UniversalConstructedTypeDecoder(AbstractConstructedDecoder):
idx += 1
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
-
- return asn1Object, substrate
+ yield asn1Object
-class SequenceOrSequenceOfDecoder(UniversalConstructedTypeDecoder):
+class SequenceOrSequenceOfPayloadDecoder(ConstructedPayloadDecoderBase):
protoRecordComponent = univ.Sequence()
protoSequenceComponent = univ.SequenceOf()
-class SequenceDecoder(SequenceOrSequenceOfDecoder):
+class SequencePayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
protoComponent = univ.Sequence()
-class SequenceOfDecoder(SequenceOrSequenceOfDecoder):
+class SequenceOfPayloadDecoder(SequenceOrSequenceOfPayloadDecoder):
protoComponent = univ.SequenceOf()
-class SetOrSetOfDecoder(UniversalConstructedTypeDecoder):
+class SetOrSetOfPayloadDecoder(ConstructedPayloadDecoderBase):
protoRecordComponent = univ.Set()
protoSequenceComponent = univ.SetOf()
-class SetDecoder(SetOrSetOfDecoder):
+class SetPayloadDecoder(SetOrSetOfPayloadDecoder):
protoComponent = univ.Set()
-
-class SetOfDecoder(SetOrSetOfDecoder):
+class SetOfPayloadDecoder(SetOrSetOfPayloadDecoder):
protoComponent = univ.SetOf()
-class ChoiceDecoder(AbstractConstructedDecoder):
+class ChoicePayloadDecoder(ConstructedPayloadDecoderBase):
protoComponent = univ.Choice()
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
-
if asn1Spec is None:
asn1Object = self.protoComponent.clone(tagSet=tagSet)
@@ -989,24 +1126,31 @@ class ChoiceDecoder(AbstractConstructedDecoder):
asn1Object = asn1Spec.clone()
if substrateFun:
- return substrateFun(asn1Object, substrate, length)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
+
+ return
+
+ options = self._passAsn1Object(asn1Object, options)
if asn1Object.tagSet == tagSet:
if LOG:
LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
- component, head = decodeFun(
- head, asn1Object.componentTagMap, **options
- )
+ for component in decodeFun(
+ substrate, asn1Object.componentTagMap, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
else:
if LOG:
LOG('decoding %s as untagged CHOICE' % (tagSet,))
- component, head = decodeFun(
- head, asn1Object.componentTagMap,
- tagSet, length, state, **options
- )
+ for component in decodeFun(
+ substrate, asn1Object.componentTagMap, tagSet, length,
+ state, **options):
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
effectiveTagSet = component.effectiveTagSet
@@ -1020,7 +1164,7 @@ class ChoiceDecoder(AbstractConstructedDecoder):
innerFlag=False
)
- return asn1Object, tail
+ yield asn1Object
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -1028,53 +1172,67 @@ class ChoiceDecoder(AbstractConstructedDecoder):
**options):
if asn1Spec is None:
asn1Object = self.protoComponent.clone(tagSet=tagSet)
+
else:
asn1Object = asn1Spec.clone()
if substrateFun:
- return substrateFun(asn1Object, substrate, length)
+ for chunk in substrateFun(asn1Object, substrate, length, options):
+ yield chunk
- if asn1Object.tagSet == tagSet:
- if LOG:
- LOG('decoding %s as explicitly tagged CHOICE' % (tagSet,))
+ return
- component, substrate = decodeFun(
- substrate, asn1Object.componentType.tagMapUnique, **options
- )
+ options = self._passAsn1Object(asn1Object, options)
- # eat up EOO marker
- eooMarker, substrate = decodeFun(
- substrate, allowEoo=True, **options
- )
+ isTagged = asn1Object.tagSet == tagSet
- if eooMarker is not eoo.endOfOctets:
- raise error.PyAsn1Error('No EOO seen before substrate ends')
+ if LOG:
+ LOG('decoding %s as %stagged CHOICE' % (
+ tagSet, isTagged and 'explicitly ' or 'un'))
- else:
- if LOG:
- LOG('decoding %s as untagged CHOICE' % (tagSet,))
+ while True:
- component, substrate = decodeFun(
- substrate, asn1Object.componentType.tagMapUnique,
- tagSet, length, state, **options
- )
+ if isTagged:
+ iterator = decodeFun(
+ substrate, asn1Object.componentType.tagMapUnique,
+ **dict(options, allowEoo=True))
- effectiveTagSet = component.effectiveTagSet
+ else:
+ iterator = decodeFun(
+ substrate, asn1Object.componentType.tagMapUnique,
+ tagSet, length, state, **dict(options, allowEoo=True))
- if LOG:
- LOG('decoded component %s, effective tag set %s' % (component, effectiveTagSet))
+ for component in iterator:
- asn1Object.setComponentByType(
- effectiveTagSet, component,
- verifyConstraints=False,
- matchTags=False, matchConstraints=False,
- innerFlag=False
- )
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
+
+ effectiveTagSet = component.effectiveTagSet
- return asn1Object, substrate
+ if LOG:
+ LOG('decoded component %s, effective tag set '
+ '%s' % (component, effectiveTagSet))
+
+ asn1Object.setComponentByType(
+ effectiveTagSet, component,
+ verifyConstraints=False,
+ matchTags=False, matchConstraints=False,
+ innerFlag=False
+ )
+ if not isTagged:
+ break
+
+ if not isTagged or component is eoo.endOfOctets:
+ break
-class AnyDecoder(AbstractSimpleDecoder):
+ yield asn1Object
+
+
+class AnyPayloadDecoder(AbstractSimplePayloadDecoder):
protoComponent = univ.Any()
def valueDecoder(self, substrate, asn1Spec,
@@ -1091,22 +1249,32 @@ class AnyDecoder(AbstractSimpleDecoder):
isUntagged = tagSet != asn1Spec.tagSet
if isUntagged:
- fullSubstrate = options['fullSubstrate']
+ fullPosition = substrate.markedPosition
+ currentPosition = substrate.tell()
- # untagged Any container, recover inner header substrate
- length += len(fullSubstrate) - len(substrate)
- substrate = fullSubstrate
+ substrate.seek(fullPosition, os.SEEK_SET)
+ length += currentPosition - fullPosition
if LOG:
- LOG('decoding as untagged ANY, substrate %s' % debug.hexdump(substrate))
+ for chunk in peekIntoStream(substrate, length):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+ LOG('decoding as untagged ANY, substrate '
+ '%s' % debug.hexdump(chunk))
if substrateFun:
- return substrateFun(self._createComponent(asn1Spec, tagSet, noValue, **options),
- substrate, length)
+ for chunk in substrateFun(
+ self._createComponent(asn1Spec, tagSet, noValue, **options),
+ substrate, length, options):
+ yield chunk
- head, tail = substrate[:length], substrate[length:]
+ return
- return self._createComponent(asn1Spec, tagSet, head, **options), tail
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+
+ yield self._createComponent(asn1Spec, tagSet, chunk, **options)
def indefLenValueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
@@ -1123,26 +1291,36 @@ class AnyDecoder(AbstractSimpleDecoder):
if isTagged:
# tagged Any type -- consume header substrate
- header = null
+ chunk = null
if LOG:
LOG('decoding as tagged ANY')
else:
- fullSubstrate = options['fullSubstrate']
+ # TODO: Seems not to be tested
+ fullPosition = substrate.markedPosition
+ currentPosition = substrate.tell()
- # untagged Any, recover header substrate
- header = fullSubstrate[:-len(substrate)]
+ substrate.seek(fullPosition, os.SEEK_SET)
+ for chunk in readFromStream(substrate, currentPosition - fullPosition, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
if LOG:
- LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(header))
+ LOG('decoding as untagged ANY, header substrate %s' % debug.hexdump(chunk))
# Any components do not inherit initial tag
asn1Spec = self.protoComponent
if substrateFun and substrateFun is not self.substrateCollector:
- asn1Object = self._createComponent(asn1Spec, tagSet, noValue, **options)
- return substrateFun(asn1Object, header + substrate, length + len(header))
+ asn1Object = self._createComponent(
+ asn1Spec, tagSet, noValue, **options)
+
+ for chunk in substrateFun(
+ asn1Object, chunk + substrate, length + len(chunk), options):
+ yield chunk
+
+ return
if LOG:
LOG('assembling constructed serialization')
@@ -1150,131 +1328,134 @@ class AnyDecoder(AbstractSimpleDecoder):
# All inner fragments are of the same type, treat them as octet string
substrateFun = self.substrateCollector
- while substrate:
- component, substrate = decodeFun(substrate, asn1Spec,
- substrateFun=substrateFun,
- allowEoo=True, **options)
+ while True: # loop over fragments
+
+ for component in decodeFun(
+ substrate, asn1Spec, substrateFun=substrateFun,
+ allowEoo=True, **options):
+
+ if isinstance(component, SubstrateUnderrunError):
+ yield component
+
+ if component is eoo.endOfOctets:
+ break
+
if component is eoo.endOfOctets:
break
- header += component
-
- else:
- raise error.SubstrateUnderrunError(
- 'No EOO seen before substrate ends'
- )
+ chunk += component
if substrateFun:
- return header, substrate
+ yield chunk # TODO: Weird
else:
- return self._createComponent(asn1Spec, tagSet, header, **options), substrate
+ yield self._createComponent(asn1Spec, tagSet, chunk, **options)
# character string types
-class UTF8StringDecoder(OctetStringDecoder):
+class UTF8StringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.UTF8String()
-class NumericStringDecoder(OctetStringDecoder):
+class NumericStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.NumericString()
-class PrintableStringDecoder(OctetStringDecoder):
+class PrintableStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.PrintableString()
-class TeletexStringDecoder(OctetStringDecoder):
+class TeletexStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.TeletexString()
-class VideotexStringDecoder(OctetStringDecoder):
+class VideotexStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.VideotexString()
-class IA5StringDecoder(OctetStringDecoder):
+class IA5StringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.IA5String()
-class GraphicStringDecoder(OctetStringDecoder):
+class GraphicStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.GraphicString()
-class VisibleStringDecoder(OctetStringDecoder):
+class VisibleStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.VisibleString()
-class GeneralStringDecoder(OctetStringDecoder):
+class GeneralStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.GeneralString()
-class UniversalStringDecoder(OctetStringDecoder):
+class UniversalStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.UniversalString()
-class BMPStringDecoder(OctetStringDecoder):
+class BMPStringPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = char.BMPString()
# "useful" types
-class ObjectDescriptorDecoder(OctetStringDecoder):
+class ObjectDescriptorPayloadDecoder(OctetStringPayloadDecoder):
protoComponent = useful.ObjectDescriptor()
-class GeneralizedTimeDecoder(OctetStringDecoder):
+class GeneralizedTimePayloadDecoder(OctetStringPayloadDecoder):
protoComponent = useful.GeneralizedTime()
-class UTCTimeDecoder(OctetStringDecoder):
+class UTCTimePayloadDecoder(OctetStringPayloadDecoder):
protoComponent = useful.UTCTime()
-tagMap = {
- univ.Integer.tagSet: IntegerDecoder(),
- univ.Boolean.tagSet: BooleanDecoder(),
- univ.BitString.tagSet: BitStringDecoder(),
- univ.OctetString.tagSet: OctetStringDecoder(),
- univ.Null.tagSet: NullDecoder(),
- univ.ObjectIdentifier.tagSet: ObjectIdentifierDecoder(),
- univ.Enumerated.tagSet: IntegerDecoder(),
- univ.Real.tagSet: RealDecoder(),
- univ.Sequence.tagSet: SequenceOrSequenceOfDecoder(), # conflicts with SequenceOf
- univ.Set.tagSet: SetOrSetOfDecoder(), # conflicts with SetOf
- univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
+TAG_MAP = {
+ univ.Integer.tagSet: IntegerPayloadDecoder(),
+ univ.Boolean.tagSet: BooleanPayloadDecoder(),
+ univ.BitString.tagSet: BitStringPayloadDecoder(),
+ univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+ univ.Null.tagSet: NullPayloadDecoder(),
+ univ.ObjectIdentifier.tagSet: ObjectIdentifierPayloadDecoder(),
+ univ.Enumerated.tagSet: IntegerPayloadDecoder(),
+ univ.Real.tagSet: RealPayloadDecoder(),
+ univ.Sequence.tagSet: SequenceOrSequenceOfPayloadDecoder(), # conflicts with SequenceOf
+ univ.Set.tagSet: SetOrSetOfPayloadDecoder(), # conflicts with SetOf
+ univ.Choice.tagSet: ChoicePayloadDecoder(), # conflicts with Any
# character string types
- char.UTF8String.tagSet: UTF8StringDecoder(),
- char.NumericString.tagSet: NumericStringDecoder(),
- char.PrintableString.tagSet: PrintableStringDecoder(),
- char.TeletexString.tagSet: TeletexStringDecoder(),
- char.VideotexString.tagSet: VideotexStringDecoder(),
- char.IA5String.tagSet: IA5StringDecoder(),
- char.GraphicString.tagSet: GraphicStringDecoder(),
- char.VisibleString.tagSet: VisibleStringDecoder(),
- char.GeneralString.tagSet: GeneralStringDecoder(),
- char.UniversalString.tagSet: UniversalStringDecoder(),
- char.BMPString.tagSet: BMPStringDecoder(),
+ char.UTF8String.tagSet: UTF8StringPayloadDecoder(),
+ char.NumericString.tagSet: NumericStringPayloadDecoder(),
+ char.PrintableString.tagSet: PrintableStringPayloadDecoder(),
+ char.TeletexString.tagSet: TeletexStringPayloadDecoder(),
+ char.VideotexString.tagSet: VideotexStringPayloadDecoder(),
+ char.IA5String.tagSet: IA5StringPayloadDecoder(),
+ char.GraphicString.tagSet: GraphicStringPayloadDecoder(),
+ char.VisibleString.tagSet: VisibleStringPayloadDecoder(),
+ char.GeneralString.tagSet: GeneralStringPayloadDecoder(),
+ char.UniversalString.tagSet: UniversalStringPayloadDecoder(),
+ char.BMPString.tagSet: BMPStringPayloadDecoder(),
# useful types
- useful.ObjectDescriptor.tagSet: ObjectDescriptorDecoder(),
- useful.GeneralizedTime.tagSet: GeneralizedTimeDecoder(),
- useful.UTCTime.tagSet: UTCTimeDecoder()
+ useful.ObjectDescriptor.tagSet: ObjectDescriptorPayloadDecoder(),
+ useful.GeneralizedTime.tagSet: GeneralizedTimePayloadDecoder(),
+ useful.UTCTime.tagSet: UTCTimePayloadDecoder()
}
# Type-to-codec map for ambiguous ASN.1 types
-typeMap = {
- univ.Set.typeId: SetDecoder(),
- univ.SetOf.typeId: SetOfDecoder(),
- univ.Sequence.typeId: SequenceDecoder(),
- univ.SequenceOf.typeId: SequenceOfDecoder(),
- univ.Choice.typeId: ChoiceDecoder(),
- univ.Any.typeId: AnyDecoder()
+TYPE_MAP = {
+ univ.Set.typeId: SetPayloadDecoder(),
+ univ.SetOf.typeId: SetOfPayloadDecoder(),
+ univ.Sequence.typeId: SequencePayloadDecoder(),
+ univ.SequenceOf.typeId: SequenceOfPayloadDecoder(),
+ univ.Choice.typeId: ChoicePayloadDecoder(),
+ univ.Any.typeId: AnyPayloadDecoder()
}
# Put in non-ambiguous types for faster codec lookup
-for typeDecoder in tagMap.values():
+for typeDecoder in TAG_MAP.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
- if typeId is not None and typeId not in typeMap:
- typeMap[typeId] = typeDecoder
+ if typeId is not None and typeId not in TYPE_MAP:
+ TYPE_MAP[typeId] = typeDecoder
(stDecodeTag,
@@ -1289,65 +1470,81 @@ for typeDecoder in tagMap.values():
stStop) = [x for x in range(10)]
-class Decoder(object):
+EOO_SENTINEL = ints2octs((0, 0))
+
+
+class SingleItemDecoder(object):
defaultErrorState = stErrorCondition
#defaultErrorState = stDumpRawValue
- defaultRawDecoder = AnyDecoder()
+ defaultRawDecoder = AnyPayloadDecoder()
+
supportIndefLength = True
- # noinspection PyDefaultArgument
- def __init__(self, tagMap, typeMap={}):
- self.__tagMap = tagMap
- self.__typeMap = typeMap
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
+ def __init__(self, **options):
+ self._tagMap = options.get('tagMap', self.TAG_MAP)
+ self._typeMap = options.get('typeMap', self.TYPE_MAP)
+
# Tag & TagSet objects caches
- self.__tagCache = {}
- self.__tagSetCache = {}
- self.__eooSentinel = ints2octs((0, 0))
+ self._tagCache = {}
+ self._tagSetCache = {}
def __call__(self, substrate, asn1Spec=None,
tagSet=None, length=None, state=stDecodeTag,
decodeFun=None, substrateFun=None,
**options):
- if LOG:
- LOG('decoder called at scope %s with state %d, working with up to %d octets of substrate: %s' % (debug.scope, state, len(substrate), debug.hexdump(substrate)))
-
allowEoo = options.pop('allowEoo', False)
+ if LOG:
+ LOG('decoder called at scope %s with state %d, working with up '
+ 'to %s octets of substrate: '
+ '%s' % (debug.scope, state, length, substrate))
+
# Look for end-of-octets sentinel
if allowEoo and self.supportIndefLength:
- if substrate[:2] == self.__eooSentinel:
+
+ for eoo_candidate in readFromStream(substrate, 2, options):
+ if isinstance(eoo_candidate, SubstrateUnderrunError):
+ yield eoo_candidate
+
+ if eoo_candidate == EOO_SENTINEL:
if LOG:
LOG('end-of-octets sentinel found')
- return eoo.endOfOctets, substrate[2:]
+ yield eoo.endOfOctets
+ return
- value = noValue
+ else:
+ substrate.seek(-2, os.SEEK_CUR)
- tagMap = self.__tagMap
- typeMap = self.__typeMap
- tagCache = self.__tagCache
- tagSetCache = self.__tagSetCache
+ tagMap = self._tagMap
+ typeMap = self._typeMap
+ tagCache = self._tagCache
+ tagSetCache = self._tagSetCache
- fullSubstrate = substrate
+ value = noValue
+
+ substrate.markedPosition = substrate.tell()
while state is not stStop:
if state is stDecodeTag:
- if not substrate:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on tag decoding'
- )
-
# Decode tag
isShortTag = True
- firstOctet = substrate[0]
- substrate = substrate[1:]
+
+ for firstByte in readFromStream(substrate, 1, options):
+ if isinstance(firstByte, SubstrateUnderrunError):
+ yield firstByte
+
+ firstOctet = ord(firstByte)
try:
lastTag = tagCache[firstOctet]
except KeyError:
- integerTag = oct2int(firstOctet)
+ integerTag = firstOctet
tagClass = integerTag & 0xC0
tagFormat = integerTag & 0x20
tagId = integerTag & 0x1F
@@ -1357,21 +1554,23 @@ class Decoder(object):
lengthOctetIdx = 0
tagId = 0
- try:
- while True:
- integerTag = oct2int(substrate[lengthOctetIdx])
- lengthOctetIdx += 1
- tagId <<= 7
- tagId |= (integerTag & 0x7F)
- if not integerTag & 0x80:
- break
-
- substrate = substrate[lengthOctetIdx:]
-
- except IndexError:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on long tag decoding'
- )
+ while True:
+ for integerByte in readFromStream(substrate, 1, options):
+ if isinstance(integerByte, SubstrateUnderrunError):
+ yield integerByte
+
+ if not integerByte:
+ raise error.SubstrateUnderrunError(
+ 'Short octet stream on long tag decoding'
+ )
+
+ integerTag = ord(integerByte)
+ lengthOctetIdx += 1
+ tagId <<= 7
+ tagId |= (integerTag & 0x7F)
+
+ if not integerTag & 0x80:
+ break
lastTag = tag.Tag(
tagClass=tagClass, tagFormat=tagFormat, tagId=tagId
@@ -1403,21 +1602,22 @@ class Decoder(object):
if state is stDecodeLength:
# Decode length
- if not substrate:
- raise error.SubstrateUnderrunError(
- 'Short octet stream on length decoding'
- )
+ for firstOctet in readFromStream(substrate, 1, options):
+ if isinstance(firstOctet, SubstrateUnderrunError):
+ yield firstOctet
- firstOctet = oct2int(substrate[0])
+ firstOctet = ord(firstOctet)
if firstOctet < 128:
- size = 1
length = firstOctet
elif firstOctet > 128:
size = firstOctet & 0x7F
# encoded in size bytes
- encodedLength = octs2ints(substrate[1:size + 1])
+ for encodedLength in readFromStream(substrate, size, options):
+ if isinstance(encodedLength, SubstrateUnderrunError):
+ yield encodedLength
+ encodedLength = list(encodedLength)
# missing check on maximum size, which shouldn't be a
# problem, we can handle more than is possible
if len(encodedLength) != size:
@@ -1428,27 +1628,19 @@ class Decoder(object):
length = 0
for lengthOctet in encodedLength:
length <<= 8
- length |= lengthOctet
+ length |= oct2int(lengthOctet)
size += 1
- else:
- size = 1
+ else: # 128 means indefinite
length = -1
- substrate = substrate[size:]
-
- if length == -1:
- if not self.supportIndefLength:
- raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
-
- else:
- if len(substrate) < length:
- raise error.SubstrateUnderrunError('%d-octet short' % (length - len(substrate)))
+ if length == -1 and not self.supportIndefLength:
+ raise error.PyAsn1Error('Indefinite length encoding not supported by this codec')
state = stGetValueDecoder
if LOG:
- LOG('value length decoded into %d, payload substrate is: %s' % (length, debug.hexdump(length == -1 and substrate or substrate[:length])))
+ LOG('value length decoded into %d' % length)
if state is stGetValueDecoder:
if asn1Spec is None:
@@ -1567,26 +1759,33 @@ class Decoder(object):
if not options.get('recursiveFlag', True) and not substrateFun: # deprecate this
substrateFun = lambda a, b, c: (a, b[:c])
- options.update(fullSubstrate=fullSubstrate)
+ original_position = substrate.tell()
if length == -1: # indef length
- value, substrate = concreteDecoder.indefLenValueDecoder(
- substrate, asn1Spec,
- tagSet, length, stGetValueDecoder,
- self, substrateFun,
- **options
- )
+ for value in concreteDecoder.indefLenValueDecoder(
+ substrate, asn1Spec,
+ tagSet, length, stGetValueDecoder,
+ self, substrateFun, **options):
+ if isinstance(value, SubstrateUnderrunError):
+ yield value
else:
- value, substrate = concreteDecoder.valueDecoder(
- substrate, asn1Spec,
- tagSet, length, stGetValueDecoder,
- self, substrateFun,
- **options
- )
+ for value in concreteDecoder.valueDecoder(
+ substrate, asn1Spec,
+ tagSet, length, stGetValueDecoder,
+ self, substrateFun, **options):
+ if isinstance(value, SubstrateUnderrunError):
+ yield value
+
+ bytesRead = substrate.tell() - original_position
+ if bytesRead != length:
+ raise PyAsn1Error(
+ "Read %s bytes instead of expected %s." % (bytesRead, length))
if LOG:
- LOG('codec %s yields type %s, value:\n%s\n...remaining substrate is: %s' % (concreteDecoder.__class__.__name__, value.__class__.__name__, isinstance(value, base.Asn1Item) and value.prettyPrint() or value, substrate and debug.hexdump(substrate) or '<none>'))
+ LOG('codec %s yields type %s, value:\n%s\n...' % (
+ concreteDecoder.__class__.__name__, value.__class__.__name__,
+ isinstance(value, base.Asn1Item) and value.prettyPrint() or value))
state = stStop
break
@@ -1596,7 +1795,7 @@ class Decoder(object):
tagSet[0].tagFormat == tag.tagFormatConstructed and
tagSet[0].tagClass != tag.tagClassUniversal):
# Assume explicit tagging
- concreteDecoder = explicitTagDecoder
+ concreteDecoder = rawPayloadDecoder
state = stDecodeValue
else:
@@ -1623,7 +1822,190 @@ class Decoder(object):
debug.scope.pop()
LOG('decoder left scope %s, call completed' % debug.scope)
- return value, substrate
+ yield value
+
+
+class StreamingDecoder(object):
+ """Create an iterator that turns BER/CER/DER byte stream into ASN.1 objects.
+
+ On each iteration, consume whatever BER/CER/DER serialization is
+ available in the `substrate` stream-like object and turns it into
+ one or more, possibly nested, ASN.1 objects.
+
+ Parameters
+ ----------
+ substrate: :py:class:`file`, :py:class:`io.BytesIO`
+ BER/CER/DER serialization in form of a byte stream
+
+ Keyword Args
+ ------------
+ asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
+ A pyasn1 type object to act as a template guiding the decoder.
+ Depending on the ASN.1 structure being decoded, `asn1Spec` may
+ or may not be required. One of the reasons why `asn1Spec` may
+ me required is that ASN.1 structure is encoded in the *IMPLICIT*
+ tagging mode.
+
+ Yields
+ ------
+ : :py:class:`~pyasn1.type.base.PyAsn1Item`, :py:class:`~pyasn1.error.SubstrateUnderrunError`
+ Decoded ASN.1 object (possibly, nested) or
+ :py:class:`~pyasn1.error.SubstrateUnderrunError` object indicating
+ insufficient BER/CER/DER serialization on input to fully recover ASN.1
+ objects from it.
+
+ In the latter case the caller is advised to ensure some more data in
+ the input stream, then call the iterator again. The decoder will resume
+ the decoding process using the newly arrived data.
+
+ The `context` property of :py:class:`~pyasn1.error.SubstrateUnderrunError`
+ object might hold a reference to the partially populated ASN.1 object
+ being reconstructed.
+
+ Raises
+ ------
+ ~pyasn1.error.PyAsn1Error, ~pyasn1.error.EndOfStreamError
+ `PyAsn1Error` on deserialization error, `EndOfStreamError` on
+ premature stream closure.
+
+ Examples
+ --------
+ Decode BER serialisation without ASN.1 schema
+
+ .. code-block:: pycon
+
+ >>> stream = io.BytesIO(
+ ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+ >>>
+ >>> for asn1Object in StreamingDecoder(stream):
+ ... print(asn1Object)
+ >>>
+ SequenceOf:
+ 1 2 3
+
+ Decode BER serialisation with ASN.1 schema
+
+ .. code-block:: pycon
+
+ >>> stream = io.BytesIO(
+ ... b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+ >>>
+ >>> schema = SequenceOf(componentType=Integer())
+ >>>
+ >>> decoder = StreamingDecoder(stream, asn1Spec=schema)
+ >>> for asn1Object in decoder:
+ ... print(asn1Object)
+ >>>
+ SequenceOf:
+ 1 2 3
+ """
+
+ SINGLE_ITEM_DECODER = SingleItemDecoder
+
+ def __init__(self, substrate, asn1Spec=None, **options):
+ self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options)
+ self._substrate = asSeekableStream(substrate)
+ self._asn1Spec = asn1Spec
+ self._options = options
+
+ def __iter__(self):
+ while True:
+ for asn1Object in self._singleItemDecoder(
+ self._substrate, self._asn1Spec, **self._options):
+ yield asn1Object
+
+ for chunk in isEndOfStream(self._substrate):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield
+
+ break
+
+ if chunk:
+ break
+
+
+class Decoder(object):
+ """Create a BER decoder object.
+
+ Parse BER/CER/DER octet-stream into one, possibly nested, ASN.1 object.
+ """
+ STREAMING_DECODER = StreamingDecoder
+
+ @classmethod
+ def __call__(cls, substrate, asn1Spec=None, **options):
+ """Turns BER/CER/DER octet stream into an ASN.1 object.
+
+ Takes BER/CER/DER octet-stream in form of :py:class:`bytes` (Python 3)
+ or :py:class:`str` (Python 2) and decode it into an ASN.1 object
+ (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative) which
+ may be a scalar or an arbitrary nested structure.
+
+ Parameters
+ ----------
+ substrate: :py:class:`bytes` (Python 3) or :py:class:`str` (Python 2)
+ BER/CER/DER octet-stream to parse
+
+ Keyword Args
+ ------------
+ asn1Spec: :py:class:`~pyasn1.type.base.PyAsn1Item`
+ A pyasn1 type object (:py:class:`~pyasn1.type.base.PyAsn1Item`
+ derivative) to act as a template guiding the decoder.
+ Depending on the ASN.1 structure being decoded, `asn1Spec` may or
+ may not be required. Most common reason for it to require is that
+ ASN.1 structure is encoded in *IMPLICIT* tagging mode.
+
+ Returns
+ -------
+ : :py:class:`tuple`
+ A tuple of :py:class:`~pyasn1.type.base.PyAsn1Item` object
+ recovered from BER/CER/DER substrate and the unprocessed trailing
+ portion of the `substrate` (may be empty)
+
+ Raises
+ ------
+ : :py:class:`~pyasn1.error.PyAsn1Error`
+ :py:class:`~pyasn1.error.SubstrateUnderrunError` on insufficient
+ input or :py:class:`~pyasn1.error.PyAsn1Error` on decoding error.
+
+ Examples
+ --------
+ Decode BER/CER/DER serialisation without ASN.1 schema
+
+ .. code-block:: pycon
+
+ >>> s, unprocessed = decode(b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03')
+ >>> str(s)
+ SequenceOf:
+ 1 2 3
+
+ Decode BER/CER/DER serialisation with ASN.1 schema
+
+ .. code-block:: pycon
+
+ >>> seq = SequenceOf(componentType=Integer())
+ >>> s, unprocessed = decode(
+ b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03', asn1Spec=seq)
+ >>> str(s)
+ SequenceOf:
+ 1 2 3
+
+ """
+ substrate = asSeekableStream(substrate)
+
+ streamingDecoder = cls.STREAMING_DECODER(
+ substrate, asn1Spec, **options)
+
+ for asn1Object in streamingDecoder:
+ if isinstance(asn1Object, SubstrateUnderrunError):
+ raise error.SubstrateUnderrunError('Short substrate on input')
+
+ try:
+ tail = next(readFromStream(substrate))
+
+ except error.EndOfStreamError:
+ tail = null
+
+ return asn1Object, tail
#: Turns BER octet stream into an ASN.1 object.
@@ -1655,6 +2037,11 @@ class Decoder(object):
#: ~pyasn1.error.PyAsn1Error, ~pyasn1.error.SubstrateUnderrunError
#: On decoding errors
#:
+#: Notes
+#: -----
+#: This function is deprecated. Please use :py:class:`Decoder` or
+#: :py:class:`StreamingDecoder` class instance.
+#:
#: Examples
#: --------
#: Decode BER serialisation without ASN.1 schema
@@ -1676,7 +2063,4 @@ class Decoder(object):
#: SequenceOf:
#: 1 2 3
#:
-decode = Decoder(tagMap, typeMap)
-
-# XXX
-# non-recursive decoding; return position rather than substrate
+decode = Decoder()
diff --git a/pyasn1/codec/ber/encoder.py b/pyasn1/codec/ber/encoder.py
index 778aa86..826ea73 100644
--- a/pyasn1/codec/ber/encoder.py
+++ b/pyasn1/codec/ber/encoder.py
@@ -17,7 +17,7 @@ from pyasn1.type import tag
from pyasn1.type import univ
from pyasn1.type import useful
-__all__ = ['encode']
+__all__ = ['Encoder', 'encode']
LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_ENCODER)
@@ -706,7 +706,7 @@ class AnyEncoder(OctetStringEncoder):
return value, not options.get('defMode', True), True
-tagMap = {
+TAG_MAP = {
eoo.endOfOctets.tagSet: EndOfOctetsEncoder(),
univ.Boolean.tagSet: BooleanEncoder(),
univ.Integer.tagSet: IntegerEncoder(),
@@ -739,7 +739,7 @@ tagMap = {
}
# Put in ambiguous & non-ambiguous types for faster codec lookup
-typeMap = {
+TYPE_MAP = {
univ.Boolean.typeId: BooleanEncoder(),
univ.Integer.typeId: IntegerEncoder(),
univ.BitString.typeId: BitStringEncoder(),
@@ -774,14 +774,16 @@ typeMap = {
}
-class Encoder(object):
+class SingleItemEncoder(object):
fixedDefLengthMode = None
fixedChunkSize = None
- # noinspection PyDefaultArgument
- def __init__(self, tagMap, typeMap={}):
- self.__tagMap = tagMap
- self.__typeMap = typeMap
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
+ def __init__(self, **options):
+ self._tagMap = options.get('tagMap', self.TAG_MAP)
+ self._typeMap = options.get('typeMap', self.TYPE_MAP)
def __call__(self, value, asn1Spec=None, **options):
try:
@@ -795,8 +797,11 @@ class Encoder(object):
'and "asn1Spec" not given' % (value,))
if LOG:
- LOG('encoder called in %sdef mode, chunk size %s for '
- 'type %s, value:\n%s' % (not options.get('defMode', True) and 'in' or '', options.get('maxChunkSize', 0), asn1Spec is None and value.prettyPrintType() or asn1Spec.prettyPrintType(), value))
+ LOG('encoder called in %sdef mode, chunk size %s for type %s, '
+ 'value:\n%s' % (not options.get('defMode', True) and 'in' or '',
+ options.get('maxChunkSize', 0),
+ asn1Spec is None and value.prettyPrintType() or
+ asn1Spec.prettyPrintType(), value))
if self.fixedDefLengthMode is not None:
options.update(defMode=self.fixedDefLengthMode)
@@ -804,12 +809,12 @@ class Encoder(object):
if self.fixedChunkSize is not None:
options.update(maxChunkSize=self.fixedChunkSize)
-
try:
- concreteEncoder = self.__typeMap[typeId]
+ concreteEncoder = self._typeMap[typeId]
if LOG:
- LOG('using value codec %s chosen by type ID %s' % (concreteEncoder.__class__.__name__, typeId))
+ LOG('using value codec %s chosen by type ID '
+ '%s' % (concreteEncoder.__class__.__name__, typeId))
except KeyError:
if asn1Spec is None:
@@ -821,21 +826,36 @@ class Encoder(object):
baseTagSet = tag.TagSet(tagSet.baseTag, tagSet.baseTag)
try:
- concreteEncoder = self.__tagMap[baseTagSet]
+ concreteEncoder = self._tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('No encoder for %r (%s)' % (value, tagSet))
if LOG:
- LOG('using value codec %s chosen by tagSet %s' % (concreteEncoder.__class__.__name__, tagSet))
+ LOG('using value codec %s chosen by tagSet '
+ '%s' % (concreteEncoder.__class__.__name__, tagSet))
substrate = concreteEncoder.encode(value, asn1Spec, self, **options)
if LOG:
- LOG('codec %s built %s octets of substrate: %s\nencoder completed' % (concreteEncoder, len(substrate), debug.hexdump(substrate)))
+ LOG('codec %s built %s octets of substrate: %s\nencoder '
+ 'completed' % (concreteEncoder, len(substrate),
+ debug.hexdump(substrate)))
return substrate
+
+class Encoder(object):
+ SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+ def __init__(self, **options):
+ self._singleItemEncoder = self.SINGLE_ITEM_ENCODER(**options)
+
+ def __call__(self, pyObject, asn1Spec=None, **options):
+ return self._singleItemEncoder(
+ pyObject, asn1Spec=asn1Spec, **options)
+
+
#: Turns ASN.1 object into BER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
@@ -887,4 +907,4 @@ class Encoder(object):
#: >>> encode(seq)
#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
#:
-encode = Encoder(tagMap, typeMap)
+encode = Encoder()
diff --git a/pyasn1/codec/cer/decoder.py b/pyasn1/codec/cer/decoder.py
index 3e86fd0..0a92b26 100644
--- a/pyasn1/codec/cer/decoder.py
+++ b/pyasn1/codec/cer/decoder.py
@@ -5,60 +5,88 @@
# License: http://snmplabs.com/pyasn1/license.html
#
from pyasn1 import error
+from pyasn1.codec.streaming import readFromStream
from pyasn1.codec.ber import decoder
from pyasn1.compat.octets import oct2int
from pyasn1.type import univ
-__all__ = ['decode']
+__all__ = ['decode', 'StreamingDecoder']
+SubstrateUnderrunError = error.SubstrateUnderrunError
-class BooleanDecoder(decoder.AbstractSimpleDecoder):
+
+class BooleanPayloadDecoder(decoder.AbstractSimplePayloadDecoder):
protoComponent = univ.Boolean(0)
def valueDecoder(self, substrate, asn1Spec,
tagSet=None, length=None, state=None,
decodeFun=None, substrateFun=None,
**options):
- head, tail = substrate[:length], substrate[length:]
- if not head or length != 1:
+
+ if length != 1:
raise error.PyAsn1Error('Not single-octet Boolean payload')
- byte = oct2int(head[0])
+
+ for chunk in readFromStream(substrate, length, options):
+ if isinstance(chunk, SubstrateUnderrunError):
+ yield chunk
+
+ byte = oct2int(chunk[0])
+
# CER/DER specifies encoding of TRUE as 0xFF and FALSE as 0x0, while
# BER allows any non-zero value as TRUE; cf. sections 8.2.2. and 11.1
# in https://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf
if byte == 0xff:
value = 1
+
elif byte == 0x00:
value = 0
+
else:
raise error.PyAsn1Error('Unexpected Boolean payload: %s' % byte)
- return self._createComponent(asn1Spec, tagSet, value, **options), tail
+
+ yield self._createComponent(asn1Spec, tagSet, value, **options)
+
# TODO: prohibit non-canonical encoding
-BitStringDecoder = decoder.BitStringDecoder
-OctetStringDecoder = decoder.OctetStringDecoder
-RealDecoder = decoder.RealDecoder
-
-tagMap = decoder.tagMap.copy()
-tagMap.update(
- {univ.Boolean.tagSet: BooleanDecoder(),
- univ.BitString.tagSet: BitStringDecoder(),
- univ.OctetString.tagSet: OctetStringDecoder(),
- univ.Real.tagSet: RealDecoder()}
+BitStringPayloadDecoder = decoder.BitStringPayloadDecoder
+OctetStringPayloadDecoder = decoder.OctetStringPayloadDecoder
+RealPayloadDecoder = decoder.RealPayloadDecoder
+
+TAG_MAP = decoder.TAG_MAP.copy()
+TAG_MAP.update(
+ {univ.Boolean.tagSet: BooleanPayloadDecoder(),
+ univ.BitString.tagSet: BitStringPayloadDecoder(),
+ univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+ univ.Real.tagSet: RealPayloadDecoder()}
)
-typeMap = decoder.typeMap.copy()
+TYPE_MAP = decoder.TYPE_MAP.copy()
# Put in non-ambiguous types for faster codec lookup
-for typeDecoder in tagMap.values():
+for typeDecoder in TAG_MAP.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
- if typeId is not None and typeId not in typeMap:
- typeMap[typeId] = typeDecoder
+ if typeId is not None and typeId not in TYPE_MAP:
+ TYPE_MAP[typeId] = typeDecoder
+
+
+class SingleItemDecoder(decoder.SingleItemDecoder):
+ __doc__ = decoder.SingleItemDecoder.__doc__
+
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
+
+class StreamingDecoder(decoder.StreamingDecoder):
+ __doc__ = decoder.StreamingDecoder.__doc__
+
+ SINGLE_ITEM_DECODER = SingleItemDecoder
class Decoder(decoder.Decoder):
- pass
+ __doc__ = decoder.Decoder.__doc__
+
+ STREAMING_DECODER = StreamingDecoder
#: Turns CER octet stream into an ASN.1 object.
@@ -111,4 +139,4 @@ class Decoder(decoder.Decoder):
#: SequenceOf:
#: 1 2 3
#:
-decode = Decoder(tagMap, decoder.typeMap)
+decode = Decoder()
diff --git a/pyasn1/codec/cer/encoder.py b/pyasn1/codec/cer/encoder.py
index 935b696..9e6cdac 100644
--- a/pyasn1/codec/cer/encoder.py
+++ b/pyasn1/codec/cer/encoder.py
@@ -10,7 +10,7 @@ from pyasn1.compat.octets import str2octs, null
from pyasn1.type import univ
from pyasn1.type import useful
-__all__ = ['encode']
+__all__ = ['Encoder', 'encode']
class BooleanEncoder(encoder.IntegerEncoder):
@@ -234,8 +234,9 @@ class SequenceEncoder(encoder.SequenceEncoder):
omitEmptyOptionals = True
-tagMap = encoder.tagMap.copy()
-tagMap.update({
+TAG_MAP = encoder.TAG_MAP.copy()
+
+TAG_MAP.update({
univ.Boolean.tagSet: BooleanEncoder(),
univ.Real.tagSet: RealEncoder(),
useful.GeneralizedTime.tagSet: GeneralizedTimeEncoder(),
@@ -245,8 +246,9 @@ tagMap.update({
univ.Sequence.typeId: SequenceEncoder()
})
-typeMap = encoder.typeMap.copy()
-typeMap.update({
+TYPE_MAP = encoder.TYPE_MAP.copy()
+
+TYPE_MAP.update({
univ.Boolean.typeId: BooleanEncoder(),
univ.Real.typeId: RealEncoder(),
useful.GeneralizedTime.typeId: GeneralizedTimeEncoder(),
@@ -259,10 +261,18 @@ typeMap.update({
})
-class Encoder(encoder.Encoder):
+class SingleItemEncoder(encoder.SingleItemEncoder):
fixedDefLengthMode = False
fixedChunkSize = 1000
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
+
+class Encoder(encoder.Encoder):
+ SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+
#: Turns ASN.1 object into CER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
@@ -308,6 +318,6 @@ class Encoder(encoder.Encoder):
#: >>> encode(seq)
#: b'0\x80\x02\x01\x01\x02\x01\x02\x02\x01\x03\x00\x00'
#:
-encode = Encoder(tagMap, typeMap)
+encode = Encoder()
# EncoderFactory queries class instance and builds a map of tags -> encoders
diff --git a/pyasn1/codec/der/decoder.py b/pyasn1/codec/der/decoder.py
index 1a13fdb..b9526c3 100644
--- a/pyasn1/codec/der/decoder.py
+++ b/pyasn1/codec/der/decoder.py
@@ -7,40 +7,58 @@
from pyasn1.codec.cer import decoder
from pyasn1.type import univ
-__all__ = ['decode']
+__all__ = ['decode', 'StreamingDecoder']
-class BitStringDecoder(decoder.BitStringDecoder):
+class BitStringPayloadDecoder(decoder.BitStringPayloadDecoder):
supportConstructedForm = False
-class OctetStringDecoder(decoder.OctetStringDecoder):
+class OctetStringPayloadDecoder(decoder.OctetStringPayloadDecoder):
supportConstructedForm = False
+
# TODO: prohibit non-canonical encoding
-RealDecoder = decoder.RealDecoder
+RealPayloadDecoder = decoder.RealPayloadDecoder
-tagMap = decoder.tagMap.copy()
-tagMap.update(
- {univ.BitString.tagSet: BitStringDecoder(),
- univ.OctetString.tagSet: OctetStringDecoder(),
- univ.Real.tagSet: RealDecoder()}
+TAG_MAP = decoder.TAG_MAP.copy()
+TAG_MAP.update(
+ {univ.BitString.tagSet: BitStringPayloadDecoder(),
+ univ.OctetString.tagSet: OctetStringPayloadDecoder(),
+ univ.Real.tagSet: RealPayloadDecoder()}
)
-typeMap = decoder.typeMap.copy()
+TYPE_MAP = decoder.TYPE_MAP.copy()
# Put in non-ambiguous types for faster codec lookup
-for typeDecoder in tagMap.values():
+for typeDecoder in TAG_MAP.values():
if typeDecoder.protoComponent is not None:
typeId = typeDecoder.protoComponent.__class__.typeId
- if typeId is not None and typeId not in typeMap:
- typeMap[typeId] = typeDecoder
+ if typeId is not None and typeId not in TYPE_MAP:
+ TYPE_MAP[typeId] = typeDecoder
-class Decoder(decoder.Decoder):
+class SingleItemDecoder(decoder.SingleItemDecoder):
+ __doc__ = decoder.SingleItemDecoder.__doc__
+
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
supportIndefLength = False
+class StreamingDecoder(decoder.StreamingDecoder):
+ __doc__ = decoder.StreamingDecoder.__doc__
+
+ SINGLE_ITEM_DECODER = SingleItemDecoder
+
+
+class Decoder(decoder.Decoder):
+ __doc__ = decoder.Decoder.__doc__
+
+ STREAMING_DECODER = StreamingDecoder
+
+
#: Turns DER octet stream into an ASN.1 object.
#:
#: Takes DER octet-stream and decode it into an ASN.1 object
@@ -91,4 +109,4 @@ class Decoder(decoder.Decoder):
#: SequenceOf:
#: 1 2 3
#:
-decode = Decoder(tagMap, typeMap)
+decode = Decoder()
diff --git a/pyasn1/codec/der/encoder.py b/pyasn1/codec/der/encoder.py
index 90e982d..1a6af82 100644
--- a/pyasn1/codec/der/encoder.py
+++ b/pyasn1/codec/der/encoder.py
@@ -8,7 +8,7 @@ from pyasn1 import error
from pyasn1.codec.cer import encoder
from pyasn1.type import univ
-__all__ = ['encode']
+__all__ = ['Encoder', 'encode']
class SetEncoder(encoder.SetEncoder):
@@ -42,23 +42,34 @@ class SetEncoder(encoder.SetEncoder):
else:
return compType.tagSet
-tagMap = encoder.tagMap.copy()
-tagMap.update({
+
+TAG_MAP = encoder.TAG_MAP.copy()
+
+TAG_MAP.update({
# Set & SetOf have same tags
univ.Set.tagSet: SetEncoder()
})
-typeMap = encoder.typeMap.copy()
-typeMap.update({
+TYPE_MAP = encoder.TYPE_MAP.copy()
+
+TYPE_MAP.update({
# Set & SetOf have same tags
univ.Set.typeId: SetEncoder()
})
-class Encoder(encoder.Encoder):
+class SingleItemEncoder(encoder.SingleItemEncoder):
fixedDefLengthMode = True
fixedChunkSize = 0
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
+
+
+class Encoder(encoder.Encoder):
+ SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+
#: Turns ASN.1 object into DER octet stream.
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
@@ -104,4 +115,4 @@ class Encoder(encoder.Encoder):
#: >>> encode(seq)
#: b'0\t\x02\x01\x01\x02\x01\x02\x02\x01\x03'
#:
-encode = Encoder(tagMap, typeMap)
+encode = Encoder()
diff --git a/pyasn1/codec/native/decoder.py b/pyasn1/codec/native/decoder.py
index 104b92e..1838b7d 100644
--- a/pyasn1/codec/native/decoder.py
+++ b/pyasn1/codec/native/decoder.py
@@ -17,17 +17,17 @@ __all__ = ['decode']
LOG = debug.registerLoggee(__name__, flags=debug.DEBUG_DECODER)
-class AbstractScalarDecoder(object):
+class AbstractScalarPayloadDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(pyObject)
-class BitStringDecoder(AbstractScalarDecoder):
+class BitStringPayloadDecoder(AbstractScalarPayloadDecoder):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
return asn1Spec.clone(univ.BitString.fromBinaryString(pyObject))
-class SequenceOrSetDecoder(object):
+class SequenceOrSetPayloadDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
@@ -40,7 +40,7 @@ class SequenceOrSetDecoder(object):
return asn1Value
-class SequenceOfOrSetOfDecoder(object):
+class SequenceOfOrSetOfPayloadDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
@@ -50,7 +50,7 @@ class SequenceOfOrSetOfDecoder(object):
return asn1Value
-class ChoiceDecoder(object):
+class ChoicePayloadDecoder(object):
def __call__(self, pyObject, asn1Spec, decodeFun=None, **options):
asn1Value = asn1Spec.clone()
@@ -64,112 +64,132 @@ class ChoiceDecoder(object):
return asn1Value
-tagMap = {
- univ.Integer.tagSet: AbstractScalarDecoder(),
- univ.Boolean.tagSet: AbstractScalarDecoder(),
- univ.BitString.tagSet: BitStringDecoder(),
- univ.OctetString.tagSet: AbstractScalarDecoder(),
- univ.Null.tagSet: AbstractScalarDecoder(),
- univ.ObjectIdentifier.tagSet: AbstractScalarDecoder(),
- univ.Enumerated.tagSet: AbstractScalarDecoder(),
- univ.Real.tagSet: AbstractScalarDecoder(),
- univ.Sequence.tagSet: SequenceOrSetDecoder(), # conflicts with SequenceOf
- univ.Set.tagSet: SequenceOrSetDecoder(), # conflicts with SetOf
- univ.Choice.tagSet: ChoiceDecoder(), # conflicts with Any
+TAG_MAP = {
+ univ.Integer.tagSet: AbstractScalarPayloadDecoder(),
+ univ.Boolean.tagSet: AbstractScalarPayloadDecoder(),
+ univ.BitString.tagSet: BitStringPayloadDecoder(),
+ univ.OctetString.tagSet: AbstractScalarPayloadDecoder(),
+ univ.Null.tagSet: AbstractScalarPayloadDecoder(),
+ univ.ObjectIdentifier.tagSet: AbstractScalarPayloadDecoder(),
+ univ.Enumerated.tagSet: AbstractScalarPayloadDecoder(),
+ univ.Real.tagSet: AbstractScalarPayloadDecoder(),
+ univ.Sequence.tagSet: SequenceOrSetPayloadDecoder(), # conflicts with SequenceOf
+ univ.Set.tagSet: SequenceOrSetPayloadDecoder(), # conflicts with SetOf
+ univ.Choice.tagSet: ChoicePayloadDecoder(), # conflicts with Any
# character string types
- char.UTF8String.tagSet: AbstractScalarDecoder(),
- char.NumericString.tagSet: AbstractScalarDecoder(),
- char.PrintableString.tagSet: AbstractScalarDecoder(),
- char.TeletexString.tagSet: AbstractScalarDecoder(),
- char.VideotexString.tagSet: AbstractScalarDecoder(),
- char.IA5String.tagSet: AbstractScalarDecoder(),
- char.GraphicString.tagSet: AbstractScalarDecoder(),
- char.VisibleString.tagSet: AbstractScalarDecoder(),
- char.GeneralString.tagSet: AbstractScalarDecoder(),
- char.UniversalString.tagSet: AbstractScalarDecoder(),
- char.BMPString.tagSet: AbstractScalarDecoder(),
+ char.UTF8String.tagSet: AbstractScalarPayloadDecoder(),
+ char.NumericString.tagSet: AbstractScalarPayloadDecoder(),
+ char.PrintableString.tagSet: AbstractScalarPayloadDecoder(),
+ char.TeletexString.tagSet: AbstractScalarPayloadDecoder(),
+ char.VideotexString.tagSet: AbstractScalarPayloadDecoder(),
+ char.IA5String.tagSet: AbstractScalarPayloadDecoder(),
+ char.GraphicString.tagSet: AbstractScalarPayloadDecoder(),
+ char.VisibleString.tagSet: AbstractScalarPayloadDecoder(),
+ char.GeneralString.tagSet: AbstractScalarPayloadDecoder(),
+ char.UniversalString.tagSet: AbstractScalarPayloadDecoder(),
+ char.BMPString.tagSet: AbstractScalarPayloadDecoder(),
# useful types
- useful.ObjectDescriptor.tagSet: AbstractScalarDecoder(),
- useful.GeneralizedTime.tagSet: AbstractScalarDecoder(),
- useful.UTCTime.tagSet: AbstractScalarDecoder()
+ useful.ObjectDescriptor.tagSet: AbstractScalarPayloadDecoder(),
+ useful.GeneralizedTime.tagSet: AbstractScalarPayloadDecoder(),
+ useful.UTCTime.tagSet: AbstractScalarPayloadDecoder()
}
# Put in ambiguous & non-ambiguous types for faster codec lookup
-typeMap = {
- univ.Integer.typeId: AbstractScalarDecoder(),
- univ.Boolean.typeId: AbstractScalarDecoder(),
- univ.BitString.typeId: BitStringDecoder(),
- univ.OctetString.typeId: AbstractScalarDecoder(),
- univ.Null.typeId: AbstractScalarDecoder(),
- univ.ObjectIdentifier.typeId: AbstractScalarDecoder(),
- univ.Enumerated.typeId: AbstractScalarDecoder(),
- univ.Real.typeId: AbstractScalarDecoder(),
+TYPE_MAP = {
+ univ.Integer.typeId: AbstractScalarPayloadDecoder(),
+ univ.Boolean.typeId: AbstractScalarPayloadDecoder(),
+ univ.BitString.typeId: BitStringPayloadDecoder(),
+ univ.OctetString.typeId: AbstractScalarPayloadDecoder(),
+ univ.Null.typeId: AbstractScalarPayloadDecoder(),
+ univ.ObjectIdentifier.typeId: AbstractScalarPayloadDecoder(),
+ univ.Enumerated.typeId: AbstractScalarPayloadDecoder(),
+ univ.Real.typeId: AbstractScalarPayloadDecoder(),
# ambiguous base types
- univ.Set.typeId: SequenceOrSetDecoder(),
- univ.SetOf.typeId: SequenceOfOrSetOfDecoder(),
- univ.Sequence.typeId: SequenceOrSetDecoder(),
- univ.SequenceOf.typeId: SequenceOfOrSetOfDecoder(),
- univ.Choice.typeId: ChoiceDecoder(),
- univ.Any.typeId: AbstractScalarDecoder(),
+ univ.Set.typeId: SequenceOrSetPayloadDecoder(),
+ univ.SetOf.typeId: SequenceOfOrSetOfPayloadDecoder(),
+ univ.Sequence.typeId: SequenceOrSetPayloadDecoder(),
+ univ.SequenceOf.typeId: SequenceOfOrSetOfPayloadDecoder(),
+ univ.Choice.typeId: ChoicePayloadDecoder(),
+ univ.Any.typeId: AbstractScalarPayloadDecoder(),
# character string types
- char.UTF8String.typeId: AbstractScalarDecoder(),
- char.NumericString.typeId: AbstractScalarDecoder(),
- char.PrintableString.typeId: AbstractScalarDecoder(),
- char.TeletexString.typeId: AbstractScalarDecoder(),
- char.VideotexString.typeId: AbstractScalarDecoder(),
- char.IA5String.typeId: AbstractScalarDecoder(),
- char.GraphicString.typeId: AbstractScalarDecoder(),
- char.VisibleString.typeId: AbstractScalarDecoder(),
- char.GeneralString.typeId: AbstractScalarDecoder(),
- char.UniversalString.typeId: AbstractScalarDecoder(),
- char.BMPString.typeId: AbstractScalarDecoder(),
+ char.UTF8String.typeId: AbstractScalarPayloadDecoder(),
+ char.NumericString.typeId: AbstractScalarPayloadDecoder(),
+ char.PrintableString.typeId: AbstractScalarPayloadDecoder(),
+ char.TeletexString.typeId: AbstractScalarPayloadDecoder(),
+ char.VideotexString.typeId: AbstractScalarPayloadDecoder(),
+ char.IA5String.typeId: AbstractScalarPayloadDecoder(),
+ char.GraphicString.typeId: AbstractScalarPayloadDecoder(),
+ char.VisibleString.typeId: AbstractScalarPayloadDecoder(),
+ char.GeneralString.typeId: AbstractScalarPayloadDecoder(),
+ char.UniversalString.typeId: AbstractScalarPayloadDecoder(),
+ char.BMPString.typeId: AbstractScalarPayloadDecoder(),
# useful types
- useful.ObjectDescriptor.typeId: AbstractScalarDecoder(),
- useful.GeneralizedTime.typeId: AbstractScalarDecoder(),
- useful.UTCTime.typeId: AbstractScalarDecoder()
+ useful.ObjectDescriptor.typeId: AbstractScalarPayloadDecoder(),
+ useful.GeneralizedTime.typeId: AbstractScalarPayloadDecoder(),
+ useful.UTCTime.typeId: AbstractScalarPayloadDecoder()
}
-class Decoder(object):
+class SingleItemDecoder(object):
+
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
- # noinspection PyDefaultArgument
- def __init__(self, tagMap, typeMap):
- self.__tagMap = tagMap
- self.__typeMap = typeMap
+ def __init__(self, **options):
+ self._tagMap = options.get('tagMap', self.TAG_MAP)
+ self._typeMap = options.get('typeMap', self.TYPE_MAP)
def __call__(self, pyObject, asn1Spec, **options):
if LOG:
debug.scope.push(type(pyObject).__name__)
- LOG('decoder called at scope %s, working with type %s' % (debug.scope, type(pyObject).__name__))
+ LOG('decoder called at scope %s, working with '
+ 'type %s' % (debug.scope, type(pyObject).__name__))
if asn1Spec is None or not isinstance(asn1Spec, base.Asn1Item):
- raise error.PyAsn1Error('asn1Spec is not valid (should be an instance of an ASN.1 Item, not %s)' % asn1Spec.__class__.__name__)
+ raise error.PyAsn1Error(
+ 'asn1Spec is not valid (should be an instance of an ASN.1 '
+ 'Item, not %s)' % asn1Spec.__class__.__name__)
try:
- valueDecoder = self.__typeMap[asn1Spec.typeId]
+ valueDecoder = self._typeMap[asn1Spec.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
baseTagSet = tag.TagSet(asn1Spec.tagSet.baseTag, asn1Spec.tagSet.baseTag)
try:
- valueDecoder = self.__tagMap[baseTagSet]
+ valueDecoder = self._tagMap[baseTagSet]
+
except KeyError:
raise error.PyAsn1Error('Unknown ASN.1 tag %s' % asn1Spec.tagSet)
if LOG:
- LOG('calling decoder %s on Python type %s <%s>' % (type(valueDecoder).__name__, type(pyObject).__name__, repr(pyObject)))
+ LOG('calling decoder %s on Python type %s '
+ '<%s>' % (type(valueDecoder).__name__,
+ type(pyObject).__name__, repr(pyObject)))
value = valueDecoder(pyObject, asn1Spec, self, **options)
if LOG:
- LOG('decoder %s produced ASN.1 type %s <%s>' % (type(valueDecoder).__name__, type(value).__name__, repr(value)))
+ LOG('decoder %s produced ASN.1 type %s '
+ '<%s>' % (type(valueDecoder).__name__,
+ type(value).__name__, repr(value)))
debug.scope.pop()
return value
+class Decoder(object):
+ SINGLE_ITEM_DECODER = SingleItemDecoder
+
+ def __init__(self, **options):
+ self._singleItemDecoder = self.SINGLE_ITEM_DECODER(**options)
+
+ def __call__(self, pyObject, asn1Spec=None, **kwargs):
+ return self._singleItemDecoder(pyObject, asn1Spec=asn1Spec, **kwargs)
+
+
#: Turns Python objects of built-in types into ASN.1 objects.
#:
#: Takes Python objects of built-in types and turns them into a tree of
@@ -210,4 +230,4 @@ class Decoder(object):
#: SequenceOf:
#: 1 2 3
#:
-decode = Decoder(tagMap, typeMap)
+decode = Decoder()
diff --git a/pyasn1/codec/native/encoder.py b/pyasn1/codec/native/encoder.py
index a83d00a..6b8ca6d 100644
--- a/pyasn1/codec/native/encoder.py
+++ b/pyasn1/codec/native/encoder.py
@@ -103,7 +103,7 @@ class AnyEncoder(AbstractItemEncoder):
return value.asOctets()
-tagMap = {
+TAG_MAP = {
univ.Boolean.tagSet: BooleanEncoder(),
univ.Integer.tagSet: IntegerEncoder(),
univ.BitString.tagSet: BitStringEncoder(),
@@ -136,7 +136,7 @@ tagMap = {
# Put in ambiguous & non-ambiguous types for faster codec lookup
-typeMap = {
+TYPE_MAP = {
univ.Boolean.typeId: BooleanEncoder(),
univ.Integer.typeId: IntegerEncoder(),
univ.BitString.typeId: BitStringEncoder(),
@@ -171,48 +171,66 @@ typeMap = {
}
-class Encoder(object):
+class SingleItemEncoder(object):
+
+ TAG_MAP = TAG_MAP
+ TYPE_MAP = TYPE_MAP
- # noinspection PyDefaultArgument
- def __init__(self, tagMap, typeMap={}):
- self.__tagMap = tagMap
- self.__typeMap = typeMap
+ def __init__(self, **options):
+ self._tagMap = options.get('tagMap', self.TAG_MAP)
+ self._typeMap = options.get('typeMap', self.TYPE_MAP)
def __call__(self, value, **options):
if not isinstance(value, base.Asn1Item):
- raise error.PyAsn1Error('value is not valid (should be an instance of an ASN.1 Item)')
+ raise error.PyAsn1Error(
+ 'value is not valid (should be an instance of an ASN.1 Item)')
if LOG:
debug.scope.push(type(value).__name__)
- LOG('encoder called for type %s <%s>' % (type(value).__name__, value.prettyPrint()))
+ LOG('encoder called for type %s '
+ '<%s>' % (type(value).__name__, value.prettyPrint()))
tagSet = value.tagSet
try:
- concreteEncoder = self.__typeMap[value.typeId]
+ concreteEncoder = self._typeMap[value.typeId]
except KeyError:
# use base type for codec lookup to recover untagged types
- baseTagSet = tag.TagSet(value.tagSet.baseTag, value.tagSet.baseTag)
+ baseTagSet = tag.TagSet(
+ value.tagSet.baseTag, value.tagSet.baseTag)
try:
- concreteEncoder = self.__tagMap[baseTagSet]
+ concreteEncoder = self._tagMap[baseTagSet]
except KeyError:
raise error.PyAsn1Error('No encoder for %s' % (value,))
if LOG:
- LOG('using value codec %s chosen by %s' % (concreteEncoder.__class__.__name__, tagSet))
+ LOG('using value codec %s chosen by '
+ '%s' % (concreteEncoder.__class__.__name__, tagSet))
pyObject = concreteEncoder.encode(value, self, **options)
if LOG:
- LOG('encoder %s produced: %s' % (type(concreteEncoder).__name__, repr(pyObject)))
+ LOG('encoder %s produced: '
+ '%s' % (type(concreteEncoder).__name__, repr(pyObject)))
debug.scope.pop()
return pyObject
+class Encoder(object):
+ SINGLE_ITEM_ENCODER = SingleItemEncoder
+
+ def __init__(self, **options):
+ self._singleItemEncoder = self.SINGLE_ITEM_ENCODER(**options)
+
+ def __call__(self, pyObject, asn1Spec=None, **options):
+ return self._singleItemEncoder(
+ pyObject, asn1Spec=asn1Spec, **options)
+
+
#: Turns ASN.1 object into a Python built-in type object(s).
#:
#: Takes any ASN.1 object (e.g. :py:class:`~pyasn1.type.base.PyAsn1Item` derivative)
@@ -248,4 +266,4 @@ class Encoder(object):
#: >>> encode(seq)
#: [1, 2, 3]
#:
-encode = Encoder(tagMap, typeMap)
+encode = SingleItemEncoder()
diff --git a/pyasn1/codec/streaming.py b/pyasn1/codec/streaming.py
new file mode 100644
index 0000000..6d0146b
--- /dev/null
+++ b/pyasn1/codec/streaming.py
@@ -0,0 +1,243 @@
+#
+# This file is part of pyasn1 software.
+#
+# Copyright (c) 2005-2019, Ilya Etingof <etingof@gmail.com>
+# License: http://snmplabs.com/pyasn1/license.html
+#
+import io
+import os
+import sys
+
+from pyasn1 import error
+from pyasn1.type import univ
+
+_PY2 = sys.version_info < (3,)
+
+
+class CachingStreamWrapper(io.IOBase):
+ """Wrapper around non-seekable streams.
+
+ Note that the implementation is tied to the decoder,
+ not checking for dangerous arguments for the sake
+ of performance.
+
+ The read bytes are kept in an internal cache until
+ setting _markedPosition which may reset the cache.
+ """
+ def __init__(self, raw):
+ self._raw = raw
+ self._cache = io.BytesIO()
+ self._markedPosition = 0
+
+ def peek(self, n):
+ result = self.read(n)
+ self._cache.seek(-len(result), os.SEEK_CUR)
+ return result
+
+ def seekable(self):
+ return True
+
+ def seek(self, n=-1, whence=os.SEEK_SET):
+ # Note that this not safe for seeking forward.
+ return self._cache.seek(n, whence)
+
+ def read(self, n=-1):
+ read_from_cache = self._cache.read(n)
+ if n != -1:
+ n -= len(read_from_cache)
+ if not n: # 0 bytes left to read
+ return read_from_cache
+
+ read_from_raw = self._raw.read(n)
+
+ self._cache.write(read_from_raw)
+
+ return read_from_cache + read_from_raw
+
+ @property
+ def markedPosition(self):
+ """Position where the currently processed element starts.
+
+ This is used for back-tracking in SingleItemDecoder.__call__
+ and (indefLen)ValueDecoder and should not be used for other purposes.
+ The client is not supposed to ever seek before this position.
+ """
+ return self._markedPosition
+
+ @markedPosition.setter
+ def markedPosition(self, value):
+ # By setting the value, we ensure we won't seek back before it.
+ # `value` should be the same as the current position
+ # We don't check for this for performance reasons.
+ self._markedPosition = value
+
+ # Whenever we set _marked_position, we know for sure
+ # that we will not return back, and thus it is
+ # safe to drop all cached data.
+ if self._cache.tell() > io.DEFAULT_BUFFER_SIZE:
+ self._cache = io.BytesIO(self._cache.read())
+ self._markedPosition = 0
+
+ def tell(self):
+ return self._cache.tell()
+
+
+def asSeekableStream(substrate):
+ """Convert object to seekable byte-stream.
+
+ Parameters
+ ----------
+ substrate: :py:class:`bytes` or :py:class:`io.IOBase` or :py:class:`univ.OctetString`
+
+ Returns
+ -------
+ : :py:class:`io.IOBase`
+
+ Raises
+ ------
+ : :py:class:`~pyasn1.error.PyAsn1Error`
+ If the supplied substrate cannot be converted to a seekable stream.
+ """
+ if isinstance(substrate, io.BytesIO):
+ return substrate
+
+ elif isinstance(substrate, bytes):
+ return io.BytesIO(substrate)
+
+ elif isinstance(substrate, univ.OctetString):
+ return io.BytesIO(substrate.asOctets())
+
+ try:
+ # Special case: impossible to set attributes on `file` built-in
+ if _PY2 and isinstance(substrate, file):
+ return io.BufferedReader(substrate)
+
+ elif substrate.seekable(): # Will fail for most invalid types
+ return substrate
+
+ else:
+ return CachingStreamWrapper(substrate)
+
+ except AttributeError:
+ raise error.UnsupportedSubstrateError(
+ "Cannot convert " + substrate.__class__.__name__ +
+ " to a seekable bit stream.")
+
+
+def isEndOfStream(substrate):
+ """Check whether we have reached the end of a stream.
+
+ Although it is more effective to read and catch exceptions, this
+ function
+
+ Parameters
+ ----------
+ substrate: :py:class:`IOBase`
+ Stream to check
+
+ Returns
+ -------
+ : :py:class:`bool`
+ """
+ if isinstance(substrate, io.BytesIO):
+ cp = substrate.tell()
+ substrate.seek(0, os.SEEK_END)
+ result = substrate.tell() == cp
+ substrate.seek(cp, os.SEEK_SET)
+ yield result
+
+ else:
+ received = substrate.read(1)
+ if received is None:
+ yield
+
+ if received:
+ substrate.seek(-1, os.SEEK_CUR)
+
+ yield not received
+
+
+def peekIntoStream(substrate, size=-1):
+ """Peek into stream.
+
+ Parameters
+ ----------
+ substrate: :py:class:`IOBase`
+ Stream to read from.
+
+ size: :py:class:`int`
+ How many bytes to peek (-1 = all available)
+
+ Returns
+ -------
+ : :py:class:`bytes` or :py:class:`str`
+ The return type depends on Python major version
+ """
+ if hasattr(substrate, "peek"):
+ received = substrate.peek(size)
+ if received is None:
+ yield
+
+ while len(received) < size:
+ yield
+
+ yield received
+
+ else:
+ current_position = substrate.tell()
+ try:
+ for chunk in readFromStream(substrate, size):
+ yield chunk
+
+ finally:
+ substrate.seek(current_position)
+
+
+def readFromStream(substrate, size=-1, context=None):
+ """Read from the stream.
+
+ Parameters
+ ----------
+ substrate: :py:class:`IOBase`
+ Stream to read from.
+
+ Keyword parameters
+ ------------------
+ size: :py:class:`int`
+ How many bytes to read (-1 = all available)
+
+ context: :py:class:`dict`
+ Opaque caller context will be attached to exception objects created
+ by this function.
+
+ Yields
+ ------
+ : :py:class:`bytes` or :py:class:`str` or :py:class:`SubstrateUnderrunError`
+ Read data or :py:class:`~pyasn1.error.SubstrateUnderrunError`
+ object if no `size` bytes is readily available in the stream. The
+ data type depends on Python major version
+
+ Raises
+ ------
+ : :py:class:`~pyasn1.error.EndOfStreamError`
+ Input stream is exhausted
+ """
+ while True:
+ # this will block unless stream is non-blocking
+ received = substrate.read(size)
+ if received is None: # non-blocking stream can do this
+ yield error.SubstrateUnderrunError(context=context)
+
+ elif not received and size != 0: # end-of-stream
+ raise error.EndOfStreamError(context=context)
+
+ elif len(received) < size:
+ substrate.seek(-len(received), os.SEEK_CUR)
+
+ # behave like a non-blocking stream
+ yield error.SubstrateUnderrunError(context=context)
+
+ else:
+ break
+
+ yield received