• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
5import 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
14class 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.Struct, mojom.FixedArray)):
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, ordinal):
56    self.field = field
57    self.ordinal = ordinal
58    self.size = self.GetSizeForKind(field.kind)
59    self.offset = None
60    self.bit = None
61
62
63# Returns the pad necessary to reserve space for alignment of |size|.
64def GetPad(offset, size):
65  return (size - (offset % size)) % size
66
67
68# Returns a 2-tuple of the field offset and bit (for BOOLs)
69def GetFieldOffset(field, last_field):
70  if field.field.kind == mojom.BOOL and \
71      last_field.field.kind == mojom.BOOL and \
72      last_field.bit < 7:
73    return (last_field.offset, last_field.bit + 1)
74
75  offset = last_field.offset + last_field.size
76  pad = GetPad(offset, field.size)
77  return (offset + pad, 0)
78
79
80class PackedStruct(object):
81  def __init__(self, struct):
82    self.struct = struct
83    self.packed_fields = []
84
85    # No fields.
86    if (len(struct.fields) == 0):
87      return
88
89    # Start by sorting by ordinal.
90    src_fields = []
91    ordinal = 0
92    for field in struct.fields:
93      if field.ordinal is not None:
94        ordinal = field.ordinal
95      src_fields.append(PackedField(field, ordinal))
96      ordinal += 1
97    src_fields.sort(key=lambda field: field.ordinal)
98
99    src_field = src_fields[0]
100    src_field.offset = 0
101    src_field.bit = 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)
105
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:
113          # Found hole.
114          src_field.offset = offset
115          src_field.bit = bit
116          dst_fields.insert(i, src_field)
117          break
118        last_field = next_field
119      if src_field.offset is None:
120        # Add to end
121        src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
122        dst_fields.append(src_field)
123
124  def GetTotalSize(self):
125    if not self.packed_fields:
126      return 0
127    last_field = self.packed_fields[-1]
128    offset = last_field.offset + last_field.size
129    pad = GetPad(offset, 8)
130    return offset + pad
131
132
133class ByteInfo(object):
134  def __init__(self):
135    self.is_padding = False
136    self.packed_fields = []
137
138
139def GetByteLayout(packed_struct):
140  bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
141
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
148
149  for i in xrange(limit_of_previous_field, len(bytes)):
150    bytes[i].is_padding = True
151
152  for byte in bytes:
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)
155
156  return bytes
157