use super::helpers::BlobTyBuilder;
use aster::struct_field::StructFieldBuilder;
use ir::comp::CompInfo;
use ir::context::BindgenContext;
use ir::layout::Layout;
use ir::ty::{Type, TypeKind};
use std::cmp;
use std::mem;
use syntax::ast;
#[derive(Debug)]
pub struct StructLayoutTracker<'a, 'ctx: 'a> {
name: &'a str,
ctx: &'a BindgenContext<'ctx>,
comp: &'a CompInfo,
latest_offset: usize,
padding_count: usize,
latest_field_layout: Option<Layout>,
max_field_align: usize,
last_field_was_bitfield: bool,
}
pub fn align_to(size: usize, align: usize) -> usize {
if align == 0 {
return size;
}
let rem = size % align;
if rem == 0 {
return size;
}
size + align - rem
}
pub fn bytes_from_bits_pow2(mut n: usize) -> usize {
if n == 0 {
return 0;
}
if n <= 8 {
return 1;
}
if !n.is_power_of_two() {
n = n.next_power_of_two();
}
n / 8
}
#[test]
fn test_align_to() {
assert_eq!(align_to(1, 1), 1);
assert_eq!(align_to(1, 2), 2);
assert_eq!(align_to(1, 4), 4);
assert_eq!(align_to(5, 1), 5);
assert_eq!(align_to(17, 4), 20);
}
#[test]
fn test_bytes_from_bits_pow2() {
assert_eq!(bytes_from_bits_pow2(0), 0);
for i in 1..9 {
assert_eq!(bytes_from_bits_pow2(i), 1);
}
for i in 9..17 {
assert_eq!(bytes_from_bits_pow2(i), 2);
}
for i in 17..33 {
assert_eq!(bytes_from_bits_pow2(i), 4);
}
}
impl<'a, 'ctx> StructLayoutTracker<'a, 'ctx> {
pub fn new(ctx: &'a BindgenContext<'ctx>, comp: &'a CompInfo, name: &'a str) -> Self {
StructLayoutTracker {
name: name,
ctx: ctx,
comp: comp,
latest_offset: 0,
padding_count: 0,
latest_field_layout: None,
max_field_align: 0,
last_field_was_bitfield: false,
}
}
pub fn saw_vtable(&mut self) {
debug!("saw vtable for {}", self.name);
let ptr_size = mem::size_of::<*mut ()>();
self.latest_offset += ptr_size;
self.latest_field_layout = Some(Layout::new(ptr_size, ptr_size));
self.max_field_align = ptr_size;
}
pub fn saw_base(&mut self, base_ty: &Type) {
debug!("saw base for {}", self.name);
if let Some(layout) = base_ty.layout(self.ctx) {
self.align_to_latest_field(layout);
self.latest_offset += self.padding_bytes(layout) + layout.size;
self.latest_field_layout = Some(layout);
self.max_field_align = cmp::max(self.max_field_align, layout.align);
}
}
pub fn saw_bitfield_unit(&mut self, layout: Layout) {
debug!("saw bitfield unit for {}: {:?}", self.name, layout);
self.align_to_latest_field(layout);
self.latest_offset += layout.size;
debug!("Offset: <bitfield>: {} -> {}",
self.latest_offset - layout.size,
self.latest_offset);
self.latest_field_layout = Some(layout);
self.last_field_was_bitfield = true;
}
pub fn saw_union(&mut self, layout: Layout) {
debug!("saw union for {}: {:?}", self.name, layout);
self.align_to_latest_field(layout);
self.latest_offset += self.padding_bytes(layout) + layout.size;
self.latest_field_layout = Some(layout);
self.max_field_align = cmp::max(self.max_field_align, layout.align);
}
pub fn pad_field(&mut self,
field_name: &str,
field_ty: &Type,
field_offset: Option<usize>)
-> Option<ast::StructField> {
let mut field_layout = match field_ty.layout(self.ctx) {
Some(l) => l,
None => return None,
};
if let TypeKind::Array(inner, len) =
*field_ty.canonical_type(self.ctx).kind() {
if let Some(layout) = self.ctx
.resolve_type(inner)
.layout(self.ctx) {
if layout.align > mem::size_of::<*mut ()>() {
field_layout.size = align_to(layout.size, layout.align) *
len;
field_layout.align = mem::size_of::<*mut ()>();
}
}
}
let will_merge_with_bitfield = self.align_to_latest_field(field_layout);
let padding_layout = if self.comp.packed() {
None
} else {
let padding_bytes = match field_offset {
Some(offset) if offset / 8 > self.latest_offset => {
offset / 8 - self.latest_offset
}
_ if will_merge_with_bitfield || field_layout.align == 0 => 0,
_ => self.padding_bytes(field_layout),
};
let need_padding = padding_bytes >= field_layout.align ||
field_layout.align > mem::size_of::<*mut ()>();
self.latest_offset += padding_bytes;
debug!("Offset: <padding>: {} -> {}",
self.latest_offset - padding_bytes,
self.latest_offset);
debug!("align field {} to {}/{} with {} padding bytes {:?}",
field_name,
self.latest_offset,
field_offset.unwrap_or(0) / 8,
padding_bytes,
field_layout);
if need_padding && padding_bytes != 0 {
Some(Layout::new(padding_bytes,
cmp::min(field_layout.align,
mem::size_of::<*mut ()>())))
} else {
None
}
};
self.latest_offset += field_layout.size;
self.latest_field_layout = Some(field_layout);
self.max_field_align = cmp::max(self.max_field_align,
field_layout.align);
self.last_field_was_bitfield = false;
debug!("Offset: {}: {} -> {}",
field_name,
self.latest_offset - field_layout.size,
self.latest_offset);
padding_layout.map(|layout| self.padding_field(layout))
}
pub fn pad_struct(&mut self, layout: Layout) -> Option<ast::StructField> {
debug!("pad_struct:\n\tself = {:#?}\n\tlayout = {:#?}", self, layout);
if layout.size < self.latest_offset {
error!("Calculated wrong layout for {}, too more {} bytes",
self.name,
self.latest_offset - layout.size);
return None;
}
let padding_bytes = layout.size - self.latest_offset;
if padding_bytes > 0 &&
(padding_bytes >= layout.align ||
(self.last_field_was_bitfield &&
padding_bytes >= self.latest_field_layout.unwrap().align) ||
layout.align > mem::size_of::<*mut ()>()) {
let layout = if self.comp.packed() {
Layout::new(padding_bytes, 1)
} else if self.last_field_was_bitfield ||
layout.align > mem::size_of::<*mut ()>() {
Layout::for_size(padding_bytes)
} else {
Layout::new(padding_bytes, layout.align)
};
debug!("pad bytes to struct {}, {:?}", self.name, layout);
Some(self.padding_field(layout))
} else {
None
}
}
pub fn align_struct(&self, layout: Layout) -> Option<ast::StructField> {
if self.max_field_align < layout.align &&
layout.align <= mem::size_of::<*mut ()>() {
let ty = BlobTyBuilder::new(Layout::new(0, layout.align)).build();
Some(StructFieldBuilder::named("__bindgen_align")
.pub_()
.build_ty(ty))
} else {
None
}
}
fn padding_bytes(&self, layout: Layout) -> usize {
align_to(self.latest_offset, layout.align) - self.latest_offset
}
fn padding_field(&mut self, layout: Layout) -> ast::StructField {
let ty = BlobTyBuilder::new(layout).build();
let padding_count = self.padding_count;
self.padding_count += 1;
let padding_field_name = format!("__bindgen_padding_{}", padding_count);
self.max_field_align = cmp::max(self.max_field_align, layout.align);
StructFieldBuilder::named(padding_field_name).pub_().build_ty(ty)
}
fn align_to_latest_field(&mut self, new_field_layout: Layout) -> bool {
if self.comp.packed() {
return false;
}
let layout = match self.latest_field_layout {
Some(l) => l,
None => return false,
};
debug!("align_to_bitfield? {}: {:?} {:?}",
self.last_field_was_bitfield,
layout,
new_field_layout);
if self.last_field_was_bitfield &&
new_field_layout.align <= layout.size % layout.align &&
new_field_layout.size <= layout.size % layout.align {
debug!("Will merge with bitfield");
return true;
}
self.latest_offset += self.padding_bytes(layout);
return false;
}
}