<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>