Home | History | Annotate | Download | only in generate
      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.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|.
     64 def GetPad(offset, size):
     65   return (size - (offset % size)) % size
     66 
     67 
     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 \
     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 
     80 class 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 
    133 class ByteInfo(object):
    134   def __init__(self):
    135     self.is_padding = False
    136     self.packed_fields = []
    137 
    138 
    139 def 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