1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved.
21e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
31e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# found in the LICENSE file.
41e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
50529e5d033099cbfc42635f6f6183833b09dff6eBen Murdochimport module as mojom
61e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
70529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch# This module provides a mechanism for determining the packed order and offsets
81e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# of a mojom.Struct.
91e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)#
100529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch# ps = pack.PackedStruct(struct)
111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# ps.packed_fields will access a list of PackedField objects, each of which
121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# will have an offset, a size and a bit (for mojom.BOOLs).
131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class PackedField(object):
151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  kind_to_size = {
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.BOOL:                  1,
175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.INT8:                  1,
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.UINT8:                 1,
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.INT16:                 2,
205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.UINT16:                2,
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.INT32:                 4,
225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.UINT32:                4,
235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.FLOAT:                 4,
245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.HANDLE:                4,
255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.MSGPIPE:               4,
265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.SHAREDBUFFER:          4,
275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.DCPIPE:                4,
285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.DPPIPE:                4,
295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_HANDLE:       4,
305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_MSGPIPE:      4,
315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_SHAREDBUFFER: 4,
325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_DCPIPE:       4,
335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_DPPIPE:       4,
345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.INT64:                 8,
355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.UINT64:                8,
365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.DOUBLE:                8,
375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.STRING:                8,
385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    mojom.NULLABLE_STRING:       8
391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  }
401e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
411e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  @classmethod
421e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def GetSizeForKind(cls, kind):
43116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    if isinstance(kind, (mojom.Array, mojom.Struct, mojom.FixedArray)):
441e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return 8
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    if isinstance(kind, mojom.Interface) or \
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)       isinstance(kind, mojom.InterfaceRequest):
475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      kind = mojom.MSGPIPE
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if isinstance(kind, mojom.Enum):
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      # TODO(mpcomplete): what about big enums?
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      return cls.kind_to_size[mojom.INT32]
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    if not kind in cls.kind_to_size:
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      raise Exception("Invalid kind: %s" % kind.spec)
531e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return cls.kind_to_size[kind]
541e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
551e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def __init__(self, field, ordinal):
561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.field = field
571e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.ordinal = ordinal
581e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.size = self.GetSizeForKind(field.kind)
591e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.offset = None
601e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.bit = None
611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
631e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Returns the pad necessary to reserve space for alignment of |size|.
641e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)def GetPad(offset, size):
651e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return (size - (offset % size)) % size
661e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
681e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)# Returns a 2-tuple of the field offset and bit (for BOOLs)
691e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)def GetFieldOffset(field, last_field):
701e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if field.field.kind == mojom.BOOL and \
711e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      last_field.field.kind == mojom.BOOL and \
721e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      last_field.bit < 7:
731e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return (last_field.offset, last_field.bit + 1)
741e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
751e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  offset = last_field.offset + last_field.size
761e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  pad = GetPad(offset, field.size)
771e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return (offset + pad, 0)
781e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
791e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
801e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)class PackedStruct(object):
811e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  def __init__(self, struct):
821e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.struct = struct
831e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    self.packed_fields = []
841e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
851e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # No fields.
861e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    if (len(struct.fields) == 0):
871e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      return
881e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
891e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Start by sorting by ordinal.
901e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src_fields = []
915c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    ordinal = 0
921e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    for field in struct.fields:
931e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if field.ordinal is not None:
941e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        ordinal = field.ordinal
951e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      src_fields.append(PackedField(field, ordinal))
961e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      ordinal += 1
971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src_fields.sort(key=lambda field: field.ordinal)
981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src_field = src_fields[0]
1001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src_field.offset = 0
1011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    src_field.bit = 0
1021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # dst_fields will contain each of the fields, in increasing offset order.
1031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    dst_fields = self.packed_fields
1041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    dst_fields.append(src_field)
1051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
1061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    # Then find first slot that each field will fit.
1071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    for src_field in src_fields[1:]:
1081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      last_field = dst_fields[0]
1091e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      for i in xrange(1, len(dst_fields)):
1101e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        next_field = dst_fields[i]
1111e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        offset, bit = GetFieldOffset(src_field, last_field)
1121e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        if offset + src_field.size <= next_field.offset:
1131e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          # Found hole.
1141e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          src_field.offset = offset
1151e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          src_field.bit = bit
1161e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          dst_fields.insert(i, src_field)
1171e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)          break
1181e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        last_field = next_field
1191e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      if src_field.offset is None:
1201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        # Add to end
1211e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        src_field.offset, src_field.bit = GetFieldOffset(src_field, last_field)
1221e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)        dst_fields.append(src_field)
123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def GetTotalSize(self):
125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    if not self.packed_fields:
1265c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu      return 0
127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    last_field = self.packed_fields[-1]
128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    offset = last_field.offset + last_field.size
129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    pad = GetPad(offset, 8)
1305c02ac1a9c1b504631c0a3d2b6e737b5d738bae1Bo Liu    return offset + pad
131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class ByteInfo(object):
134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  def __init__(self):
135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.is_padding = False
136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    self.packed_fields = []
137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)def GetByteLayout(packed_struct):
140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  bytes = [ByteInfo() for i in xrange(packed_struct.GetTotalSize())]
141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  limit_of_previous_field = 0
143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for packed_field in packed_struct.packed_fields:
144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    for i in xrange(limit_of_previous_field, packed_field.offset):
145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)      bytes[i].is_padding = True
146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bytes[packed_field.offset].packed_fields.append(packed_field)
147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    limit_of_previous_field = packed_field.offset + packed_field.size
148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for i in xrange(limit_of_previous_field, len(bytes)):
150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    bytes[i].is_padding = True
151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  for byte in bytes:
153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    # A given byte cannot both be padding and have a fields packed into it.
154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)    assert not (byte.is_padding and byte.packed_fields)
155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)
156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)  return bytes
157