• 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.INT64:        8,
30    mojom.UINT64:       8,
31    mojom.DOUBLE:       8,
32    mojom.STRING:       8
33  }
34
35  @classmethod
36  def GetSizeForKind(cls, kind):
37    if isinstance(kind, mojom.Array) or isinstance(kind, mojom.Struct):
38      return 8
39    if isinstance(kind, mojom.Interface) or \
40       isinstance(kind, mojom.InterfaceRequest):
41      kind = mojom.MSGPIPE
42    if isinstance(kind, mojom.Enum):
43      # TODO(mpcomplete): what about big enums?
44      return cls.kind_to_size[mojom.INT32]
45    if not kind in cls.kind_to_size:
46      raise Exception("Invalid kind: %s" % kind.spec)
47    return cls.kind_to_size[kind]
48
49  def __init__(self, field, ordinal):
50    self.field = field
51    self.ordinal = ordinal
52    self.size = self.GetSizeForKind(field.kind)
53    self.offset = None
54    self.bit = None
55
56
57# Returns the pad necessary to reserve space for alignment of |size|.
58def GetPad(offset, size):
59  return (size - (offset % size)) % size
60
61
62# Returns a 2-tuple of the field offset and bit (for BOOLs)
63def GetFieldOffset(field, last_field):
64  if field.field.kind == mojom.BOOL and \
65      last_field.field.kind == mojom.BOOL and \
66      last_field.bit < 7:
67    return (last_field.offset, last_field.bit + 1)
68
69  offset = last_field.offset + last_field.size
70  pad = GetPad(offset, field.size)
71  return (offset + pad, 0)
72
73
74class PackedStruct(object):
75  def __init__(self, struct):
76    self.struct = struct
77    self.packed_fields = []
78
79    # No fields.
80    if (len(struct.fields) == 0):
81      return
82
83    # Start by sorting by ordinal.
84    src_fields = []
85    ordinal = 0
86    for field in struct.fields:
87      if field.ordinal is not None:
88        ordinal = field.ordinal
89      src_fields.append(PackedField(field, ordinal))
90      ordinal += 1
91    src_fields.sort(key=lambda field: field.ordinal)
92
93    src_field = src_fields[0]
94    src_field.offset = 0
95    src_field.bit = 0
96    # dst_fields will contain each of the fields, in increasing offset order.
97    dst_fields = self.packed_fields
98    dst_fields.append(src_field)
99
100    # Then find first slot that each field will fit.
101    for src_field in src_fields[1:]:
102      last_field = dst_fields[0]
103      for i in xrange(1, len(dst_fields)):
104        next_field = dst_fields[i]
105        offset, bit = GetFieldOffset(src_field, last_field)
106        if offset + src_field.size <= next_field.offset:
107          # Found hole.
108          src_field.offset = offset
109          src_field.bit = bit
110          dst_fields.insert(i, src_field)
111          break
112        last_field = next_field
113      if src_field.offset is None:
114        # Add to end
115        src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
116        dst_fields.append(src_field)
117
118  def GetTotalSize(self):
119    if not self.packed_fields:
120      return 0
121    last_field = self.packed_fields[-1]
122    offset = last_field.offset + last_field.size
123    pad = GetPad(offset, 8)
124    return offset + pad
125
126
127class ByteInfo(object):
128  def __init__(self):
129    self.is_padding = False
130    self.packed_fields = []
131
132
133def GetByteLayout(packed_struct):
134  bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
135
136  limit_of_previous_field = 0
137  for packed_field in packed_struct.packed_fields:
138    for i in xrange(limit_of_previous_field, packed_field.offset):
139      bytes[i].is_padding = True
140    bytes[packed_field.offset].packed_fields.append(packed_field)
141    limit_of_previous_field = packed_field.offset + packed_field.size
142
143  for i in xrange(limit_of_previous_field, len(bytes)):
144    bytes[i].is_padding = True
145
146  for byte in bytes:
147    # A given byte cannot both be padding and have a fields packed into it.
148    assert not (byte.is_padding and byte.packed_fields)
149
150  return bytes
151