blob: c898be0dd1f7ff6577fa0888cc814ec67de471a8 [file]
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import module as mojom
# This module provides a mechanism for determining the packed order and offsets
# of a mojom.Struct.
#
# ps = pack.PackedStruct(struct)
# ps.packed_fields will access a list of PackedField objects, each of which
# will have an offset, a size and a bit (for mojom.BOOLs).
# Size of struct header in bytes: num_bytes [4B] + version [4B].
HEADER_SIZE = 8
class PackedField(object):
kind_to_size = {
mojom.BOOL: 1,
mojom.INT8: 1,
mojom.UINT8: 1,
mojom.INT16: 2,
mojom.UINT16: 2,
mojom.INT32: 4,
mojom.UINT32: 4,
mojom.FLOAT: 4,
mojom.HANDLE: 4,
mojom.MSGPIPE: 4,
mojom.SHAREDBUFFER: 4,
mojom.DCPIPE: 4,
mojom.DPPIPE: 4,
mojom.NULLABLE_HANDLE: 4,
mojom.NULLABLE_MSGPIPE: 4,
mojom.NULLABLE_SHAREDBUFFER: 4,
mojom.NULLABLE_DCPIPE: 4,
mojom.NULLABLE_DPPIPE: 4,
mojom.INT64: 8,
mojom.UINT64: 8,
mojom.DOUBLE: 8,
mojom.STRING: 8,
mojom.NULLABLE_STRING: 8
}
@classmethod
def GetSizeForKind(cls, kind):
if isinstance(kind, (mojom.Array, mojom.Map, mojom.Struct,
mojom.Interface)):
return 8
if isinstance(kind, mojom.Union):
return 16
if isinstance(kind, mojom.InterfaceRequest):
kind = mojom.MSGPIPE
if isinstance(kind, mojom.Enum):
# TODO(mpcomplete): what about big enums?
return cls.kind_to_size[mojom.INT32]
if not kind in cls.kind_to_size:
raise Exception("Invalid kind: %s" % kind.spec)
return cls.kind_to_size[kind]
def __init__(self, field):
"""
Args:
field: the original field.
"""
self.field = field
self.index = field.declaration_order
self.ordinal = field.computed_ordinal
self.size = self.GetSizeForKind(field.kind)
self.offset = field.computed_offset
self.bit = field.computed_bit
self.min_version = field.computed_min_version
def GetPad(offset, alignment):
"""Returns the pad necessary to reserve space so that |offset + pad| equals to
some multiple of |alignment|."""
return (alignment - (offset % alignment)) % alignment
class PackedStruct(object):
def __init__(self, struct):
self.struct = struct
# |packed_fields_in_ordinal_order| contains all the fields,
# in ordinal order.
self.packed_fields_in_ordinal_order = [PackedField(field)
for field in struct.fields_in_ordinal_order]
# |packed_fields| refers to the same fields as
# |packed_fields_in_ordinal_order|, but in increasing offset order.
self.packed_fields = [field for field in
self.packed_fields_in_ordinal_order]
self.packed_fields.sort(key=lambda f: (f.offset, f.bit))
class ByteInfo(object):
def __init__(self):
self.is_padding = False
self.packed_fields = []
def GetByteLayout(packed_struct):
total_payload_size = packed_struct.struct.versions[-1].num_bytes - HEADER_SIZE
bytes = [ByteInfo() for i in xrange(total_payload_size)]
limit_of_previous_field = 0
for packed_field in packed_struct.packed_fields:
for i in xrange(limit_of_previous_field, packed_field.offset):
bytes[i].is_padding = True
bytes[packed_field.offset].packed_fields.append(packed_field)
limit_of_previous_field = packed_field.offset + packed_field.size
for i in xrange(limit_of_previous_field, len(bytes)):
bytes[i].is_padding = True
for byte in bytes:
# A given byte cannot both be padding and have a fields packed into it.
assert not (byte.is_padding and byte.packed_fields)
return bytes
class VersionInfo(object):
def __init__(self, version, num_fields, num_bytes):
self.version = version
self.num_fields = num_fields
self.num_bytes = num_bytes