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