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