class StructTemplate(_Template): has_length = False def __init__(self, type, name, parent, parameters=None, length=None, align=None): self._parameters = parameters or {} self.type = type if length: self._set_length(length) self._align = int(align or 1) _Template.__init__(self, name, parent) def _set_length(self, length): self.has_length = True self.length = Length(length) def get_static_length(self): return sum(field.get_static_length() for field in self._fields.values()) def decode(self, data, parent=None, name=None, little_endian=False): if self.has_length: length = self.length.decode(parent) data = data[:length] return _Template.decode(self, data, parent, name, little_endian) def encode(self, message_params, parent=None, name=None, little_endian=False): struct = self._get_struct(name, parent) self._add_struct_params(message_params) self._encode_fields(struct, self._get_params_sub_tree(message_params, name), little_endian=little_endian) if self.has_length: length, aligned_length = self.length.find_length_and_set_if_necessary(parent, len(struct)) if len(struct) != length: raise AssertionError('Length of struct %s does not match defined length. defined length:%s Struct:\n%s' % (self.name, length, repr(struct))) return struct # TODO: Cleanup setting the parent to constructor of message -elements def _get_struct(self, name, parent): struct = Struct(name or self.name, self.type, align=self._align) struct._parent = parent return struct def validate(self, parent, message_fields, name=None): self._add_struct_params(message_fields) errors = [] name = name or self.name message = parent[name] if self.has_length: length = self.length.decode(message) if len(message) != length: errors.append('Length of struct %s does not match defined length. defined length:%s struct length:%s' % (message._name, length, len(message))) return errors + _Template.validate(self, message, self._get_params_sub_tree(message_fields, name)) def _add_struct_params(self, params): for key in self._parameters.keys(): params[key] = self._parameters.pop(key) if key not in params else params[key]
def __init__(self, length, name, parent): self.length = Length(length) _Template.__init__(self, name, parent)
class ListTemplate(_Template): param_pattern = re.compile(r'([^.]*?)\[(.*?)\](.*)') has_length = True type = 'List' def __init__(self, length, name, parent): self.length = Length(length) _Template.__init__(self, name, parent) def get_static_length(self): return self.length.value * self.field.get_static_length() def encode(self, message_params, parent, name=None, little_endian=False): name = name or self.name params_subtree = self._get_params_sub_tree(message_params, name) list = self._get_struct(name, parent) for index in range(self.length.decode(parent)): list[str(index)] = self.field.encode(params_subtree, parent, name=str(index), little_endian=little_endian) self._check_params_empty(params_subtree, name) return list @property def field(self): return self._fields.values()[0] def _get_struct(self, name=None, parent=None): ls = List(name or self.name, self.field.type) ls._parent = parent return ls def decode(self, data, parent, name=None, little_endian=False): name = name or self.name message = self._get_struct(name, parent) data_index = 0 # maximum_length is given for free length (*) to limit the absolute maximum number of entries for index in range(0, self.length.decode(parent, maximum_length=len(data))): message[str(index)] = self.field.decode(data[data_index:], message, name=str(index), little_endian=little_endian) data_index += len(message[index]) if self.length.free and data_index == len(data): break return message def validate(self, parent, message_fields, name=None): name = name or self.name params_subtree = self._get_params_sub_tree(message_fields, name) list = parent[name] errors = [] for index in range(list.len): errors += self.field.validate(list, params_subtree, name=str(index)) self._check_params_empty(params_subtree, name) return errors def _get_params_sub_tree(self, params, name=None): result = OrderedDict({'*': params['*']} if '*' in params else {}) name = name or self.name for key in params.keys(): self._consume_params_with_brackets(name, params, result, key) self._consume_dot_syntax(name, params, result, key) return result def _consume_params_with_brackets(self, name, params, result, key): match = self.param_pattern.match(key) if match: prefix, child_name, ending = match.groups() if prefix == name: result[child_name + ending] = params.pop(key) elif prefix == '*': result[child_name + ending] = params[key] def _consume_dot_syntax(self, name, params, result, key): prefix, _, ending = key.partition('.') if prefix == name: result[ending] = params.pop(key) elif prefix == '*' and ending: result[ending] = params[key]
def _set_length(self, length): self.has_length = True self.length = Length(length)