| # 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(SerializableType): | 
 |   """Type object for booleans""" | 
 |  | 
 |   def Convert(self, value): | 
 |     return bool(value) | 
 |  | 
 |   def Serialize(self, value, data_offset, data, handle_offset): | 
 |     return (_ConvertBooleansToByte([value]), []) | 
 |  | 
 |   def Deserialize(self, value, context): | 
 |     return _ConvertByteToBooleans(value, 1)[0] | 
 |  | 
 |  | 
 | 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 is not None: | 
 |       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('B') | 
 |  | 
 | 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 |