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.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|. 64def GetPad(offset, size): 65 return (size - (offset % size)) % size 66 67 68# Returns a 2-tuple of the field offset and bit (for BOOLs) 69def 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 80class 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 133class ByteInfo(object): 134 def __init__(self): 135 self.is_padding = False 136 self.packed_fields = [] 137 138 139def 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