1 //! Intermediate representation for the physical layout of some type.
2 
3 use super::derive::CanDerive;
4 use super::ty::{Type, TypeKind, RUST_DERIVE_IN_ARRAY_LIMIT};
5 use crate::clang;
6 use crate::ir::context::BindgenContext;
7 use std::cmp;
8 
9 /// A type that represents the struct layout of a type.
10 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
11 pub(crate) struct Layout {
12     /// The size (in bytes) of this layout.
13     pub(crate) size: usize,
14     /// The alignment (in bytes) of this layout.
15     pub(crate) align: usize,
16     /// Whether this layout's members are packed or not.
17     pub(crate) packed: bool,
18 }
19 
20 #[test]
test_layout_for_size()21 fn test_layout_for_size() {
22     use std::mem;
23 
24     let ptr_size = mem::size_of::<*mut ()>();
25     assert_eq!(
26         Layout::for_size_internal(ptr_size, ptr_size),
27         Layout::new(ptr_size, ptr_size)
28     );
29     assert_eq!(
30         Layout::for_size_internal(ptr_size, 3 * ptr_size),
31         Layout::new(3 * ptr_size, ptr_size)
32     );
33 }
34 
35 impl Layout {
36     /// Gets the integer type name for a given known size.
known_type_for_size( ctx: &BindgenContext, size: usize, ) -> Option<syn::Type>37     pub(crate) fn known_type_for_size(
38         ctx: &BindgenContext,
39         size: usize,
40     ) -> Option<syn::Type> {
41         Some(match size {
42             16 if ctx.options().rust_features.i128_and_u128 => {
43                 syn::parse_quote! { u128 }
44             }
45             8 => syn::parse_quote! { u64 },
46             4 => syn::parse_quote! { u32 },
47             2 => syn::parse_quote! { u16 },
48             1 => syn::parse_quote! { u8 },
49             _ => return None,
50         })
51     }
52 
53     /// Construct a new `Layout` with the given `size` and `align`. It is not
54     /// packed.
new(size: usize, align: usize) -> Self55     pub(crate) fn new(size: usize, align: usize) -> Self {
56         Layout {
57             size,
58             align,
59             packed: false,
60         }
61     }
62 
for_size_internal(ptr_size: usize, size: usize) -> Self63     fn for_size_internal(ptr_size: usize, size: usize) -> Self {
64         let mut next_align = 2;
65         while size % next_align == 0 && next_align <= ptr_size {
66             next_align *= 2;
67         }
68         Layout {
69             size,
70             align: next_align / 2,
71             packed: false,
72         }
73     }
74 
75     /// Creates a non-packed layout for a given size, trying to use the maximum
76     /// alignment possible.
for_size(ctx: &BindgenContext, size: usize) -> Self77     pub(crate) fn for_size(ctx: &BindgenContext, size: usize) -> Self {
78         Self::for_size_internal(ctx.target_pointer_size(), size)
79     }
80 
81     /// Get this layout as an opaque type.
opaque(&self) -> Opaque82     pub(crate) fn opaque(&self) -> Opaque {
83         Opaque(*self)
84     }
85 }
86 
87 /// When we are treating a type as opaque, it is just a blob with a `Layout`.
88 #[derive(Clone, Debug, PartialEq, Eq)]
89 pub(crate) struct Opaque(pub(crate) Layout);
90 
91 impl Opaque {
92     /// Construct a new opaque type from the given clang type.
from_clang_ty( ty: &clang::Type, ctx: &BindgenContext, ) -> Type93     pub(crate) fn from_clang_ty(
94         ty: &clang::Type,
95         ctx: &BindgenContext,
96     ) -> Type {
97         let layout = Layout::new(ty.size(ctx), ty.align(ctx));
98         let ty_kind = TypeKind::Opaque;
99         let is_const = ty.is_const();
100         Type::new(None, Some(layout), ty_kind, is_const)
101     }
102 
103     /// Return the known rust type we should use to create a correctly-aligned
104     /// field with this layout.
known_rust_type_for_array( &self, ctx: &BindgenContext, ) -> Option<syn::Type>105     pub(crate) fn known_rust_type_for_array(
106         &self,
107         ctx: &BindgenContext,
108     ) -> Option<syn::Type> {
109         Layout::known_type_for_size(ctx, self.0.align)
110     }
111 
112     /// Return the array size that an opaque type for this layout should have if
113     /// we know the correct type for it, or `None` otherwise.
array_size(&self, ctx: &BindgenContext) -> Option<usize>114     pub(crate) fn array_size(&self, ctx: &BindgenContext) -> Option<usize> {
115         if self.known_rust_type_for_array(ctx).is_some() {
116             Some(self.0.size / cmp::max(self.0.align, 1))
117         } else {
118             None
119         }
120     }
121 
122     /// Return `true` if this opaque layout's array size will fit within the
123     /// maximum number of array elements that Rust allows deriving traits
124     /// with. Return `false` otherwise.
array_size_within_derive_limit( &self, ctx: &BindgenContext, ) -> CanDerive125     pub(crate) fn array_size_within_derive_limit(
126         &self,
127         ctx: &BindgenContext,
128     ) -> CanDerive {
129         if self
130             .array_size(ctx)
131             .map_or(false, |size| size <= RUST_DERIVE_IN_ARRAY_LIMIT)
132         {
133             CanDerive::Yes
134         } else {
135             CanDerive::Manually
136         }
137     }
138 }
139