aboutsummaryrefslogtreecommitdiff
path: root/src/ir/layout.rs
blob: 28a6604d9596fe79faf28563e3a54be8159a5da4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//! Intermediate representation for the physical layout of some type.

use super::derive::CanDerive;
use super::ty::{Type, TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
use crate::clang;
use crate::ir::context::BindgenContext;
use std::cmp;

/// A type that represents the struct layout of a type.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Layout {
    /// The size (in bytes) of this layout.
    pub size: usize,
    /// The alignment (in bytes) of this layout.
    pub align: usize,
    /// Whether this layout's members are packed or not.
    pub packed: bool,
}

#[test]
fn test_layout_for_size() {
    use std::mem;

    let ptr_size = mem::size_of::<*mut ()>();
    assert_eq!(
        Layout::for_size_internal(ptr_size, ptr_size),
        Layout::new(ptr_size, ptr_size)
    );
    assert_eq!(
        Layout::for_size_internal(ptr_size, 3 * ptr_size),
        Layout::new(3 * ptr_size, ptr_size)
    );
}

impl Layout {
    /// Gets the integer type name for a given known size.
    pub fn known_type_for_size(
        ctx: &BindgenContext,
        size: usize,
    ) -> Option<&'static str> {
        Some(match size {
            16 if ctx.options().rust_features.i128_and_u128 => "u128",
            8 => "u64",
            4 => "u32",
            2 => "u16",
            1 => "u8",
            _ => return None,
        })
    }

    /// Construct a new `Layout` with the given `size` and `align`. It is not
    /// packed.
    pub fn new(size: usize, align: usize) -> Self {
        Layout {
            size,
            align,
            packed: false,
        }
    }

    fn for_size_internal(ptr_size: usize, size: usize) -> Self {
        let mut next_align = 2;
        while size % next_align == 0 && next_align <= ptr_size {
            next_align *= 2;
        }
        Layout {
            size: size,
            align: next_align / 2,
            packed: false,
        }
    }

    /// Creates a non-packed layout for a given size, trying to use the maximum
    /// alignment possible.
    pub fn for_size(ctx: &BindgenContext, size: usize) -> Self {
        Self::for_size_internal(ctx.target_pointer_size(), size)
    }

    /// Is this a zero-sized layout?
    pub fn is_zero(&self) -> bool {
        self.size == 0 && self.align == 0
    }

    /// Construct a zero-sized layout.
    pub fn zero() -> Self {
        Self::new(0, 0)
    }

    /// Get this layout as an opaque type.
    pub fn opaque(&self) -> Opaque {
        Opaque(*self)
    }
}

/// When we are treating a type as opaque, it is just a blob with a `Layout`.
#[derive(Clone, Debug, PartialEq)]
pub struct Opaque(pub Layout);

impl Opaque {
    /// Construct a new opaque type from the given clang type.
    pub fn from_clang_ty(ty: &clang::Type, ctx: &BindgenContext) -> Type {
        let layout = Layout::new(ty.size(ctx), ty.align(ctx));
        let ty_kind = TypeKind::Opaque;
        let is_const = ty.is_const();
        Type::new(None, Some(layout), ty_kind, is_const)
    }

    /// Return the known rust type we should use to create a correctly-aligned
    /// field with this layout.
    pub fn known_rust_type_for_array(
        &self,
        ctx: &BindgenContext,
    ) -> Option<&'static str> {
        Layout::known_type_for_size(ctx, self.0.align)
    }

    /// Return the array size that an opaque type for this layout should have if
    /// we know the correct type for it, or `None` otherwise.
    pub fn array_size(&self, ctx: &BindgenContext) -> Option<usize> {
        if self.known_rust_type_for_array(ctx).is_some() {
            Some(self.0.size / cmp::max(self.0.align, 1))
        } else {
            None
        }
    }

    /// Return `true` if this opaque layout's array size will fit within the
    /// maximum number of array elements that Rust allows deriving traits
    /// with. Return `false` otherwise.
    pub fn array_size_within_derive_limit(
        &self,
        ctx: &BindgenContext,
    ) -> CanDerive {
        if self
            .array_size(ctx)
            .map_or(false, |size| size <= RUST_DERIVE_IN_ARRAY_LIMIT)
        {
            CanDerive::Yes
        } else {
            CanDerive::Manually
        }
    }
}