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.
7 # This module provides a mechanism for determining the packed order and offsets
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).
14 class PackedField(object):
26 mojom.SHAREDBUFFER: 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,
38 mojom.NULLABLE_STRING: 8
42 def GetSizeForKind(cls, kind):
43 if isinstance(kind, (mojom.Array, mojom.Struct, mojom.FixedArray)):
45 if isinstance(kind, mojom.Interface) or \
46 isinstance(kind, mojom.InterfaceRequest):
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]
55 def __init__(self, field, ordinal):
57 self.ordinal = ordinal
58 self.size = self.GetSizeForKind(field.kind)
63 # Returns the pad necessary to reserve space for alignment of |size|.
64 def GetPad(offset, size):
65 return (size - (offset % size)) % size
68 # Returns a 2-tuple of the field offset and bit (for BOOLs)
69 def GetFieldOffset(field, last_field):
70 if field.field.kind == mojom.BOOL and \
71 last_field.field.kind == mojom.BOOL and \
73 return (last_field.offset, last_field.bit + 1)
75 offset = last_field.offset + last_field.size
76 pad = GetPad(offset, field.size)
77 return (offset + pad, 0)
80 class PackedStruct(object):
81 def __init__(self, struct):
83 self.packed_fields = []
86 if (len(struct.fields) == 0):
89 # Start by sorting by ordinal.
92 for field in struct.fields:
93 if field.ordinal is not None:
94 ordinal = field.ordinal
95 src_fields.append(PackedField(field, ordinal))
97 src_fields.sort(key=lambda field: field.ordinal)
99 src_field = src_fields[0]
102 # dst_fields will contain each of the fields, in increasing offset order.
103 dst_fields = self.packed_fields
104 dst_fields.append(src_field)
106 # Then find first slot that each field will fit.
107 for src_field in src_fields[1:]:
108 last_field = dst_fields[0]
109 for i in xrange(1, len(dst_fields)):
110 next_field = dst_fields[i]
111 offset, bit = GetFieldOffset(src_field, last_field)
112 if offset + src_field.size <= next_field.offset:
114 src_field.offset = offset
116 dst_fields.insert(i, src_field)
118 last_field = next_field
119 if src_field.offset is None:
121 src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
122 dst_fields.append(src_field)
124 def GetTotalSize(self):
125 if not self.packed_fields:
127 last_field = self.packed_fields[-1]
128 offset = last_field.offset + last_field.size
129 pad = GetPad(offset, 8)
133 class ByteInfo(object):
135 self.is_padding = False
136 self.packed_fields = []
139 def GetByteLayout(packed_struct):
140 bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
142 limit_of_previous_field = 0
143 for packed_field in packed_struct.packed_fields:
144 for i in xrange(limit_of_previous_field, packed_field.offset):
145 bytes[i].is_padding = True
146 bytes[packed_field.offset].packed_fields.append(packed_field)
147 limit_of_previous_field = packed_field.offset + packed_field.size
149 for i in xrange(limit_of_previous_field, len(bytes)):
150 bytes[i].is_padding = True
153 # A given byte cannot both be padding and have a fields packed into it.
154 assert not (byte.is_padding and byte.packed_fields)