• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2014 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
6The descriptors used to define generated elements of the mojo python bindings.
7"""
8
9import array
10import itertools
11import struct
12
13# pylint: disable=F0401
14import mojo.bindings.serialization as serialization
15import mojo.system
16
17
18class Type(object):
19  """Describes the type of a struct field or a method parameter,"""
20
21  def Convert(self, value): # pylint: disable=R0201
22    """
23    Convert the given value into its canonical representation, raising an
24    exception if the value cannot be converted.
25    """
26    return value
27
28  def GetDefaultValue(self, value):
29    """
30    Returns the default value for this type associated with the given value.
31    This method must be able to correcly handle value being None.
32    """
33    return self.Convert(value)
34
35
36class SerializableType(Type):
37  """Describe a type that can be serialized by itself."""
38
39  def __init__(self, typecode):
40    Type.__init__(self)
41    self.typecode = typecode
42    self.byte_size = struct.calcsize('=%s' % self.GetTypeCode())
43
44  def GetTypeCode(self):
45    """
46    Returns the type code (as defined by the struct module) used to encode
47    this type.
48    """
49    return self.typecode
50
51  def GetByteSize(self):
52    """
53    Returns the size of the encoding of this type.
54    """
55    return self.byte_size
56
57  def Serialize(self, value, data_offset, data, handle_offset):
58    """
59    Serialize a value of this type.
60
61    Args:
62      value: the value to serialize.
63      data_offset: the offset to the end of the data bytearray. Used to encode
64                   pointers.
65      data: the bytearray to append additional data to.
66      handle_offset: the offset to use to encode handles.
67
68    Returns a a tuple where the first element is the value to encode, and the
69    second is the array of handles to add to the message.
70    """
71    raise NotImplementedError()
72
73  def Deserialize(self, value, data, handles):
74    """
75    Deserialize a value of this type.
76
77    Args:
78      value: the base value for this type. This is always a numeric type, and
79             corresponds to the first element in the tuple returned by
80             Serialize.
81      data: the bytearray to retrieve additional data from.
82      handles: the array of handles contained in the message to deserialize.
83
84    Returns the deserialized value.
85    """
86    raise NotImplementedError()
87
88
89class BooleanType(Type):
90  """Type object for booleans"""
91
92  def Convert(self, value):
93    return bool(value)
94
95
96class NumericType(SerializableType):
97  """Base Type object for all numeric types"""
98
99  def GetDefaultValue(self, value):
100    if value is None:
101      return self.Convert(0)
102    return self.Convert(value)
103
104  def Serialize(self, value, data_offset, data, handle_offset):
105    return (value, [])
106
107  def Deserialize(self, value, data, handles):
108    return value
109
110
111class IntegerType(NumericType):
112  """Type object for integer types."""
113
114  def __init__(self, typecode):
115    NumericType.__init__(self, typecode)
116    size = 8 * self.byte_size
117    signed = typecode.islower()
118    if signed:
119      self._min_value = -(1 << (size - 1))
120      self._max_value = (1 << (size - 1)) - 1
121    else:
122      self._min_value = 0
123      self._max_value = (1 << size) - 1
124
125  def Convert(self, value):
126    if value is None:
127      raise TypeError('None is not an integer.')
128    if not isinstance(value, (int, long)):
129      raise TypeError('%r is not an integer type' % value)
130    if value < self._min_value or value > self._max_value:
131      raise OverflowError('%r is not in the range [%d, %d]' %
132                          (value, self._min_value, self._max_value))
133    return value
134
135
136class FloatType(NumericType):
137  """Type object for floating point number types."""
138
139  def Convert(self, value):
140    if value is None:
141      raise TypeError('None is not an floating point number.')
142    if not isinstance(value, (int, long, float)):
143      raise TypeError('%r is not a numeric type' % value)
144    return float(value)
145
146
147class PointerType(SerializableType):
148  """Base Type object for pointers."""
149
150  def __init__(self, nullable=False):
151    SerializableType.__init__(self, 'Q')
152    self.nullable = nullable
153
154  def Serialize(self, value, data_offset, data, handle_offset):
155    if value is None and not self.nullable:
156      raise serialization.SerializationException(
157          'Trying to serialize null for non nullable type.')
158    if value is None:
159      return (0, [])
160    return self.SerializePointer(value, data_offset, data, handle_offset)
161
162  def Deserialize(self, value, data, handles):
163    if value == 0:
164      if not self.nullable:
165        raise serialization.DeserializationException(
166            'Trying to deserialize null for non nullable type.')
167      return None
168    pointed_data = buffer(data, value)
169    (size, nb_elements) = serialization.HEADER_STRUCT.unpack_from(pointed_data)
170    return self.DeserializePointer(size, nb_elements, pointed_data, handles)
171
172  def SerializePointer(self, value, data_offset, data, handle_offset):
173    """Serialize the not null value."""
174    raise NotImplementedError()
175
176  def DeserializePointer(self, size, nb_elements, data, handles):
177    raise NotImplementedError()
178
179
180class StringType(PointerType):
181  """
182  Type object for strings.
183
184  Strings are represented as unicode, and the conversion is done using the
185  default encoding if a string instance is used.
186  """
187
188  def __init__(self, nullable=False):
189    PointerType.__init__(self, nullable)
190    self._array_type = NativeArrayType('B', nullable)
191
192  def Convert(self, value):
193    if value is None or isinstance(value, unicode):
194      return value
195    if isinstance(value, str):
196      return unicode(value)
197    raise TypeError('%r is not a string' % value)
198
199  def SerializePointer(self, value, data_offset, data, handle_offset):
200    string_array = array.array('b')
201    string_array.fromstring(value.encode('utf8'))
202    return self._array_type.SerializeArray(
203        string_array, data_offset, data, handle_offset)
204
205  def DeserializePointer(self, size, nb_elements, data, handles):
206    string_array = self._array_type.DeserializeArray(
207        size, nb_elements, data, handles)
208    return unicode(string_array.tostring(), 'utf8')
209
210
211class HandleType(SerializableType):
212  """Type object for handles."""
213
214  def __init__(self, nullable=False):
215    SerializableType.__init__(self, 'i')
216    self.nullable = nullable
217
218  def Convert(self, value):
219    if value is None:
220      return mojo.system.Handle()
221    if not isinstance(value, mojo.system.Handle):
222      raise TypeError('%r is not a handle' % value)
223    return value
224
225  def Serialize(self, value, data_offset, data, handle_offset):
226    if not value.IsValid() and not self.nullable:
227      raise serialization.SerializationException(
228          'Trying to serialize null for non nullable type.')
229    if not value.IsValid():
230      return (-1, [])
231    return (handle_offset, [value])
232
233  def Deserialize(self, value, data, handles):
234    if value == -1:
235      if not self.nullable:
236        raise serialization.DeserializationException(
237            'Trying to deserialize null for non nullable type.')
238      return mojo.system.Handle()
239    # TODO(qsr) validate handle order
240    return handles[value]
241
242
243class BaseArrayType(PointerType):
244  """Abstract Type object for arrays."""
245
246  def __init__(self, nullable=False, length=0):
247    PointerType.__init__(self, nullable)
248    self.length = length
249
250  def SerializePointer(self, value, data_offset, data, handle_offset):
251    if self.length != 0 and len(value) != self.length:
252      raise serialization.SerializationException('Incorrect array size')
253    return self.SerializeArray(value, data_offset, data, handle_offset)
254
255  def SerializeArray(self, value, data_offset, data, handle_offset):
256    """Serialize the not null array."""
257    raise NotImplementedError()
258
259  def DeserializePointer(self, size, nb_elements, data, handles):
260    if self.length != 0 and size != self.length:
261      raise serialization.DeserializationException('Incorrect array size')
262    return self.DeserializeArray(size, nb_elements, data, handles)
263
264  def DeserializeArray(self, size, nb_elements, data, handles):
265    raise NotImplementedError()
266
267
268class BooleanArrayType(BaseArrayType):
269
270  def __init__(self, nullable=False, length=0):
271    BaseArrayType.__init__(self, nullable, length)
272    self._array_type = NativeArrayType('B', nullable)
273
274  def Convert(self, value):
275    if value is None:
276      return value
277    return [TYPE_BOOL.Convert(x) for x in value]
278
279  def SerializeArray(self, value, data_offset, data, handle_offset):
280    groups = [value[i:i+8] for i in range(0, len(value), 8)]
281    converted = array.array('B', [_ConvertBooleansToByte(x) for x in groups])
282    return _SerializeNativeArray(converted, data_offset, data, len(value))
283
284  def DeserializeArray(self, size, nb_elements, data, handles):
285    converted = self._array_type.DeserializeArray(
286        size, nb_elements, data, handles)
287    elements = list(itertools.islice(
288        itertools.chain.from_iterable(
289            [_ConvertByteToBooleans(x, 8) for x in converted]),
290        0,
291        nb_elements))
292    return elements
293
294
295class GenericArrayType(BaseArrayType):
296  """Type object for arrays of pointers."""
297
298  def __init__(self, sub_type, nullable=False, length=0):
299    BaseArrayType.__init__(self, nullable, length)
300    assert isinstance(sub_type, SerializableType)
301    self.sub_type = sub_type
302
303  def Convert(self, value):
304    if value is None:
305      return value
306    return [self.sub_type.Convert(x) for x in value]
307
308  def SerializeArray(self, value, data_offset, data, handle_offset):
309    size = (serialization.HEADER_STRUCT.size +
310            self.sub_type.GetByteSize() * len(value))
311    data_end = len(data)
312    position = len(data) + serialization.HEADER_STRUCT.size
313    data.extend(bytearray(size +
314                          serialization.NeededPaddingForAlignment(size)))
315    returned_handles = []
316    to_pack = []
317    for item in value:
318      (new_data, new_handles) = self.sub_type.Serialize(
319          item,
320          len(data) - position,
321          data,
322          handle_offset + len(returned_handles))
323      to_pack.append(new_data)
324      returned_handles.extend(new_handles)
325      position = position + self.sub_type.GetByteSize()
326    serialization.HEADER_STRUCT.pack_into(data, data_end, size, len(value))
327    struct.pack_into('%d%s' % (len(value), self.sub_type.GetTypeCode()),
328                     data,
329                     data_end + serialization.HEADER_STRUCT.size,
330                     *to_pack)
331    return (data_offset, returned_handles)
332
333  def DeserializeArray(self, size, nb_elements, data, handles):
334    values = struct.unpack_from(
335        '%d%s' % (nb_elements, self.sub_type.GetTypeCode()),
336        buffer(data, serialization.HEADER_STRUCT.size))
337    result = []
338    position = serialization.HEADER_STRUCT.size
339    for value in values:
340      result.append(
341          self.sub_type.Deserialize(value, buffer(data, position), handles))
342      position += self.sub_type.GetByteSize()
343    return result
344
345
346class NativeArrayType(BaseArrayType):
347  """Type object for arrays of native types."""
348
349  def __init__(self, typecode, nullable=False, length=0):
350    BaseArrayType.__init__(self, nullable, length)
351    self.array_typecode = typecode
352
353  def Convert(self, value):
354    if value is None:
355      return value
356    if (isinstance(value, array.array) and
357        value.array_typecode == self.array_typecode):
358      return value
359    return array.array(self.array_typecode, value)
360
361  def SerializeArray(self, value, data_offset, data, handle_offset):
362    return _SerializeNativeArray(value, data_offset, data, len(value))
363
364  def DeserializeArray(self, size, nb_elements, data, handles):
365    result = array.array(self.array_typecode)
366    result.fromstring(buffer(data,
367                             serialization.HEADER_STRUCT.size,
368                             size - serialization.HEADER_STRUCT.size))
369    return result
370
371
372class StructType(PointerType):
373  """Type object for structs."""
374
375  def __init__(self, struct_type, nullable=False):
376    PointerType.__init__(self)
377    self.struct_type = struct_type
378    self.nullable = nullable
379
380  def Convert(self, value):
381    if value is None or isinstance(value, self.struct_type):
382      return value
383    raise TypeError('%r is not an instance of %r' % (value, self.struct_type))
384
385  def GetDefaultValue(self, value):
386    if value:
387      return self.struct_type()
388    return None
389
390  def SerializePointer(self, value, data_offset, data, handle_offset):
391    (new_data, new_handles) = value.Serialize(handle_offset)
392    data.extend(new_data)
393    return (data_offset, new_handles)
394
395  def DeserializePointer(self, size, nb_elements, data, handles):
396    return self.struct_type.Deserialize(data, handles)
397
398
399class NoneType(SerializableType):
400  """Placeholder type, used temporarily until all mojo types are handled."""
401
402  def __init__(self):
403    SerializableType.__init__(self, 'B')
404
405  def Convert(self, value):
406    return None
407
408  def Serialize(self, value, data_offset, data, handle_offset):
409    return (0, [])
410
411  def Deserialize(self, value, data, handles):
412    return None
413
414
415TYPE_NONE = NoneType()
416
417TYPE_BOOL = BooleanType()
418
419TYPE_INT8 = IntegerType('b')
420TYPE_INT16 = IntegerType('h')
421TYPE_INT32 = IntegerType('i')
422TYPE_INT64 = IntegerType('q')
423
424TYPE_UINT8 = IntegerType('B')
425TYPE_UINT16 = IntegerType('H')
426TYPE_UINT32 = IntegerType('I')
427TYPE_UINT64 = IntegerType('Q')
428
429TYPE_FLOAT = FloatType('f')
430TYPE_DOUBLE = FloatType('d')
431
432TYPE_STRING = StringType()
433TYPE_NULLABLE_STRING = StringType(True)
434
435TYPE_HANDLE = HandleType()
436TYPE_NULLABLE_HANDLE = HandleType(True)
437
438
439class FieldDescriptor(object):
440  """Describes a field in a generated struct."""
441
442  def __init__(self, name, field_type, field_number, default_value=None):
443    self.name = name
444    self.field_type = field_type
445    self.field_number = field_number
446    self._default_value = default_value
447
448  def GetDefaultValue(self):
449    return self.field_type.GetDefaultValue(self._default_value)
450
451
452class FieldGroup(object):
453  """
454  Describe a list of field in the generated struct that must be
455  serialized/deserialized together.
456  """
457  def __init__(self, descriptors):
458    self.descriptors = descriptors
459
460  def GetDescriptors(self):
461    return self.descriptors
462
463  def GetTypeCode(self):
464    raise NotImplementedError()
465
466  def GetByteSize(self):
467    raise NotImplementedError()
468
469  def GetVersion(self):
470    raise NotImplementedError()
471
472  def Serialize(self, obj, data_offset, data, handle_offset):
473    raise NotImplementedError()
474
475  def Deserialize(self, value, data, handles):
476    raise NotImplementedError()
477
478
479class SingleFieldGroup(FieldGroup, FieldDescriptor):
480  """A FieldGroup that contains a single FieldDescriptor."""
481
482  def __init__(self, name, field_type, field_number, default_value=None):
483    FieldDescriptor.__init__(
484        self, name, field_type, field_number, default_value)
485    FieldGroup.__init__(self, [self])
486
487  def GetTypeCode(self):
488    return self.field_type.GetTypeCode()
489
490  def GetByteSize(self):
491    return self.field_type.GetByteSize()
492
493  def GetVersion(self):
494    return self.field_number
495
496  def Serialize(self, obj, data_offset, data, handle_offset):
497    value = getattr(obj, self.name)
498    return self.field_type.Serialize(value, data_offset, data, handle_offset)
499
500  def Deserialize(self, value, data, handles):
501    entity = self.field_type.Deserialize(value, data, handles)
502    return { self.name: entity }
503
504
505class BooleanGroup(FieldGroup):
506  """A FieldGroup to pack booleans."""
507  def __init__(self, descriptors):
508    FieldGroup.__init__(self, descriptors)
509    self.version = min([descriptor.field_number  for descriptor in descriptors])
510
511  def GetTypeCode(self):
512    return 'B'
513
514  def GetByteSize(self):
515    return 1
516
517  def GetVersion(self):
518    return self.version
519
520  def Serialize(self, obj, data_offset, data, handle_offset):
521    value = _ConvertBooleansToByte(
522        [getattr(obj, field.name) for field in self.GetDescriptors()])
523    return (value, [])
524
525  def Deserialize(self, value, data, handles):
526    values =  itertools.izip_longest([x.name for x in self.descriptors],
527                                      _ConvertByteToBooleans(value),
528                                     fillvalue=False)
529    return dict(values)
530
531
532def _SerializeNativeArray(value, data_offset, data, length):
533  data_size = len(data)
534  data.extend(bytearray(serialization.HEADER_STRUCT.size))
535  data.extend(buffer(value))
536  data_length = len(data) - data_size
537  data.extend(bytearray(serialization.NeededPaddingForAlignment(data_length)))
538  serialization.HEADER_STRUCT.pack_into(data, data_size, data_length, length)
539  return (data_offset, [])
540
541
542def _ConvertBooleansToByte(booleans):
543  """Pack a list of booleans into an integer."""
544  return reduce(lambda x, y: x * 2 + y, reversed(booleans), 0)
545
546
547def _ConvertByteToBooleans(value, min_size=0):
548  "Unpack an integer into a list of booleans."""
549  res = []
550  while value:
551    res.append(bool(value&1))
552    value = value / 2
553  res.extend([False] * (min_size - len(res)))
554  return res
555