Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support named members decoding #124

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ gmon.out
/*.mk
Cargo.lock
/rust_source.rs
/*.o
/*.o
.idea/
venv/
74 changes: 60 additions & 14 deletions asn1tools/codecs/ber.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
from . import add_error_location
from .compiler import enum_values_as_dict
from .compiler import clean_bit_string_value

from .compiler import named_numbers_as_dict

class Class(object):
UNIVERSAL = 0x00
Expand Down Expand Up @@ -803,10 +803,13 @@ def __repr__(self):

class Integer(Type):

def __init__(self, name):
def __init__(self, name, named_members=None):
super(Integer, self).__init__(name,
'INTEGER',
Tag.INTEGER)
self.named_members = named_members
if named_members is not None:
self.named_members = named_numbers_as_dict(named_members)

@add_error_location
def encode(self, data, encoded):
Expand All @@ -819,8 +822,10 @@ def _decode(self, data, offset):

length, offset = decode_length_definite(data, offset)
end_offset = offset + length

return decode_signed_integer(data[offset:end_offset]), end_offset
decoded = decode_signed_integer(data[offset:end_offset])
if self.named_members:
decoded = self.named_members.get(decoded) or decoded
return decoded, end_offset

def __repr__(self):
return 'Integer({})'.format(self.name)
Expand Down Expand Up @@ -871,12 +876,20 @@ def __repr__(self):

class BitString(PrimitiveOrConstructedType):

def __init__(self, name, has_named_bits):
def __init__(self, name, has_named_bits, named_members):
super(BitString, self).__init__(name,
'BIT STRING',
Tag.BIT_STRING,
self)
self.has_named_bits = has_named_bits
self.named_members = named_members
if has_named_bits and self.named_members:
self.named_members = enum_values_as_dict(self.named_members)
# ensure no gap between named bits
number_of_named_bits = len(self.named_members)
maximum_number_in_named_bits = max([int(key) for key in self.named_members.keys()]) + 1
if maximum_number_in_named_bits > number_of_named_bits:
self.named_members = None

def is_default(self, value):
if self.default is None:
Expand All @@ -886,7 +899,6 @@ def is_default(self, value):
self.has_named_bits)
clean_default = clean_bit_string_value(self.default,
self.has_named_bits)

return clean_value == clean_default

@add_error_location
Expand All @@ -909,12 +921,42 @@ def encode(self, data, encoded):
encoded.append(number_of_unused_bits)
encoded.extend(data)

def get_string_list_representation(self, bytes_array, bits_length):
number_of_named_bits = len(self.named_members)

# convert bytes_array into bit string
bit_string = ''.join(["{:08b}".format(byte) for byte in bytes_array])[:bits_length]
current_bit_string_length = len(bit_string)

if current_bit_string_length < number_of_named_bits:
# insert zeros to left
bit_string = bit_string.rjust(number_of_named_bits, '0')
elif current_bit_string_length > number_of_named_bits:
extra_bits = bit_string[:current_bit_string_length - number_of_named_bits]
is_extra_bits_all_zero = True
for bit in extra_bits:
is_extra_bits_all_zero = is_extra_bits_all_zero and bit == '0'
if is_extra_bits_all_zero:
# clean extra bits
bit_string = bit_string[current_bit_string_length - number_of_named_bits:]
else:
return None

current_bit_string_length = len(bit_string)
string_list = []
for key, val in self.named_members.items():
if bit_string[current_bit_string_length - int(key) - 1] == '1':
string_list.append(val)
return string_list

def decode_primitive_contents(self, data, offset, length):
length -= 1
number_of_bits = 8 * length - data[offset]
offset += 1

return (data[offset:offset + length], number_of_bits)
decoded = data[offset:offset + length]
if self.has_named_bits and self.named_members:
decoded = self.get_string_list_representation(decoded, number_of_bits) or decoded
return decoded, number_of_bits

def decode_constructed_segments(self, segments):
decoded = bytearray()
Expand All @@ -923,8 +965,10 @@ def decode_constructed_segments(self, segments):
for data, length in segments:
decoded.extend(data)
number_of_bits += length

return (bytes(decoded), number_of_bits)
decoded = bytes(decoded)
if self.has_named_bits and self.named_members:
decoded = self.get_string_list_representation(decoded, number_of_bits) or decoded
return decoded, number_of_bits

def __repr__(self):
return 'BitString({})'.format(self.name)
Expand Down Expand Up @@ -1606,7 +1650,8 @@ def compile_implicit_type(self, name, type_descriptor, module_name):
*self.compile_members(type_descriptor['members'],
module_name))
elif type_name == 'INTEGER':
compiled = Integer(name)
named_members = type_descriptor.get('named-numbers') if self.named_members else None
compiled = Integer(name, named_members)
elif type_name == 'REAL':
compiled = Real(name)
elif type_name == 'ENUMERATED':
Expand Down Expand Up @@ -1652,7 +1697,8 @@ def compile_implicit_type(self, name, type_descriptor, module_name):
compiled = DateTime(name)
elif type_name == 'BIT STRING':
has_named_bits = ('named-bits' in type_descriptor)
compiled = BitString(name, has_named_bits)
named_members = type_descriptor.get('named-bits') if self.named_members else None
compiled = BitString(name, has_named_bits, named_members)
elif type_name == 'ANY':
compiled = Any(name)
elif type_name == 'ANY DEFINED BY':
Expand Down Expand Up @@ -1764,8 +1810,8 @@ def compile_extension_member(self,
additions.append(compiled_member)


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(data):
Expand Down
9 changes: 8 additions & 1 deletion asn1tools/codecs/compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,13 +183,14 @@ def decode(self, data):

class Compiler(object):

def __init__(self, specification, numeric_enums=False):
def __init__(self, specification, numeric_enums=False, named_members=False):
self._specification = specification
self._numeric_enums = numeric_enums
self._types_backtrace = []
self.recursive_types = []
self.compiled = {}
self.current_type_descriptor = None
self.named_members = named_members

def types_backtrace_push(self, type_name):
self._types_backtrace.append(type_name)
Expand Down Expand Up @@ -1179,6 +1180,12 @@ def enum_values_as_dict(values):
}


def named_numbers_as_dict(values):
return {
val: key
for key, val in values.items()
}

def enum_values_split(values):
if EXTENSION_MARKER in values:
index = values.index(EXTENSION_MARKER)
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/constraints_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,5 +457,5 @@ def char_range(begin, end):
return ''.join(sorted(value))


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()
5 changes: 2 additions & 3 deletions asn1tools/codecs/der.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,6 @@ def encode(self, data, encoded, values=None):
def _decode(self, data, offset):
length, offset = decode_length_definite(data, offset)
end_offset = offset + length

return decode_signed_integer(data[offset:end_offset]), end_offset

def __repr__(self):
Expand Down Expand Up @@ -459,5 +458,5 @@ def compile_implicit_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()
6 changes: 3 additions & 3 deletions asn1tools/codecs/gser.py
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ def compile_type(self, name, type_descriptor, module_name):
module_name)
compiled = Choice(name, members)
elif type_name == 'INTEGER':
compiled = Integer(name)
compiled = Integer(name)
elif type_name == 'REAL':
compiled = Real(name)
elif type_name == 'ENUMERATED':
Expand Down Expand Up @@ -620,8 +620,8 @@ def compile_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/jer.py
Original file line number Diff line number Diff line change
Expand Up @@ -660,8 +660,8 @@ def compile_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/oer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1502,8 +1502,8 @@ def compile_extension_member(self,
additions.append(compiled_member)


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/per.py
Original file line number Diff line number Diff line change
Expand Up @@ -2287,8 +2287,8 @@ def char_range(begin, end):
return PermittedAlphabet(encode_map, decode_map)


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/type_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -377,5 +377,5 @@ def compile_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()
4 changes: 2 additions & 2 deletions asn1tools/codecs/uper.py
Original file line number Diff line number Diff line change
Expand Up @@ -637,8 +637,8 @@ def compile_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums=False):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums=False, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
4 changes: 2 additions & 2 deletions asn1tools/codecs/xer.py
Original file line number Diff line number Diff line change
Expand Up @@ -801,8 +801,8 @@ def compile_type(self, name, type_descriptor, module_name):
return compiled


def compile_dict(specification, numeric_enums):
return Compiler(specification, numeric_enums).process()
def compile_dict(specification, numeric_enums, named_members=False):
return Compiler(specification, numeric_enums, named_members).process()


def decode_length(_data):
Expand Down
Loading