<p>laforge has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/pysim/+/24035">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">utils: Introduce DataObject representation<br><br>Represents DataObject (DO) in the sense of ISO 7816-4.  Contrary to<br>'normal' TLVs where one simply has any number of different TLVs that may<br>occur in any order at any point, ISO 7816 has the habit of specifying<br>TLV data but with very specific ordering, or specific choices of tags at<br>specific points in a stream.  This is represented by DataObjectChoice,<br>DataObjectCollection and DataObjectSequence classes.<br><br>Change-Id: Iac18e7665481c9323cc7d22a3cd93e3da7869deb<br>---<br>M pySim/utils.py<br>1 file changed, 297 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/35/24035/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/pySim/utils.py b/pySim/utils.py</span><br><span>index a177c56..de41dfb 100644</span><br><span>--- a/pySim/utils.py</span><br><span>+++ b/pySim/utils.py</span><br><span>@@ -863,3 +863,300 @@</span><br><span>       res += fstr % (heading)</span><br><span>      res += "#" * width</span><br><span>         return res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DataObject:</span><br><span style="color: hsl(120, 100%, 40%);">+    """A DataObject (DO) in the sense of ISO 7816-4.  Contrary to 'normal' TLVs where one</span><br><span style="color: hsl(120, 100%, 40%);">+    simply has any number of different TLVs that may occur in any order at any point, ISO 7816</span><br><span style="color: hsl(120, 100%, 40%);">+    has the habit of specifying TLV data but with very spcific ordering, or specific choices of</span><br><span style="color: hsl(120, 100%, 40%);">+    tags at specific points in a stream.  This class tries to represent this"""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, desc = None, tag = None):</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            name: A brief, all-lowercase, underscore separated string identifier</span><br><span style="color: hsl(120, 100%, 40%);">+            desc: A human-readable description of what this DO represents</span><br><span style="color: hsl(120, 100%, 40%);">+            tag : The tag associated with this DO</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        self.name = name</span><br><span style="color: hsl(120, 100%, 40%);">+        self.desc = desc</span><br><span style="color: hsl(120, 100%, 40%);">+        self.tag = tag</span><br><span style="color: hsl(120, 100%, 40%);">+        self.encoded = None</span><br><span style="color: hsl(120, 100%, 40%);">+        self.encoded = None</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.name</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __repr__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%s(%s)' % (self.__class__, self.name)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __or__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """OR-ing DataObjects together renders a DataObjectChoice."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(other, DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+            # DataObject | DataObject = DataObjectChoice</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectChoice(None, members=[self, other])</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __add__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """ADD-ing DataObjects together renders a DataObjectCollection."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(other, DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+            # DataObject + DataObject = DataObjectCollectin</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectCollection(None, members=[self, other])</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def _compute_tag(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Compute the tag (sometimes the tag encodes part of the value)."""</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.tag</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def to_dict(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Return a dict in form "name: decoded_value" """</span><br><span style="color: hsl(120, 100%, 40%);">+        return {self.name: self.decoded}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def from_value(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Parse the value part of the DO into the internal state of this instance."""</span><br><span style="color: hsl(120, 100%, 40%);">+        raise TypeError("The derived classes must defined a from_value() method")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode the internal state of this instance into the TLV value part."""</span><br><span style="color: hsl(120, 100%, 40%);">+        raise TypeError("The derived classes must defined a to_value() method")</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def from_tlv(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Parse binary TLV representation into internal state.  The resulting decoded</span><br><span style="color: hsl(120, 100%, 40%);">+        representation is _not_ returned, but just internalized in the object instance!</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            do : input bytes containing TLV-encoded representation</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            bytes remaining at end of 'do' after parsing one TLV/DO.</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        if do[0] != self.tag:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError('%s: Can only decode tag 0x%02x' % (self, self.tag))</span><br><span style="color: hsl(120, 100%, 40%);">+        length = do[1]</span><br><span style="color: hsl(120, 100%, 40%);">+        val = do[2:2+length]</span><br><span style="color: hsl(120, 100%, 40%);">+        self.from_value(val)</span><br><span style="color: hsl(120, 100%, 40%);">+        # return remaining bytes</span><br><span style="color: hsl(120, 100%, 40%);">+        return do[2+length:]</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def to_tlv(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode internal representation to binary TLV.</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            bytes encoded in TLV format.</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        val = self.to_value()</span><br><span style="color: hsl(120, 100%, 40%);">+        return bytes(self._compute_tag()) + bytes(len(val)) + val</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode a single DOs from the input data.</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            binary : binary bytes of encoded data</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            tuple of (decoded_result, binary_remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        tag = binary[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        if tag != self.tag:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError('%s: Unknown Tag 0x%02x in %s; expected 0x%02x' %</span><br><span style="color: hsl(120, 100%, 40%);">+                             (self, tag, binary, self.tag))</span><br><span style="color: hsl(120, 100%, 40%);">+        remainder = self.from_tlv(binary)</span><br><span style="color: hsl(120, 100%, 40%);">+        return (self.to_dict(), remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return self.to_tlv()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class TL0_DataObject(DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+    """Data Object that has Tag, Len=0 and no Value part."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, desc, tag, val=None):</span><br><span style="color: hsl(120, 100%, 40%);">+        super().__init__(name, desc, tag)</span><br><span style="color: hsl(120, 100%, 40%);">+        self.val = val</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def from_value(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        if len(binary) != 0:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError</span><br><span style="color: hsl(120, 100%, 40%);">+        self.decoded = self.val</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def to_value(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        return b''</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DataObjectCollection:</span><br><span style="color: hsl(120, 100%, 40%);">+    """A DataObjectCollection consits of multiple Data Objects identified by their tags.</span><br><span style="color: hsl(120, 100%, 40%);">+    A given encoded DO may contain any of them in any order, and may contain multiple instances</span><br><span style="color: hsl(120, 100%, 40%);">+    of each DO."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, desc = None, members=[]):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.name = name</span><br><span style="color: hsl(120, 100%, 40%);">+        self.desc = desc</span><br><span style="color: hsl(120, 100%, 40%);">+        self.members = members</span><br><span style="color: hsl(120, 100%, 40%);">+        self.members_by_tag = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        self.members_by_name = {}</span><br><span style="color: hsl(120, 100%, 40%);">+        for m in self.members:</span><br><span style="color: hsl(120, 100%, 40%);">+            self.members_by_tag[m.tag] = m</span><br><span style="color: hsl(120, 100%, 40%);">+            self.members_by_name[m.name] = m</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        member_strs = [str(x) for x in self.members]</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%s(%s)' % (self.name, ','.join(member_strs))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __repr__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        member_strs = [repr(x) for x in self.members]</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%s(%s)' % (self.__class__, ','.join(member_strs))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __add__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Extending DataCollections with other DataCollections or DataObjects."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(other, DataObjectCollection):</span><br><span style="color: hsl(120, 100%, 40%);">+            # adding one collection to another</span><br><span style="color: hsl(120, 100%, 40%);">+            members = self.members + other.members</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectCollection(self.name, self.desc, members)</span><br><span style="color: hsl(120, 100%, 40%);">+        elif isinstance(other, DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+            # adding a member to a collection</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectCollection(self.name, self.desc, self.members + [other])</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode any number of DOs from the collection until the end of the input data,</span><br><span style="color: hsl(120, 100%, 40%);">+        or uninitialized memory (0xFF) is found.</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            binary : binary bytes of encoded data</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            tuple of (decoded_result, binary_remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        res = []</span><br><span style="color: hsl(120, 100%, 40%);">+        remainder = binary</span><br><span style="color: hsl(120, 100%, 40%);">+        # iterate until no binary trailer is left</span><br><span style="color: hsl(120, 100%, 40%);">+        while len(remainder):</span><br><span style="color: hsl(120, 100%, 40%);">+            tag = remainder[0]</span><br><span style="color: hsl(120, 100%, 40%);">+            if tag == 0xff: # uninitialized memory at the end?</span><br><span style="color: hsl(120, 100%, 40%);">+                return (res, remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+            if not tag in self.members_by_tag:</span><br><span style="color: hsl(120, 100%, 40%);">+                raise ValueError('%s: Unknown Tag 0x%02x in %s; expected %s' %</span><br><span style="color: hsl(120, 100%, 40%);">+                                 (self, tag, remainder, self.members_by_tag.keys()))</span><br><span style="color: hsl(120, 100%, 40%);">+            obj = self.members_by_tag[tag]</span><br><span style="color: hsl(120, 100%, 40%);">+            # DO from_tlv returns remainder of binary</span><br><span style="color: hsl(120, 100%, 40%);">+            remainder = obj.from_tlv(remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+            # collect our results</span><br><span style="color: hsl(120, 100%, 40%);">+            res.append(obj.to_dict())</span><br><span style="color: hsl(120, 100%, 40%);">+        return (res, remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode(self, decoded):</span><br><span style="color: hsl(120, 100%, 40%);">+        res = bytearray()</span><br><span style="color: hsl(120, 100%, 40%);">+        for i in decoded:</span><br><span style="color: hsl(120, 100%, 40%);">+            obj = self.members_by_name(i[0])</span><br><span style="color: hsl(120, 100%, 40%);">+            res.append(obj.to_tlv())</span><br><span style="color: hsl(120, 100%, 40%);">+        return res</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DataObjectChoice(DataObjectCollection):</span><br><span style="color: hsl(120, 100%, 40%);">+    """One Data Object from within a choice, identified by its tag.</span><br><span style="color: hsl(120, 100%, 40%);">+    This means that exactly one member of the choice must occur, and which one occurs depends</span><br><span style="color: hsl(120, 100%, 40%);">+    on the tag."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __add__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """We overload the add operator here to avoid inheriting it from DataObjecCollection."""</span><br><span style="color: hsl(120, 100%, 40%);">+        raise TypeError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __or__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """OR-ing a Choice to another choice extends the choice, as does OR-ing a DataObject."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(other, DataObjectChoice):</span><br><span style="color: hsl(120, 100%, 40%);">+            # adding one collection to another</span><br><span style="color: hsl(120, 100%, 40%);">+            members = self.members + other.members</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectChoice(self.name, self.desc, members)</span><br><span style="color: hsl(120, 100%, 40%);">+        elif isinstance(other, DataObject):</span><br><span style="color: hsl(120, 100%, 40%);">+            # adding a member to a collection</span><br><span style="color: hsl(120, 100%, 40%);">+            return DataObjectChoice(self.name, self.desc, self.members + [other])</span><br><span style="color: hsl(120, 100%, 40%);">+        else:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise TypeError</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode a single DOs from the choice based on the tag.</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            binary : binary bytes of encoded data</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            tuple of (decoded_result, binary_remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        tag = binary[0]</span><br><span style="color: hsl(120, 100%, 40%);">+        if tag == 0xff:</span><br><span style="color: hsl(120, 100%, 40%);">+            return (None, binary)</span><br><span style="color: hsl(120, 100%, 40%);">+        if not tag in self.members_by_tag:</span><br><span style="color: hsl(120, 100%, 40%);">+            raise ValueError('%s: Unknown Tag 0x%02x in %s; expected %s' %</span><br><span style="color: hsl(120, 100%, 40%);">+                             (self, tag, binary, self.members_by_tag.keys()))</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = self.members_by_tag[tag]</span><br><span style="color: hsl(120, 100%, 40%);">+        remainder = obj.from_tlv(binary)</span><br><span style="color: hsl(120, 100%, 40%);">+        return (obj.to_dict(), remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode(self, decoded):</span><br><span style="color: hsl(120, 100%, 40%);">+        obj = self.members_by_name(decoded[0])</span><br><span style="color: hsl(120, 100%, 40%);">+        return obj.to_tlv()</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+class DataObjectSequence:</span><br><span style="color: hsl(120, 100%, 40%);">+    """A sequence of DataObjects or DataObjectChoices. This allows us to express a certain</span><br><span style="color: hsl(120, 100%, 40%);">+       ordered sequence of DOs or choices of DOs that have to appear as per the specification.</span><br><span style="color: hsl(120, 100%, 40%);">+       By wrapping them into this formal DataObjectSequence, we can offer convenience methods</span><br><span style="color: hsl(120, 100%, 40%);">+       for encoding or decoding an entire sequence."""</span><br><span style="color: hsl(120, 100%, 40%);">+    def __init__(self, name, desc=None, sequence=[]):</span><br><span style="color: hsl(120, 100%, 40%);">+        self.sequence = sequence</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __str__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        member_strs = [str(x) for x in self.sequence]</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%s(%s)' % (self.name, ','.join(member_strs))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __repr__(self):</span><br><span style="color: hsl(120, 100%, 40%);">+        member_strs = [repr(x) for x in self.sequence]</span><br><span style="color: hsl(120, 100%, 40%);">+        return '%s(%s)' % (self.__class__, ','.join(member_strs))</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    def __add__(self, other):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Add (append) a DataObject or DataObjectChoice to the sequence."""</span><br><span style="color: hsl(120, 100%, 40%);">+        if isinstance(other, 'DataObject'):</span><br><span style="color: hsl(120, 100%, 40%);">+                return DataObjectSequence(self.name, self.desc, self.sequence + [other])</span><br><span style="color: hsl(120, 100%, 40%);">+        elif isinstance(other, 'DataObjectChoice'):</span><br><span style="color: hsl(120, 100%, 40%);">+                return DataObjectSequence(self.name, self.desc, self.sequence + [other])</span><br><span style="color: hsl(120, 100%, 40%);">+        elif isinstance(other, 'DataObjectSequence'):</span><br><span style="color: hsl(120, 100%, 40%);">+                return DataObjectSeuqence(self.name, self.desc, self.sequence + other.sequence)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode(self, binary:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode a sequence by calling the decoder of each element in the sequence.</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            binary : binary bytes of encoded data</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            tuple of (decoded_result, binary_remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        remainder = binary</span><br><span style="color: hsl(120, 100%, 40%);">+        res = []</span><br><span style="color: hsl(120, 100%, 40%);">+        for e in self.sequence:</span><br><span style="color: hsl(120, 100%, 40%);">+            (r, remainder) = e.decode(remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+            if r:</span><br><span style="color: hsl(120, 100%, 40%);">+                res.append(r)</span><br><span style="color: hsl(120, 100%, 40%);">+        return (res, remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def decode_multi(self, do:bytes):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Decode multiple occurrences of the sequence from the binary input data.</span><br><span style="color: hsl(120, 100%, 40%);">+        Args:</span><br><span style="color: hsl(120, 100%, 40%);">+            do : binary input data to be decoded</span><br><span style="color: hsl(120, 100%, 40%);">+        Returns:</span><br><span style="color: hsl(120, 100%, 40%);">+            list of results of the decoder of this sequences</span><br><span style="color: hsl(120, 100%, 40%);">+        """</span><br><span style="color: hsl(120, 100%, 40%);">+        remainder = do</span><br><span style="color: hsl(120, 100%, 40%);">+        res = []</span><br><span style="color: hsl(120, 100%, 40%);">+        while len(remainder):</span><br><span style="color: hsl(120, 100%, 40%);">+            (r, remainder2) = self.decode(remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+            if r:</span><br><span style="color: hsl(120, 100%, 40%);">+                res.append(r)</span><br><span style="color: hsl(120, 100%, 40%);">+            if len(remainder2) < len(remainder):</span><br><span style="color: hsl(120, 100%, 40%);">+                remainder = remainder2</span><br><span style="color: hsl(120, 100%, 40%);">+            else:</span><br><span style="color: hsl(120, 100%, 40%);">+                remainder = remainder2</span><br><span style="color: hsl(120, 100%, 40%);">+                break</span><br><span style="color: hsl(120, 100%, 40%);">+        return (res, remainder)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    # 'codec' interface</span><br><span style="color: hsl(120, 100%, 40%);">+    def encode(self, decoded):</span><br><span style="color: hsl(120, 100%, 40%);">+        """Encode a sequence by calling the encoder of each element in the sequence."""</span><br><span style="color: hsl(120, 100%, 40%);">+        encoded = bytearray()</span><br><span style="color: hsl(120, 100%, 40%);">+        i = 0</span><br><span style="color: hsl(120, 100%, 40%);">+        for e in self.sequence:</span><br><span style="color: hsl(120, 100%, 40%);">+            encoded += e.encode(decoded[i])</span><br><span style="color: hsl(120, 100%, 40%);">+            i += 1</span><br><span style="color: hsl(120, 100%, 40%);">+        return encoded</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/pysim/+/24035">change 24035</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/pysim/+/24035"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: pysim </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: Iac18e7665481c9323cc7d22a3cd93e3da7869deb </div>
<div style="display:none"> Gerrit-Change-Number: 24035 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: laforge <laforge@osmocom.org> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>