| # Copyright 2014 The Chromium Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """ |
| The descriptors used to define generated elements of the mojo python bindings. |
| """ |
| |
| import array |
| import itertools |
| import struct |
| |
| import mojo_bindings.reflection as reflection |
| import mojo_bindings.serialization as serialization |
| |
| # pylint: disable=E0611,F0401 |
| import mojo_system |
| |
| |
| class Type(object): |
| """Describes the type of a struct field or a method parameter,""" |
| |
| def Convert(self, value): # pylint: disable=R0201 |
| """ |
| Convert the given value into its canonical representation, raising an |
| exception if the value cannot be converted. |
| """ |
| return value |
| |
| def GetDefaultValue(self, value): |
| """ |
| Returns the default value for this type associated with the given value. |
| This method must be able to correcly handle value being None. |
| """ |
| return self.Convert(value) |
| |
| def IsUnion(self): |
| """ |
| Returns true if the type is a union. This is necessary to be able to |
| identify a union when descriptor.py cannot be imported. |
| """ |
| return False |
| |
| |
| class SerializableType(Type): |
| """Describe a type that can be serialized by itself.""" |
| |
| def __init__(self, typecode): |
| Type.__init__(self) |
| self.typecode = typecode |
| self.byte_size = struct.calcsize('<%s' % self.GetTypeCode()) |
| |
| def GetTypeCode(self): |
| """ |
| Returns the type code (as defined by the struct module) used to encode |
| this type. |
| """ |
| return self.typecode |
| |
| def GetByteSize(self): |
| """ |
| Returns the size of the encoding of this type. |
| """ |
| return self.byte_size |
| |
| def GetAlignment(self): |
| """ |
| Returns the alignment required by the encoding of this type. By default it |
| is set to the byte size of the biggest packed value. |
| """ |
| return max([struct.calcsize('<%s' % c) for c in self.GetTypeCode()]) |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| """ |
| Serialize a value of this type. |
| |
| Args: |
| value: the value to serialize. |
| data_offset: the offset to the end of the data bytearray. Used to encode |
| pointers. |
| data: the bytearray to append additional data to. |
| handle_offset: the offset to use to encode handles. |
| |
| Returns a a tuple where the first element is the value to encode, and the |
| second is the array of handles to add to the message. |
| """ |
| raise NotImplementedError() |
| |
| def Deserialize(self, value, context): |
| """ |
| Deserialize a value of this type. |
| |
| Args: |
| value: the base value for this type. This is always a numeric type, and |
| corresponds to the first element in the tuple returned by |
| Serialize. |
| data: the bytearray to retrieve additional data from. |
| handles: the array of handles contained in the message to deserialize. |
| |
| Returns the deserialized value. |
| """ |
| raise NotImplementedError() |
| |
| |
| class BooleanType(Type): |
| """Type object for booleans""" |
| |
| def Convert(self, value): |
| return bool(value) |
| |
| |
| class NumericType(SerializableType): |
| """Base Type object for all numeric types""" |
| |
| def GetDefaultValue(self, value): |
| if value is None: |
| return self.Convert(0) |
| return self.Convert(value) |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| return (value, []) |
| |
| def Deserialize(self, value, context): |
| return value |
| |
| |
| class IntegerType(NumericType): |
| """Type object for integer types.""" |
| |
| def __init__(self, typecode): |
| NumericType.__init__(self, typecode) |
| size = 8 * self.byte_size |
| signed = typecode.islower() |
| if signed: |
| self._min_value = -(1 << (size - 1)) |
| self._max_value = (1 << (size - 1)) - 1 |
| else: |
| self._min_value = 0 |
| self._max_value = (1 << size) - 1 |
| |
| def Convert(self, value): |
| if value is None: |
| raise TypeError('None is not an integer.') |
| if not isinstance(value, (int, long)): |
| raise TypeError('%r is not an integer type' % value) |
| if value < self._min_value or value > self._max_value: |
| raise OverflowError('%r is not in the range [%d, %d]' % |
| (value, self._min_value, self._max_value)) |
| return value |
| |
| |
| class FloatType(NumericType): |
| """Type object for floating point number types.""" |
| |
| def Convert(self, value): |
| if value is None: |
| raise TypeError('None is not an floating point number.') |
| if not isinstance(value, (int, long, float)): |
| raise TypeError('%r is not a numeric type' % value) |
| return float(value) |
| |
| |
| class UnionType(SerializableType): |
| """Base Type object for union.""" |
| |
| def __init__(self, union_type_getter, nullable=False): |
| SerializableType.__init__(self, 'IIQ') |
| self.nullable = nullable |
| self._union_type_getter = union_type_getter |
| self._union_type = None |
| |
| def IsUnion(self): |
| return True |
| |
| @property |
| def union_type(self): |
| if not self._union_type: |
| self._union_type = self._union_type_getter() |
| return self._union_type |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| if not value: |
| if not self.nullable: |
| raise serialization.SerializationException( |
| 'Trying to serialize null for non nullable type.') |
| return ((0, 0, 0), []) |
| |
| ((size, tag, entry, new_data), new_handles) = ( |
| value.SerializeInline(handle_offset)) |
| if len(new_data) > 0: |
| data.extend(new_data) |
| entry = data_offset - 8 |
| |
| return ((size, tag, entry), new_handles) |
| |
| def Deserialize(self, value, context): |
| result = self.union_type.Deserialize(context) |
| if not result and not self.nullable: |
| raise serialization.DeserializationException( |
| 'Trying to deserialize null for non nullable type.') |
| return result |
| |
| |
| class PointerType(SerializableType): |
| """Base Type object for pointers.""" |
| |
| def __init__(self, nullable=False): |
| SerializableType.__init__(self, 'Q') |
| self.nullable = nullable |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| if value is None and not self.nullable: |
| raise serialization.SerializationException( |
| 'Trying to serialize null for non nullable type.') |
| if value is None: |
| return (0, []) |
| return self.SerializePointer(value, data_offset, data, handle_offset) |
| |
| def Deserialize(self, value, context): |
| if value == 0: |
| if not self.nullable: |
| raise serialization.DeserializationException( |
| 'Trying to deserialize null for non nullable type.') |
| return None |
| if value % 8 != 0: |
| raise serialization.DeserializationException( |
| 'Pointer alignment is incorrect.') |
| sub_context = context.GetSubContext(value) |
| if len(sub_context.data) < serialization.HEADER_STRUCT.size: |
| raise serialization.DeserializationException( |
| 'Available data too short to contain header.') |
| (size, nb_elements) = serialization.HEADER_STRUCT.unpack_from( |
| sub_context.data) |
| if len(sub_context.data) < size or size < serialization.HEADER_STRUCT.size: |
| raise serialization.DeserializationException('Header size is incorrect.') |
| sub_context.ClaimMemory(0, size) |
| return self.DeserializePointer(size, nb_elements, sub_context) |
| |
| def SerializePointer(self, value, data_offset, data, handle_offset): |
| """Serialize the not null value.""" |
| raise NotImplementedError() |
| |
| def DeserializePointer(self, size, nb_elements, context): |
| raise NotImplementedError() |
| |
| |
| class StringType(PointerType): |
| """ |
| Type object for strings. |
| |
| Strings are represented as unicode, and the conversion is done using the |
| default encoding if a string instance is used. |
| """ |
| |
| def __init__(self, nullable=False): |
| PointerType.__init__(self, nullable) |
| self._array_type = NativeArrayType('B', nullable) |
| |
| def Convert(self, value): |
| if value is None or isinstance(value, unicode): |
| return value |
| if isinstance(value, str): |
| return unicode(value) |
| raise TypeError('%r is not a string' % value) |
| |
| def SerializePointer(self, value, data_offset, data, handle_offset): |
| string_array = array.array('b') |
| string_array.fromstring(value.encode('utf8')) |
| return self._array_type.SerializeArray( |
| string_array, data_offset, data, handle_offset) |
| |
| def DeserializePointer(self, size, nb_elements, context): |
| string_array = self._array_type.DeserializeArray(size, nb_elements, context) |
| return unicode(string_array.tostring(), 'utf8') |
| |
| |
| class BaseHandleType(SerializableType): |
| """Type object for handles.""" |
| |
| def __init__(self, nullable=False, type_code='i'): |
| SerializableType.__init__(self, type_code) |
| self.nullable = nullable |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| handle = self.ToHandle(value) |
| if not handle.IsValid() and not self.nullable: |
| raise serialization.SerializationException( |
| 'Trying to serialize null for non nullable type.') |
| if not handle.IsValid(): |
| return (-1, []) |
| return (handle_offset, [handle]) |
| |
| def Deserialize(self, value, context): |
| if value == -1: |
| if not self.nullable: |
| raise serialization.DeserializationException( |
| 'Trying to deserialize null for non nullable type.') |
| return self.FromHandle(mojo_system.Handle()) |
| return self.FromHandle(context.ClaimHandle(value)) |
| |
| def FromHandle(self, handle): |
| raise NotImplementedError() |
| |
| def ToHandle(self, value): |
| raise NotImplementedError() |
| |
| |
| class HandleType(BaseHandleType): |
| """Type object for handles.""" |
| |
| def Convert(self, value): |
| if value is None: |
| return mojo_system.Handle() |
| if not isinstance(value, mojo_system.Handle): |
| raise TypeError('%r is not a handle' % value) |
| return value |
| |
| def FromHandle(self, handle): |
| return handle |
| |
| def ToHandle(self, value): |
| return value |
| |
| |
| class InterfaceRequestType(BaseHandleType): |
| """Type object for interface requests.""" |
| |
| def Convert(self, value): |
| if value is None: |
| return reflection.InterfaceRequest(mojo_system.Handle()) |
| if not isinstance(value, reflection.InterfaceRequest): |
| raise TypeError('%r is not an interface request' % value) |
| return value |
| |
| def FromHandle(self, handle): |
| return reflection.InterfaceRequest(handle) |
| |
| def ToHandle(self, value): |
| return value.PassMessagePipe() |
| |
| |
| class InterfaceType(BaseHandleType): |
| """Type object for interfaces.""" |
| |
| def __init__(self, interface_getter, nullable=False): |
| # handle (4 bytes) + version (4 bytes) |
| BaseHandleType.__init__(self, nullable, 'iI') |
| self._interface_getter = interface_getter |
| self._interface = None |
| |
| def Convert(self, value): |
| if value is None or isinstance(value, self.interface): |
| return value |
| raise TypeError('%r is not an instance of ' % self.interface) |
| |
| @property |
| def interface(self): |
| if not self._interface: |
| self._interface = self._interface_getter() |
| return self._interface |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| (encoded_handle, handles) = super(InterfaceType, self).Serialize( |
| value, data_offset, data, handle_offset) |
| if encoded_handle == -1: |
| version = 0 |
| else: |
| version = self.interface.manager.version |
| if value and isinstance(value, reflection.InterfaceProxy): |
| version = value.manager.version |
| return ((encoded_handle, version), handles) |
| |
| def Deserialize(self, value, context): |
| proxy = super(InterfaceType, self).Deserialize(value[0], context) |
| if proxy: |
| proxy.manager.version = value[1] |
| return proxy |
| |
| def FromHandle(self, handle): |
| if handle.IsValid(): |
| return self.interface.manager.Proxy(handle) |
| return None |
| |
| def ToHandle(self, value): |
| if not value: |
| return mojo_system.Handle() |
| if isinstance(value, reflection.InterfaceProxy): |
| return value.manager.PassMessagePipe() |
| pipe = mojo_system.MessagePipe() |
| self.interface.manager.Bind(value, pipe.handle0) |
| return pipe.handle1 |
| |
| |
| class BaseArrayType(PointerType): |
| """Abstract Type object for arrays.""" |
| |
| def __init__(self, nullable=False, length=0): |
| PointerType.__init__(self, nullable) |
| self.length = length |
| |
| def SerializePointer(self, value, data_offset, data, handle_offset): |
| if self.length != 0 and len(value) != self.length: |
| raise serialization.SerializationException('Incorrect array size') |
| return self.SerializeArray(value, data_offset, data, handle_offset) |
| |
| def SerializeArray(self, value, data_offset, data, handle_offset): |
| """Serialize the not null array.""" |
| raise NotImplementedError() |
| |
| def DeserializePointer(self, size, nb_elements, context): |
| if self.length != 0 and nb_elements != self.length: |
| raise serialization.DeserializationException('Incorrect array size') |
| if (size < |
| serialization.HEADER_STRUCT.size + self.SizeForLength(nb_elements)): |
| raise serialization.DeserializationException('Incorrect array size') |
| return self.DeserializeArray(size, nb_elements, context) |
| |
| def DeserializeArray(self, size, nb_elements, context): |
| raise NotImplementedError() |
| |
| def SizeForLength(self, nb_elements): |
| raise NotImplementedError() |
| |
| |
| class BooleanArrayType(BaseArrayType): |
| |
| def __init__(self, nullable=False, length=0): |
| BaseArrayType.__init__(self, nullable, length) |
| self._array_type = NativeArrayType('B', nullable) |
| |
| def Convert(self, value): |
| if value is None: |
| return value |
| return [TYPE_BOOL.Convert(x) for x in value] |
| |
| def SerializeArray(self, value, data_offset, data, handle_offset): |
| groups = [value[i:i+8] for i in range(0, len(value), 8)] |
| converted = array.array('B', [_ConvertBooleansToByte(x) for x in groups]) |
| return _SerializeNativeArray(converted, data_offset, data, len(value)) |
| |
| def DeserializeArray(self, size, nb_elements, context): |
| converted = self._array_type.DeserializeArray(size, nb_elements, context) |
| elements = list(itertools.islice( |
| itertools.chain.from_iterable( |
| [_ConvertByteToBooleans(x, 8) for x in converted]), |
| 0, |
| nb_elements)) |
| return elements |
| |
| def SizeForLength(self, nb_elements): |
| return (nb_elements + 7) // 8 |
| |
| |
| class GenericArrayType(BaseArrayType): |
| """Type object for arrays of pointers.""" |
| |
| def __init__(self, sub_type, nullable=False, length=0): |
| BaseArrayType.__init__(self, nullable, length) |
| assert isinstance(sub_type, SerializableType) |
| self.sub_type = sub_type |
| |
| def Convert(self, value): |
| if value is None: |
| return value |
| return [self.sub_type.Convert(x) for x in value] |
| |
| def SerializeArray(self, value, data_offset, data, handle_offset): |
| size = (serialization.HEADER_STRUCT.size + |
| self.sub_type.GetByteSize() * len(value)) |
| data_end = len(data) |
| position = len(data) + serialization.HEADER_STRUCT.size |
| data.extend(bytearray(size + |
| serialization.NeededPaddingForAlignment(size))) |
| returned_handles = [] |
| to_pack = [] |
| for item in value: |
| (new_data, new_handles) = self.sub_type.Serialize( |
| item, |
| len(data) - position, |
| data, |
| handle_offset + len(returned_handles)) |
| to_pack.extend(serialization.Flatten(new_data)) |
| returned_handles.extend(new_handles) |
| position = position + self.sub_type.GetByteSize() |
| |
| serialization.HEADER_STRUCT.pack_into(data, data_end, size, len(value)) |
| # TODO(azani): Refactor so we don't have to create big formatting strings. |
| struct.pack_into(('%s' % self.sub_type.GetTypeCode()) * len(value), |
| data, |
| data_end + serialization.HEADER_STRUCT.size, |
| *to_pack) |
| return (data_offset, returned_handles) |
| |
| def DeserializeArray(self, size, nb_elements, context): |
| # TODO(azani): Refactor so the format string isn't so big. |
| values = struct.unpack_from( |
| nb_elements * self.sub_type.GetTypeCode(), |
| buffer(context.data, serialization.HEADER_STRUCT.size)) |
| values_per_element = len(self.sub_type.GetTypeCode()) |
| assert nb_elements * values_per_element == len(values) |
| |
| result = [] |
| sub_context = context.GetSubContext(serialization.HEADER_STRUCT.size) |
| for index in xrange(nb_elements): |
| if values_per_element == 1: |
| value = values[index] |
| else: |
| value = tuple(values[index * values_per_element : |
| (index + 1) * values_per_element]) |
| result.append(self.sub_type.Deserialize( |
| value, |
| sub_context)) |
| sub_context = sub_context.GetSubContext(self.sub_type.GetByteSize()) |
| return result |
| |
| def SizeForLength(self, nb_elements): |
| return nb_elements * self.sub_type.GetByteSize(); |
| |
| |
| class NativeArrayType(BaseArrayType): |
| """Type object for arrays of native types.""" |
| |
| def __init__(self, typecode, nullable=False, length=0): |
| BaseArrayType.__init__(self, nullable, length) |
| self.array_typecode = typecode |
| self.element_size = struct.calcsize('<%s' % self.array_typecode) |
| |
| def Convert(self, value): |
| if value is None: |
| return value |
| if (isinstance(value, array.array) and |
| value.array_typecode == self.array_typecode): |
| return value |
| return array.array(self.array_typecode, value) |
| |
| def SerializeArray(self, value, data_offset, data, handle_offset): |
| return _SerializeNativeArray(value, data_offset, data, len(value)) |
| |
| def DeserializeArray(self, size, nb_elements, context): |
| result = array.array(self.array_typecode) |
| result.fromstring(buffer(context.data, |
| serialization.HEADER_STRUCT.size, |
| size - serialization.HEADER_STRUCT.size)) |
| return result |
| |
| def SizeForLength(self, nb_elements): |
| return nb_elements * self.element_size |
| |
| |
| class StructType(PointerType): |
| """Type object for structs.""" |
| |
| def __init__(self, struct_type_getter, nullable=False): |
| PointerType.__init__(self) |
| self._struct_type_getter = struct_type_getter |
| self._struct_type = None |
| self.nullable = nullable |
| |
| @property |
| def struct_type(self): |
| if not self._struct_type: |
| self._struct_type = self._struct_type_getter() |
| return self._struct_type |
| |
| def Convert(self, value): |
| if value is None or isinstance(value, self.struct_type): |
| return value |
| raise TypeError('%r is not an instance of %r' % (value, self.struct_type)) |
| |
| def GetDefaultValue(self, value): |
| if value: |
| return self.struct_type() |
| return None |
| |
| def SerializePointer(self, value, data_offset, data, handle_offset): |
| (new_data, new_handles) = value.Serialize(handle_offset) |
| data.extend(new_data) |
| return (data_offset, new_handles) |
| |
| def DeserializePointer(self, size, nb_elements, context): |
| return self.struct_type.Deserialize(context) |
| |
| |
| class MapType(SerializableType): |
| """Type objects for maps.""" |
| |
| def __init__(self, key_type, value_type, nullable=False): |
| self._key_type = key_type |
| self._value_type = value_type |
| dictionary = { |
| '__metaclass__': reflection.MojoStructType, |
| '__module__': __name__, |
| 'DESCRIPTOR': { |
| 'fields': [ |
| SingleFieldGroup('keys', MapType._GetArrayType(key_type), 0, 0), |
| SingleFieldGroup('values', MapType._GetArrayType(value_type), 1, 0), |
| ], |
| } |
| } |
| self.struct = reflection.MojoStructType('MapStruct', (object,), dictionary) |
| self.struct_type = StructType(lambda: self.struct, nullable) |
| SerializableType.__init__(self, self.struct_type.typecode) |
| |
| def Convert(self, value): |
| if value is None: |
| return value |
| if isinstance(value, dict): |
| return dict([(self._key_type.Convert(x), self._value_type.Convert(y)) for |
| x, y in value.iteritems()]) |
| raise TypeError('%r is not a dictionary.') |
| |
| def Serialize(self, value, data_offset, data, handle_offset): |
| s = None |
| if value: |
| keys, values = [], [] |
| for key, value in value.iteritems(): |
| keys.append(key) |
| values.append(value) |
| s = self.struct(keys=keys, values=values) |
| return self.struct_type.Serialize(s, data_offset, data, handle_offset) |
| |
| def Deserialize(self, value, context): |
| s = self.struct_type.Deserialize(value, context) |
| if s: |
| if len(s.keys) != len(s.values): |
| raise serialization.DeserializationException( |
| 'keys and values do not have the same length.') |
| return dict(zip(s.keys, s.values)) |
| return None |
| |
| @staticmethod |
| def _GetArrayType(t): |
| if t == TYPE_BOOL: |
| return BooleanArrayType() |
| else: |
| return GenericArrayType(t) |
| |
| |
| TYPE_BOOL = BooleanType() |
| |
| TYPE_INT8 = IntegerType('b') |
| TYPE_INT16 = IntegerType('h') |
| TYPE_INT32 = IntegerType('i') |
| TYPE_INT64 = IntegerType('q') |
| |
| TYPE_UINT8 = IntegerType('B') |
| TYPE_UINT16 = IntegerType('H') |
| TYPE_UINT32 = IntegerType('I') |
| TYPE_UINT64 = IntegerType('Q') |
| |
| TYPE_FLOAT = FloatType('f') |
| TYPE_DOUBLE = FloatType('d') |
| |
| TYPE_STRING = StringType() |
| TYPE_NULLABLE_STRING = StringType(True) |
| |
| TYPE_HANDLE = HandleType() |
| TYPE_NULLABLE_HANDLE = HandleType(True) |
| |
| TYPE_INTERFACE_REQUEST = InterfaceRequestType() |
| TYPE_NULLABLE_INTERFACE_REQUEST = InterfaceRequestType(True) |
| |
| |
| class FieldDescriptor(object): |
| """Describes a field in a generated struct.""" |
| |
| def __init__(self, name, field_type, index, version, default_value=None): |
| self.name = name |
| self.field_type = field_type |
| self.version = version |
| self.index = index |
| self._default_value = default_value |
| |
| def GetDefaultValue(self): |
| return self.field_type.GetDefaultValue(self._default_value) |
| |
| |
| class FieldGroup(object): |
| """ |
| Describe a list of field in the generated struct that must be |
| serialized/deserialized together. |
| """ |
| def __init__(self, descriptors): |
| self.descriptors = descriptors |
| |
| def GetDescriptors(self): |
| return self.descriptors |
| |
| def GetTypeCode(self): |
| raise NotImplementedError() |
| |
| def GetByteSize(self): |
| raise NotImplementedError() |
| |
| def GetAlignment(self): |
| raise NotImplementedError() |
| |
| def GetMinVersion(self): |
| raise NotImplementedError() |
| |
| def GetMaxVersion(self): |
| raise NotImplementedError() |
| |
| def Serialize(self, obj, data_offset, data, handle_offset): |
| raise NotImplementedError() |
| |
| def Deserialize(self, value, context): |
| raise NotImplementedError() |
| |
| def Filter(self, version): |
| raise NotImplementedError() |
| |
| |
| class SingleFieldGroup(FieldGroup, FieldDescriptor): |
| """A FieldGroup that contains a single FieldDescriptor.""" |
| |
| def __init__(self, name, field_type, index, version, default_value=None): |
| FieldDescriptor.__init__( |
| self, name, field_type, index, version, default_value) |
| FieldGroup.__init__(self, [self]) |
| |
| def GetTypeCode(self): |
| return self.field_type.GetTypeCode() |
| |
| def GetByteSize(self): |
| return self.field_type.GetByteSize() |
| |
| def GetAlignment(self): |
| return self.field_type.GetAlignment() |
| |
| def GetMinVersion(self): |
| return self.version |
| |
| def GetMaxVersion(self): |
| return self.version |
| |
| def Serialize(self, obj, data_offset, data, handle_offset): |
| value = getattr(obj, self.name) |
| return self.field_type.Serialize(value, data_offset, data, handle_offset) |
| |
| def Deserialize(self, value, context): |
| entity = self.field_type.Deserialize(value, context) |
| return { self.name: entity } |
| |
| def Filter(self, version): |
| return self |
| |
| |
| class BooleanGroup(FieldGroup): |
| """A FieldGroup to pack booleans.""" |
| def __init__(self, descriptors): |
| FieldGroup.__init__(self, descriptors) |
| self.min_version = min([descriptor.version for descriptor in descriptors]) |
| self.max_version = max([descriptor.version for descriptor in descriptors]) |
| |
| def GetTypeCode(self): |
| return 'B' |
| |
| def GetByteSize(self): |
| return 1 |
| |
| def GetAlignment(self): |
| return 1 |
| |
| def GetMinVersion(self): |
| return self.min_version |
| |
| def GetMaxVersion(self): |
| return self.max_version |
| |
| def Serialize(self, obj, data_offset, data, handle_offset): |
| value = _ConvertBooleansToByte( |
| [getattr(obj, field.name) for field in self.GetDescriptors()]) |
| return (value, []) |
| |
| def Deserialize(self, value, context): |
| values = itertools.izip_longest([x.name for x in self.descriptors], |
| _ConvertByteToBooleans(value), |
| fillvalue=False) |
| return dict(values) |
| |
| def Filter(self, version): |
| return BooleanGroup( |
| filter(lambda d: d.version <= version, self.descriptors)) |
| |
| |
| def _SerializeNativeArray(value, data_offset, data, length): |
| data_size = len(data) |
| data.extend(bytearray(serialization.HEADER_STRUCT.size)) |
| data.extend(buffer(value)) |
| data_length = len(data) - data_size |
| data.extend(bytearray(serialization.NeededPaddingForAlignment(data_length))) |
| serialization.HEADER_STRUCT.pack_into(data, data_size, data_length, length) |
| return (data_offset, []) |
| |
| |
| def _ConvertBooleansToByte(booleans): |
| """Pack a list of booleans into an integer.""" |
| return reduce(lambda x, y: x * 2 + y, reversed(booleans), 0) |
| |
| |
| def _ConvertByteToBooleans(value, min_size=0): |
| """Unpack an integer into a list of booleans.""" |
| res = [] |
| while value: |
| res.append(bool(value&1)) |
| value = value / 2 |
| res.extend([False] * (min_size - len(res))) |
| return res |