Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / mojo / public / tools / bindings / pylib / mojom / generate / pack.py
1 # Copyright 2013 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 import module as mojom
6
7 # This module provides a mechanism for determining the packed order and offsets
8 # of a mojom.Struct.
9 #
10 # ps = pack.PackedStruct(struct)
11 # ps.packed_fields will access a list of PackedField objects, each of which
12 # will have an offset, a size and a bit (for mojom.BOOLs).
13
14 class PackedField(object):
15   kind_to_size = {
16     mojom.BOOL:                  1,
17     mojom.INT8:                  1,
18     mojom.UINT8:                 1,
19     mojom.INT16:                 2,
20     mojom.UINT16:                2,
21     mojom.INT32:                 4,
22     mojom.UINT32:                4,
23     mojom.FLOAT:                 4,
24     mojom.HANDLE:                4,
25     mojom.MSGPIPE:               4,
26     mojom.SHAREDBUFFER:          4,
27     mojom.DCPIPE:                4,
28     mojom.DPPIPE:                4,
29     mojom.NULLABLE_HANDLE:       4,
30     mojom.NULLABLE_MSGPIPE:      4,
31     mojom.NULLABLE_SHAREDBUFFER: 4,
32     mojom.NULLABLE_DCPIPE:       4,
33     mojom.NULLABLE_DPPIPE:       4,
34     mojom.INT64:                 8,
35     mojom.UINT64:                8,
36     mojom.DOUBLE:                8,
37     mojom.STRING:                8,
38     mojom.NULLABLE_STRING:       8
39   }
40
41   @classmethod
42   def GetSizeForKind(cls, kind):
43     if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct)):
44       return 8
45     if isinstance(kind, mojom.Interface) or \
46        isinstance(kind, mojom.InterfaceRequest):
47       kind = mojom.MSGPIPE
48     if isinstance(kind, mojom.Enum):
49       # TODO(mpcomplete): what about big enums?
50       return cls.kind_to_size[mojom.INT32]
51     if not kind in cls.kind_to_size:
52       raise Exception("Invalid kind: %s" % kind.spec)
53     return cls.kind_to_size[kind]
54
55   def __init__(self, field, index, ordinal):
56     """
57     Args:
58       field: the original field.
59       index: the position of the original field in the struct.
60       ordinal: the ordinal of the field for serialization.
61     """
62     self.field = field
63     self.index = index
64     self.ordinal = ordinal
65     self.size = self.GetSizeForKind(field.kind)
66     self.offset = None
67     self.bit = None
68
69
70 # Returns the pad necessary to reserve space for alignment of |size|.
71 def GetPad(offset, size):
72   return (size - (offset % size)) % size
73
74
75 # Returns a 2-tuple of the field offset and bit (for BOOLs)
76 def GetFieldOffset(field, last_field):
77   if field.field.kind == mojom.BOOL and \
78       last_field.field.kind == mojom.BOOL and \
79       last_field.bit < 7:
80     return (last_field.offset, last_field.bit + 1)
81
82   offset = last_field.offset + last_field.size
83   pad = GetPad(offset, field.size)
84   return (offset + pad, 0)
85
86
87 class PackedStruct(object):
88   def __init__(self, struct):
89     self.struct = struct
90     self.packed_fields = []
91
92     # No fields.
93     if (len(struct.fields) == 0):
94       return
95
96     # Start by sorting by ordinal.
97     src_fields = []
98     ordinal = 0
99     for index, field in enumerate(struct.fields):
100       if field.ordinal is not None:
101         ordinal = field.ordinal
102       src_fields.append(PackedField(field, index, ordinal))
103       ordinal += 1
104     src_fields.sort(key=lambda field: field.ordinal)
105
106     src_field = src_fields[0]
107     src_field.offset = 0
108     src_field.bit = 0
109     # dst_fields will contain each of the fields, in increasing offset order.
110     dst_fields = self.packed_fields
111     dst_fields.append(src_field)
112
113     # Then find first slot that each field will fit.
114     for src_field in src_fields[1:]:
115       last_field = dst_fields[0]
116       for i in xrange(1, len(dst_fields)):
117         next_field = dst_fields[i]
118         offset, bit = GetFieldOffset(src_field, last_field)
119         if offset + src_field.size <= next_field.offset:
120           # Found hole.
121           src_field.offset = offset
122           src_field.bit = bit
123           dst_fields.insert(i, src_field)
124           break
125         last_field = next_field
126       if src_field.offset is None:
127         # Add to end
128         src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
129         dst_fields.append(src_field)
130
131   def GetTotalSize(self):
132     if not self.packed_fields:
133       return 0
134     last_field = self.packed_fields[-1]
135     offset = last_field.offset + last_field.size
136     pad = GetPad(offset, 8)
137     return offset + pad
138
139
140 class ByteInfo(object):
141   def __init__(self):
142     self.is_padding = False
143     self.packed_fields = []
144
145
146 def GetByteLayout(packed_struct):
147   bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
148
149   limit_of_previous_field = 0
150   for packed_field in packed_struct.packed_fields:
151     for i in xrange(limit_of_previous_field, packed_field.offset):
152       bytes[i].is_padding = True
153     bytes[packed_field.offset].packed_fields.append(packed_field)
154     limit_of_previous_field = packed_field.offset + packed_field.size
155
156   for i in xrange(limit_of_previous_field, len(bytes)):
157     bytes[i].is_padding = True
158
159   for byte in bytes:
160     # A given byte cannot both be padding and have a fields packed into it.
161     assert not (byte.is_padding and byte.packed_fields)
162
163   return bytes