aboutsummaryrefslogtreecommitdiff
path: root/ir/layout.rs
blob: 85a553da31aa64b716633c9809d22924ebe3f085 (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
//! 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, Eq)]
pub(crate) struct Layout {
    /// The size (in bytes) of this layout.
    pub(crate) size: usize,
    /// The alignment (in bytes) of this layout.
    pub(crate) align: usize,
    /// Whether this layout's members are packed or not.
    pub(crate) 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(crate) fn known_type_for_size(
        ctx: &BindgenContext,
        size: usize,
    ) -> Option<syn::Type> {
        Some(match size {
            16 if ctx.options().rust_features.i128_and_u128 => {
                syn::parse_quote! { u128 }
            }
            8 => syn::parse_quote! { u64 },
            4 => syn::parse_quote! { u32 },
            2 => syn::parse_quote! { u16 },
            1 => syn::parse_quote! { u8 },
            _ => return None,
        })
    }

    /// Construct a new `Layout` with the given `size` and `align`. It is not
    /// packed.
    pub(crate) 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,
            align: next_align / 2,
            packed: false,
        }
    }

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

    /// Get this layout as an opaque type.
    pub(crate) 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, Eq)]
pub(crate) struct Opaque(pub(crate) Layout);

impl Opaque {
    /// Construct a new opaque type from the given clang type.
    pub(crate) 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(crate) fn known_rust_type_for_array(
        &self,
        ctx: &BindgenContext,
    ) -> Option<syn::Type> {
        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(crate) 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(crate) 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
        }
    }
}