def substitute_len_field(cls, descriptor, container_name, container_tp): index, field = ifilter(lambda x: x[1][0] is container_tp._BOUND, enumerate(descriptor)).next() name, tp = field bound_shift = container_tp._BOUND_SHIFT if tp._OPTIONAL: raise ProphyError("array must not be bound to optional field") if not issubclass(tp, (int, long)): raise ProphyError("array must be bound to an unsigned integer") class container_len(tp): _BOUND = container_name @staticmethod def _encode(value, endianness): return tp._encode(value + bound_shift, endianness) @staticmethod def _decode(data, pos, endianness): value, size = tp._decode(data, pos, endianness) array_guard = 65536 if value > array_guard: raise ProphyError("decoded array length over %s" % array_guard) value -= bound_shift if value < 0: raise ProphyError("decoded array length smaller than shift") return value, size descriptor[index] = (name, container_len) delattr(cls, name)
def _check(value): if not isinstance(value, str): raise ProphyError("not a str") if size and len(value) > size: raise ProphyError("too long") if size and not bound: return value.ljust(size, '\x00') return value
def _decode(data, pos, endianness): value, size = tp._decode(data, pos, endianness) array_guard = 65536 if value > array_guard: raise ProphyError("decoded array length over %s" % array_guard) value -= bound_shift if value < 0: raise ProphyError("decoded array length smaller than shift") return value, size
def validate_union(descriptor): if any(type._DYNAMIC for _, type, _ in descriptor): raise ProphyError("dynamic types not allowed in union") if any(type._BOUND for _, type, _ in descriptor): raise ProphyError("bound array/bytes not allowed in union") if any( issubclass(type, container.base_array) for _, type, _ in descriptor): raise ProphyError("static array not implemented in union") if any(type._OPTIONAL for _, type, _ in descriptor): raise ProphyError("union with optional field disallowed")
def check(value): if isinstance(value, str): value = name_to_int.get(value) if value is None: raise ProphyError("unknown enumerator name") return cls(value) elif isinstance(value, (int, long)): if not value in int_to_name: raise ProphyError("unknown enumerator value") return cls(value) else: raise ProphyError("neither string nor int")
def _decode(data, pos, len_hint): if (len(data) - pos) < size: raise ProphyError("too few bytes to decode string") if size and not bound: return data[pos:pos + size], size elif size and bound: return data[pos:pos + len_hint], size elif bound: if (len(data) - pos) < len_hint: raise ProphyError("too few bytes to decode string") return data[pos:pos + len_hint], len_hint else: # greedy return data[pos:], (len(data) - pos)
def bytes(**kwargs): size = kwargs.pop("size", 0) bound = kwargs.pop("bound", None) shift = kwargs.pop("shift", 0) if shift and (not bound or size): raise ProphyError("only shifting bound bytes implemented") if kwargs: raise ProphyError("unknown arguments to bytes field") class _bytes(str): _SIZE = size _DYNAMIC = not size _UNLIMITED = not size and not bound _DEFAULT = "\x00" * size if size and not bound else "" _OPTIONAL = False _ALIGNMENT = 1 _BOUND = bound _BOUND_SHIFT = shift _PARTIAL_ALIGNMENT = None @staticmethod def _check(value): if not isinstance(value, str): raise ProphyError("not a str") if size and len(value) > size: raise ProphyError("too long") if size and not bound: return value.ljust(size, '\x00') return value @staticmethod def _encode(value): return value.ljust(size, '\x00') @staticmethod def _decode(data, pos, len_hint): if (len(data) - pos) < size: raise ProphyError("too few bytes to decode string") if size and not bound: return data[pos:pos + size], size elif size and bound: return data[pos:pos + len_hint], size elif bound: if (len(data) - pos) < len_hint: raise ProphyError("too few bytes to decode string") return data[pos:pos + len_hint], len_hint else: # greedy return data[pos:], (len(data) - pos) return _bytes
def optional(cls): if issubclass(cls, str): raise ProphyError("optional bytes not implemented") if issubclass(cls, container.base_array): raise ProphyError("optional array not implemented") if cls._DYNAMIC: raise ProphyError("optional dynamic fields not implemented") class _optional(cls): pass _optional._OPTIONAL = True _optional._optional_type = scalar.u32 return _optional
def _decode_impl(self, data, pos, endianness, terminal): disc, bytes_read = self._discriminator_type._decode( data, pos, endianness) field = get_discriminated_field(self, disc) if not field: raise ProphyError("unknown discriminator") name, type, _, _, decode_ = field self._discriminated = field bytes_read += decode_(self, name, type, data, pos + bytes_read, endianness, {}) if (len(data) - pos) < self._SIZE: raise ProphyError("not enough bytes") if terminal and (len(data) - pos) > self._SIZE: raise ProphyError("not all bytes read") return self._SIZE
def setter(self, new_value): if new_value is True: self._fields[name] = tp() elif new_value is None: self._fields.pop(name, None) else: raise ProphyError("assignment to composite field not allowed")
def composite_array_eq(self, other): if self is other: return True if not isinstance(other, self.__class__): raise ProphyError('Can only compare repeated composite fields against ' 'other repeated composite fields.') return self._values == other._values
def add(self): if self._max_len and len(self) == self._max_len: raise ProphyError("exceeded array limit") new_element = self._TYPE() self._values.append(new_element) return new_element
def getter(self): if self._discriminated is not field: raise ProphyError("currently field %s is discriminated" % self._discriminated[2]) value = self._fields.get(name) if value is None: value = type() value = self._fields.setdefault(name, value) return value
def extend(self, elem_seq): if self._max_len and len(self) + len(elem_seq) > self._max_len: raise ProphyError("exceeded array limit") composite_cls = self._TYPE for message in elem_seq: new_element = composite_cls() new_element.copy_from(message) self._values.append(new_element)
def setter(self, new_value): field = next( ifilter(lambda x: new_value in (x[0], x[2]), self._descriptor), None) if field: if field != self._discriminated: self._discriminated = field self._fields = {} else: raise ProphyError("unknown discriminator")
def _decode_impl(self, data, pos, endianness, len_hint): if self._SIZE > (len(data) - pos): raise ProphyError("too few bytes to decode array") del self[:] cursor = 0 if not self._SIZE and not self._BOUND: while (pos + cursor) < len(data): cursor += self.add()._decode_impl(data, pos + cursor, endianness, terminal = False) else: for _ in xrange(len_hint): cursor += self.add()._decode_impl(data, pos + cursor, endianness, terminal = False) return max(cursor, self._SIZE)
def _decode_impl(self, data, pos, endianness, terminal): len_hints = {} start_pos = pos for name, tp, _, decode_ in self._descriptor: pos += self._get_padding_size(pos, tp._ALIGNMENT) pos += decode_(self, name, tp, data, pos, endianness, len_hints) if tp._PARTIAL_ALIGNMENT: pos += self._get_padding_size(pos, tp._PARTIAL_ALIGNMENT) if not (self._descriptor and terminal and issubclass(tp, (container.base_array, str))): pos += self._get_padding_size(pos, self._ALIGNMENT) if terminal and pos < len(data): raise ProphyError("not all bytes read") return pos - start_pos
def array(type, **kwargs): size = kwargs.pop("size", 0) bound = kwargs.pop("bound", None) shift = kwargs.pop("shift", 0) if kwargs: raise ProphyError("unknown arguments to array field") if issubclass(type, base_array): raise ProphyError("array of arrays not allowed") if issubclass(type, str): raise ProphyError("array of strings not allowed") if size and type._DYNAMIC: raise ProphyError("static/limited array of dynamic type not allowed") if shift and (not bound or size): raise ProphyError("only shifting bound array implemented") if type._UNLIMITED: raise ProphyError("array with unlimited field disallowed") if type._OPTIONAL: raise ProphyError("array of optional type not allowed") is_static = size and not bound is_composite = issubclass(type, (composite.struct, composite.union)) if is_composite: base = fixed_composite_array if is_static else bound_composite_array else: base = fixed_scalar_array if is_static else bound_scalar_array class _array(base): __slots__ = [] _max_len = size _TYPE = type _SIZE = size * type._SIZE _DYNAMIC = not size _UNLIMITED = not size and not bound _OPTIONAL = False _ALIGNMENT = type._ALIGNMENT _BOUND = bound _BOUND_SHIFT = shift _PARTIAL_ALIGNMENT = None return _array
def add_enum_attributes(cls, enumerators): @staticmethod def check(value): if isinstance(value, str): value = name_to_int.get(value) if value is None: raise ProphyError("unknown enumerator name") return cls(value) elif isinstance(value, (int, long)): if not value in int_to_name: raise ProphyError("unknown enumerator value") return cls(value) else: raise ProphyError("neither string nor int") name_to_int = {name: value for name, value in enumerators} int_to_name = {value: name for name, value in enumerators} if len(name_to_int) < len(enumerators): raise ProphyError("names overlap") map(cls._check, (value for _, value in enumerators)) cls._DEFAULT = cls(enumerators[0][1]) cls._name_to_int = name_to_int cls._int_to_name = int_to_name cls._check = check
def check(value): if not isinstance(value, (float, int, long)): raise ProphyError("not a float") return value
def check(value): if not isinstance(value, (int, long)): raise ProphyError("not an int") if not min <= value <= max: raise ProphyError("out of bounds") return value
def validate(descriptor): if any(type._UNLIMITED for _, type in descriptor[:-1]): raise ProphyError("unlimited field is not the last one")
def setter(self, new_value): raise ProphyError("assignment to array field not allowed")
def decode(data, pos, endianness): if (len(data) - pos) < size: raise ProphyError("too few bytes to decode integer") value, = struct.unpack(endianness + id, data[pos:pos + size]) return value, size
def insert(self, idx, value): value = self._TYPE._check(value) if self._max_len and len(self) == self._max_len: raise ProphyError("exceeded array limit") self._values.insert(idx, value)
def append(self, value): value = self._TYPE._check(value) if self._max_len and len(self) == self._max_len: raise ProphyError("exceeded array limit") self._values.append(value)
def __setslice__(self, start, stop, values): if len(self._values[start:stop]) != len(values): raise ProphyError("setting slice with different length collection") self._values[start:stop] = map(self._TYPE._check, values)
def extend(self, values): if not values: return if self._max_len and len(self) + len(values) > self._max_len: raise ProphyError("exceeded array limit") self._values.extend(map(self._TYPE._check, values))
def __setslice__(self, start, stop, values): if self._max_len and len(self) + len(values) - len(self._values[start:stop]) > self._max_len: raise ProphyError("exceeded array limit") self._values[start:stop] = map(self._TYPE._check, values)
def _decode_impl(self, data, pos, endianness, len_hint): if self._SIZE > (len(data) - pos): raise ProphyError("too few bytes to decode array") self[:], size = decode_scalar_array(self._TYPE, data, pos, endianness, len_hint) return max(size, self._SIZE)