Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(342)

Unified Diff: mojo/public/python/mojo/bindings/descriptor.py

Issue 570563002: mojo: Starting serialization for python bindings. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: mojo/public/python/mojo/bindings/descriptor.py
diff --git a/mojo/public/python/mojo/bindings/descriptor.py b/mojo/public/python/mojo/bindings/descriptor.py
index 1a466b8c6a661d10cb1c89ec4ba5a1caf0fc081f..cd3aae7baebe60a28abc4ebe650e6241e23a4b15 100644
--- a/mojo/public/python/mojo/bindings/descriptor.py
+++ b/mojo/public/python/mojo/bindings/descriptor.py
@@ -7,9 +7,12 @@ The descriptors used to define generated elements of the mojo python bindings.
"""
import array
+import struct
# pylint: disable=F0401
-from mojo.system import Handle
+import mojo.bindings.serialization as serialization
+import mojo.system
+
class Type(object):
"""Describes the type of a struct field or a method parameter,"""
@@ -29,6 +32,40 @@ class Type(object):
return self.Convert(value)
+class SerializableType(Type):
+ """Describe a type that can be serialized by itself."""
+
+ def GetTypeCode(self):
+ """
+ Returns the type code (as defined by the struct module) used to encode
+ this type.
+ """
+ raise NotImplementedError()
+
+ def GetByteSize(self):
+ """
+ Returns the size of the encoding of this type.
+ """
+ raise NotImplementedError()
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ """
+ Serialize an 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()
+
sdefresne 2014/09/15 08:46:54 nit: only two blank lines
qsr 2014/09/15 11:01:53 Done.
+
+
class BooleanType(Type):
sdefresne 2014/09/15 08:46:54 Are BooleanType really unserializable or did you f
qsr 2014/09/15 11:01:52 You cannot serialize a boolean by itself, because
sdefresne 2014/09/15 11:14:46 OK, this make sense.
qsr 2014/09/15 11:42:00 Acknowledged.
"""Type object for booleans"""
@@ -36,20 +73,32 @@ class BooleanType(Type):
return bool(value)
-class NumericType(Type):
+class NumericType(SerializableType):
"""Base Type object for all numeric types"""
+ def __init__(self, type_code):
+ SerializableType.__init__(self)
+ self.type_code = type_code
+
def GetDefaultValue(self, value):
if value is None:
- return self.convert(0)
+ return self.Convert(0)
return self.Convert(value)
+ def GetTypeCode(self):
+ return self.type_code
+
+ def GetByteSize(self):
+ raise NotImplementedError()
sdefresne 2014/09/15 08:46:54 Why don't you implement this using struct.calcsize
qsr 2014/09/15 11:01:53 Refactored to take the type code in the constructo
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ return (value, [])
class IntegerType(NumericType):
"""Type object for integer types."""
- def __init__(self, size, signed):
- NumericType.__init__(self)
+ def __init__(self, type_code, size, signed):
+ NumericType.__init__(self, type_code)
self.size = size
self.signed = signed
if self.signed:
@@ -69,13 +118,16 @@ class IntegerType(NumericType):
(value, self._min_value, self._max_value))
return value
+ def GetByteSize(self):
+ return self.size / 8
+
class FloatType(NumericType):
"""Type object for floating point number types."""
- def __init__(self, size):
- NumericType.__init__(self)
- self.size = size
+ def __init__(self, type_code, byte_size):
+ NumericType.__init__(self, type_code)
+ self.byte_size = byte_size
def Convert(self, value):
if value is None:
@@ -84,8 +136,37 @@ class FloatType(NumericType):
raise TypeError('%r is not a numeric type' % value)
return float(value)
+ def GetByteSize(self):
+ return self.byte_size
-class StringType(Type):
+
+class PointerType(SerializableType):
+ """Base Type object for pointers."""
+
+ def __init__(self, nullable=False):
+ SerializableType.__init__(self)
+ self.nullable = nullable
+
+ def GetTypeCode(self):
+ return 'Q'
+
+ def GetByteSize(self):
+ return 8
+
+ 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 SerializePointer(self, value, data_offset, data, handle_offset):
+ """Serialize the not null value."""
+ raise NotImplementedError()
+
+
+class StringType(PointerType):
"""
Type object for strings.
@@ -94,8 +175,8 @@ class StringType(Type):
"""
def __init__(self, nullable=False):
- Type.__init__(self)
- self.nullable = nullable
+ PointerType.__init__(self, nullable)
+ self._array_type = NativeArrayType('B', nullable)
def Convert(self, value):
if value is None or isinstance(value, unicode):
@@ -104,36 +185,82 @@ class StringType(Type):
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)
+
-class HandleType(Type):
+class HandleType(SerializableType):
"""Type object for handles."""
def __init__(self, nullable=False):
- Type.__init__(self)
+ SerializableType.__init__(self)
self.nullable = nullable
def Convert(self, value):
if value is None:
- return Handle()
- if not isinstance(value, Handle):
+ return mojo.system.Handle()
+ if not isinstance(value, mojo.system.Handle):
raise TypeError('%r is not a handle' % value)
return value
+ def GetTypeCode(self):
+ return 'i'
+
+ def GetByteSize(self):
+ return 4
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ if not value.IsValid() and not self.nullable:
+ raise serialization.SerializationException(
+ "Trying to serialize null for non nullable type.")
+ if not value.IsValid():
+ return (-1, [])
+ return (handle_offset, [value])
-class GenericArrayType(Type):
+
+class BaseArrayType(PointerType):
"""Abstract Type object for arrays."""
def __init__(self, nullable=False, length=0):
- Type.__init__(self)
- self.nullable = nullable
+ 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()
+
+class BooleanArrayType(BaseArrayType):
-class PointerArrayType(GenericArrayType):
+ 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 self._array_type.SerializeArray(
+ converted, data_offset, data, handle_offset)
+
+
+class GenericArrayType(BaseArrayType):
"""Type object for arrays of pointers."""
def __init__(self, sub_type, nullable=False, length=0):
- GenericArrayType.__init__(self, nullable, length)
+ BaseArrayType.__init__(self, nullable, length)
+ assert sub_type != TYPE_BOOL
sdefresne 2014/09/15 08:46:54 Use "is not" instead of "!=" here as types are sin
qsr 2014/09/15 11:01:53 Except that TYPE_BOOL is an instance, not a type.
sdefresne 2014/09/15 11:14:46 Well it would have been a singleton nonetheless. C
qsr 2014/09/15 11:42:00 Acknowledged.
self.sub_type = sub_type
def Convert(self, value):
@@ -141,12 +268,38 @@ class PointerArrayType(GenericArrayType):
return value
return [self.sub_type.Convert(x) for x in value]
-
-class NativeArrayType(GenericArrayType):
+ 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) - data_offset
+ data.extend(bytearray(size +
+ serialization.NeededPaddingForAlignment(size)))
+ returned_handles = []
+ to_pack = []
+ position = position + 2
+ for item in value:
+ (new_data, new_handles) = self.sub_type.Serialize(
+ item,
+ len(data) - position,
+ data,
+ handle_offset + len(returned_handles))
+ to_pack.append(new_data)
+ returned_handles.extend(new_handles)
+ position = position + self.sub_type.GetByteSize()
+ serialization.HEADER_STRUCT.pack_into(data, data_end, size, len(value))
+ struct.pack_into("%d%s" % (len(value), self.sub_type.GetTypeCode()),
+ data,
+ data_end + serialization.HEADER_STRUCT.size,
+ *to_pack)
+ return (data_offset, returned_handles)
+
+
+class NativeArrayType(BaseArrayType):
"""Type object for arrays of native types."""
def __init__(self, typecode, nullable=False, length=0):
- GenericArrayType.__init__(self, nullable, length)
+ BaseArrayType.__init__(self, nullable, length)
self.typecode = typecode
def Convert(self, value):
@@ -156,12 +309,23 @@ class NativeArrayType(GenericArrayType):
return value
return array.array(self.typecode, value)
+ def SerializeArray(self, value, data_offset, data, handle_offset):
+ data_size = len(data)
+ data.extend(bytearray(serialization.HEADER_STRUCT.size))
+ data.extend(value.tostring())
+ data_length = len(data) - data_size
+ data.extend(bytearray(
+ serialization.NeededPaddingForAlignment(data_length)))
+ serialization.HEADER_STRUCT.pack_into(
+ data, data_size, data_length, len(value))
+ return (data_offset, [])
+
-class StructType(Type):
+class StructType(PointerType):
"""Type object for structs."""
def __init__(self, struct_type, nullable=False):
- Type.__init__(self)
+ PointerType.__init__(self)
self.struct_type = struct_type
self.nullable = nullable
@@ -175,30 +339,44 @@ class StructType(Type):
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)
-class NoneType(Type):
+
+class NoneType(SerializableType):
"""Placeholder type, used temporarily until all mojo types are handled."""
def Convert(self, value):
return None
+ def GetTypeCode(self):
+ return 'B'
+
+ def GetByteSize(self):
+ return 1
+
+ def Serialize(self, value, data_offset, data, handle_offset):
+ return (0, [])
+
TYPE_NONE = NoneType()
TYPE_BOOL = BooleanType()
-TYPE_INT8 = IntegerType(8, True)
-TYPE_INT16 = IntegerType(16, True)
-TYPE_INT32 = IntegerType(32, True)
-TYPE_INT64 = IntegerType(64, True)
+TYPE_INT8 = IntegerType('b', 8, True)
+TYPE_INT16 = IntegerType('h', 16, True)
+TYPE_INT32 = IntegerType('i', 32, True)
+TYPE_INT64 = IntegerType('q', 64, True)
-TYPE_UINT8 = IntegerType(8, False)
-TYPE_UINT16 = IntegerType(16, False)
-TYPE_UINT32 = IntegerType(32, False)
-TYPE_UINT64 = IntegerType(64, False)
+TYPE_UINT8 = IntegerType('B', 8, False)
+TYPE_UINT16 = IntegerType('H', 16, False)
+TYPE_UINT32 = IntegerType('I', 32, False)
+TYPE_UINT64 = IntegerType('Q', 64, False)
-TYPE_FLOAT = FloatType(4)
-TYPE_DOUBLE = FloatType(8)
+TYPE_FLOAT = FloatType('f', 4)
+TYPE_DOUBLE = FloatType('d', 8)
TYPE_STRING = StringType()
TYPE_NULLABLE_STRING = StringType(True)
@@ -210,13 +388,88 @@ TYPE_NULLABLE_HANDLE = HandleType(True)
class FieldDescriptor(object):
"""Describes a field in a generated struct."""
- def __init__(self, name, field_type, offset,
- bit_offset=None, default_value=None):
+ def __init__(self, name, field_type, field_number, default_value=None):
self.name = name
self.field_type = field_type
- self.offset = offset
- self.bit_offset = bit_offset
+ self.field_number = field_number
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 GetVersion(self):
+ raise NotImplementedError()
+
+ def Serialize(self, obj, data_offset, data, handle_offset):
+ raise NotImplementedError()
+
+
+class SingleFieldGroup(FieldGroup, FieldDescriptor):
+ """A FieldGroup that contains a single FieldDescriptor."""
+
+ def __init__(self, name, field_type, field_number, default_value=None):
+ FieldDescriptor.__init__(
+ self, name, field_type, field_number, default_value)
+ FieldGroup.__init__(self, [self])
+
+ def GetTypeCode(self):
+ return self.field_type.GetTypeCode()
+
+ def GetByteSize(self):
+ return self.field_type.GetByteSize()
+
+ def GetVersion(self):
+ return self.field_number
+
+ 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)
+
+
+class BooleanGroup(FieldGroup):
+ """A FieldGroup to pack booleans."""
+ def __init__(self, descriptors):
+ FieldGroup.__init__(self, descriptors)
+ self.version = min([descriptor.field_number for descriptor in descriptors])
+
+ def GetTypeCode(self):
+ return "B"
+
+ def GetByteSize(self):
+ return 1
+
+ def GetVersion(self):
+ return self.version
+
+ def Serialize(self, obj, data_offset, data, handle_offset):
+ value = _ConvertBooleansToByte(
+ [getattr(obj, field.name) for field in self.GetDescriptors()])
+ return (value, [])
+
+
+def _ConvertBooleansToByte(booleans):
+ """Pack a list of booleans into an integer."""
+ value = 0
sdefresne 2014/09/15 08:46:54 This could be rewritten as "reduce(lambda x, y: x
qsr 2014/09/15 11:01:52 Done.
+ for boolean in reversed(booleans):
+ value = value * 2
+ if boolean:
+ value = value + 1
+ return value

Powered by Google App Engine
This is Rietveld 408576698