laforge has submitted this change. (
https://gerrit.osmocom.org/c/pysim/+/35380?usp=email
)
Change subject: construct/tlv: Pass optional 'context' into construct
decoder/encoder
......................................................................
construct/tlv: Pass optional 'context' into construct decoder/encoder
The context is some opaque dictionary that can be used by the
constructs; let's allow the caller of parse_construct, from_bytes,
from_tlv to specify it.
Also, when decoding a TLV_IE_Collection, pass the decode results of
existing siblings via the construct.
Change-Id: I021016aaa09cddf9d36521c1a54b468ec49ff54d
---
M pySim/construct.py
M pySim/tlv.py
2 files changed, 52 insertions(+), 31 deletions(-)
Approvals:
Jenkins Builder: Verified
laforge: Looks good to me, approved
diff --git a/pySim/construct.py b/pySim/construct.py
index 31caf0e..cef9557 100644
--- a/pySim/construct.py
+++ b/pySim/construct.py
@@ -200,13 +200,16 @@
return r
-def parse_construct(c, raw_bin_data: bytes, length: typing.Optional[int] = None,
exclude_prefix: str = '_'):
+def parse_construct(c, raw_bin_data: bytes, length: typing.Optional[int] = None,
exclude_prefix: str = '_', context: dict = {}):
"""Helper function to wrap around normalize_construct() and
filter_dict()."""
if not length:
length = len(raw_bin_data)
- parsed = c.parse(raw_bin_data, total_len=length)
+ parsed = c.parse(raw_bin_data, total_len=length, **context)
return normalize_construct(parsed)
+def build_construct(c, decoded_data, context: dict = {}):
+ """Helper function to handle total_len."""
+ return c.build(decoded_data, total_len=None, **context)
# here we collect some shared / common definitions of data types
LV = Prefixed(Int8ub, HexAdapter(GreedyBytes))
diff --git a/pySim/tlv.py b/pySim/tlv.py
index c85d92b..6ea7dd1 100644
--- a/pySim/tlv.py
+++ b/pySim/tlv.py
@@ -26,7 +26,7 @@
from pySim.utils import bertlv_parse_one, comprehensiontlv_parse_one
from pySim.utils import bertlv_parse_tag_raw, comprehensiontlv_parse_tag_raw
-from pySim.construct import parse_construct, LV, HexAdapter, BcdAdapter, BitsRFU,
GsmStringAdapter
+from pySim.construct import build_construct, parse_construct, LV, HexAdapter, BcdAdapter,
BitsRFU, GsmStringAdapter
from pySim.exceptions import *
import inspect
@@ -84,15 +84,15 @@
self.decoded = None
self._construct = None
- def to_bytes(self) -> bytes:
+ def to_bytes(self, context: dict = {}) -> bytes:
"""Convert from internal representation to binary bytes. Store
the binary result
in the internal state and return it."""
if self.decoded == None:
do = b''
elif self._construct:
- do = self._construct.build(self.decoded, total_len=None)
+ do = build_construct(self._construct, self.decoded, context)
elif self.__class__._construct:
- do = self.__class__._construct.build(self.decoded, total_len=None)
+ do = build_construct(self.__class__._construct, self.decoded, context)
else:
do = self._to_bytes()
self.encoded = do
@@ -102,16 +102,16 @@
def _to_bytes(self):
raise NotImplementedError('%s._to_bytes' % type(self).__name__)
- def from_bytes(self, do: bytes):
+ def from_bytes(self, do: bytes, context: dict = {}):
"""Convert from binary bytes to internal representation. Store the
decoded result
in the internal state and return it."""
self.encoded = do
if self.encoded == b'':
self.decoded = None
elif self._construct:
- self.decoded = parse_construct(self._construct, do)
+ self.decoded = parse_construct(self._construct, do, context=context)
elif self.__class__._construct:
- self.decoded = parse_construct(self.__class__._construct, do)
+ self.decoded = parse_construct(self.__class__._construct, do,
context=context)
else:
self.decoded = self._from_bytes(do)
return self.decoded
@@ -174,27 +174,27 @@
return False
@abc.abstractmethod
- def to_ie(self) -> bytes:
+ def to_ie(self, context: dict = {}) -> bytes:
"""Convert the internal representation to entire IE including IE
header."""
- def to_bytes(self) -> bytes:
+ def to_bytes(self, context: dict = {}) -> bytes:
"""Convert the internal representation *of the value part* to
binary bytes."""
if self.is_constructed():
# concatenate the encoded IE of all children to form the value part
out = b''
for c in self.children:
- out += c.to_ie()
+ out += c.to_ie(context=context)
return out
else:
- return super().to_bytes()
+ return super().to_bytes(context=context)
- def from_bytes(self, do: bytes):
+ def from_bytes(self, do: bytes, context: dict = {}):
"""Parse *the value part* from binary bytes to internal
representation."""
if self.nested_collection:
- self.children = self.nested_collection.from_bytes(do)
+ self.children = self.nested_collection.from_bytes(do, context=context)
else:
self.children = []
- return super().from_bytes(do)
+ return super().from_bytes(do, context=context)
class TLV_IE(IE):
@@ -226,15 +226,15 @@
"""Encode the length part assuming a certain binary value. Must be
provided by
derived (TLV format specific) class."""
- def to_ie(self):
- return self.to_tlv()
+ def to_ie(self, context: dict = {}):
+ return self.to_tlv(context=context)
- def to_tlv(self):
+ def to_tlv(self, context: dict = {}):
"""Convert the internal representation to binary TLV
bytes."""
- val = self.to_bytes()
+ val = self.to_bytes(context=context)
return self._encode_tag() + self._encode_len(val) + val
- def from_tlv(self, do: bytes):
+ def from_tlv(self, do: bytes, context: dict = {}):
if len(do) == 0:
return {}, b''
(rawtag, remainder) = self.__class__._parse_tag_raw(do)
@@ -248,7 +248,7 @@
else:
value = do
remainder = b''
- dec = self.from_bytes(value)
+ dec = self.from_bytes(value, context=context)
return dec, remainder
@@ -343,7 +343,7 @@
else:
raise TypeError
- def from_bytes(self, binary: bytes) -> List[TLV_IE]:
+ def from_bytes(self, binary: bytes, context: dict = {}) -> List[TLV_IE]:
"""Create a list of TLV_IEs from the collection based on binary
input data.
Args:
binary : binary bytes of encoded data
@@ -357,6 +357,7 @@
first = next(iter(self.members_by_tag.values()))
# iterate until no binary trailer is left
while len(remainder):
+ context['siblings'] = res
# obtain the tag at the start of the remainder
tag, r = first._parse_tag_raw(remainder)
if tag == None:
@@ -365,7 +366,7 @@
cls = self.members_by_tag[tag]
# create an instance and parse accordingly
inst = cls()
- dec, remainder = inst.from_tlv(remainder)
+ dec, remainder = inst.from_tlv(remainder, context=context)
res.append(inst)
else:
# unknown tag; create the related class on-the-fly using the same base
class
@@ -376,7 +377,7 @@
cls._to_bytes = lambda s: bytes.fromhex(s.decoded['raw'])
# create an instance and parse accordingly
inst = cls()
- dec, remainder = inst.from_tlv(remainder)
+ dec, remainder = inst.from_tlv(remainder, context=context)
res.append(inst)
self.children = res
return res
@@ -413,17 +414,18 @@
# self.__class__.__name__, but that is usually some meaningless auto-generated
collection name.
return [x.to_dict() for x in self.children]
- def to_bytes(self):
+ def to_bytes(self, context: dict = {}):
out = b''
+ context['siblings'] = self.children
for c in self.children:
- out += c.to_tlv()
+ out += c.to_tlv(context=context)
return out
- def from_tlv(self, do):
- return self.from_bytes(do)
+ def from_tlv(self, do, context: dict = {}):
+ return self.from_bytes(do, context=context)
- def to_tlv(self):
- return self.to_bytes()
+ def to_tlv(self, context: dict = {}):
+ return self.to_bytes(context=context)
def flatten_dict_lists(inp):
--
To view, visit
https://gerrit.osmocom.org/c/pysim/+/35380?usp=email
To unsubscribe, or for help writing mail filters, visit
https://gerrit.osmocom.org/settings
Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: I021016aaa09cddf9d36521c1a54b468ec49ff54d
Gerrit-Change-Number: 35380
Gerrit-PatchSet: 3
Gerrit-Owner: laforge <laforge(a)osmocom.org>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: dexter <pmaier(a)sysmocom.de>
Gerrit-Reviewer: laforge <laforge(a)osmocom.org>
Gerrit-MessageType: merged