// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT//// Copyright 2024 The Fuchsia Authors//// Licensed under the 2-Clause BSD License <LICENSE-BSD or// https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.// This file may not be copied, modified, or distributed except according to// those terms. use core::{mem, num::NonZeroUsize}; use crate::util; /// The target pointer width, counted in bits.const POINTER_WIDTH_BITS: usize = mem::size_of::<usize>() * 8; /// The layout of a type which might be dynamically-sized.////// `DstLayout` describes the layout of sized types, slice types, and "slice/// DSTs" - ie, those that are known by the type system to have a trailing slice/// (as distinguished from `dyn Trait` types - such types *might* have a/// trailing slice type, but the type system isn't aware of it).////// Note that `DstLayout` does not have any internal invariants, so no guarantee/// is made that a `DstLayout` conforms to any of Rust's requirements regarding/// the layout of real Rust types or instances of types.#[doc(hidden)]#[allow(missing_debug_implementations, missing_copy_implementations)]#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]#[derive(Copy, Clone)]pub struct DstLayout { pub(crate) align: NonZeroUsize, pub(crate) size_info: SizeInfo, // Is it guaranteed statically (without knowing a value's runtime metadata) // that the top-level type contains no padding? This does *not* apply // recursively - for example, `[(u8, u16)]` has `statically_shallow_unpadded // = true` even though this type likely has padding inside each `(u8, u16)`. pub(crate) statically_shallow_unpadded: bool,} #[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]#[derive(Copy, Clone)]pub(crate) enum SizeInfo<E = usize> { Sized { size: usize }, SliceDst(TrailingSliceLayout<E>),} #[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]#[derive(Copy, Clone)]pub(crate) struct TrailingSliceLayout<E = usize> { // The offset of the first byte of the trailing slice field. Note that this // is NOT the same as the minimum size of the type. For example, consider // the following type: // // struct Foo { // a: u16, // b: u8, // c: [u8], // } // // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed // by a padding byte. pub(crate) offset: usize, // The size of the element type of the trailing slice field. pub(crate) elem_size: E,} impl SizeInfo { /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a /// `NonZeroUsize`. If `elem_size` is 0, returns `None`. #[allow(unused)] #[cfg_attr(not(zerocopy_inline_always), inline)] #[cfg_attr(zerocopy_inline_always, inline(always))] const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> { Some(match *self { SizeInfo::Sized { size } => SizeInfo::Sized { size }, SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => { if let Some(elem_size) = NonZeroUsize::new(elem_size) { SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) } else { return None; } } }) }} #[doc(hidden)]#[derive(Copy, Clone)]#[cfg_attr(test, derive(Debug))]#[allow(missing_debug_implementations)]pub enum CastType { Prefix, Suffix,} #[cfg_attr(test, derive(Debug))]pub(crate) enum MetadataCastError { Alignment, Size,} impl DstLayout { /// The minimum possible alignment of a type. const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) { Some(min_align) => min_align, None => const_unreachable!(), }; /// The maximum theoretic possible alignment of a type. /// /// For compatibility with future Rust versions, this is defined as the /// maximum power-of-two that fits into a `usize`. See also /// [`DstLayout::CURRENT_MAX_ALIGN`]. pub(crate) const THEORETICAL_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) { Some(max_align) => max_align, None => const_unreachable!(), }; /// The current, documented max alignment of a type \[1\]. /// /// \[1\] Per <https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers>: /// /// The alignment value must be a power of two from 1 up to /// 2<sup>29</sup>. #[cfg(not(kani))] #[cfg(not(target_pointer_width = "16"))] pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) { Some(max_align) => max_align, None => const_unreachable!(), }; #[cfg(not(kani))] #[cfg(target_pointer_width = "16")] pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 15) { Some(max_align) => max_align, None => const_unreachable!(), }; /// The maximum size of an allocation \[1\]. /// /// \[1\] Per <https://doc.rust-lang.org/1.91.1/std/ptr/index.html#allocation>: /// /// For any allocation with base `address`, `size`, and a set of `addresses`, /// the following are guaranteed: [..] /// /// - `size <= isize::MAX` /// #[allow(clippy::as_conversions)] pub(crate) const MAX_SIZE: usize = isize::MAX as usize; /// Assumes that this layout lacks static shallow padding. /// /// # Panics /// /// This method does not panic. /// /// # Safety /// /// If `self` describes the size and alignment of type that lacks static /// shallow padding, unsafe code may assume that the result of this method /// accurately reflects the size, alignment, and lack of static shallow /// padding of that type. const fn assume_shallow_unpadded(self) -> Self { Self { statically_shallow_unpadded: true, ..self } } /// Constructs a `DstLayout` for a zero-sized type with `repr_align` /// alignment (or 1). If `repr_align` is provided, then it must be a power /// of two. /// /// # Panics /// /// This function panics if the supplied `repr_align` is not a power of two. /// /// # Safety /// /// Unsafe code may assume that the contract of this function is satisfied. #[doc(hidden)] #[must_use] #[inline] pub const fn new_zst(repr_align: Option<NonZeroUsize>) -> DstLayout { let align = match repr_align { Some(align) => align, None => Self::MIN_ALIGN, }; const_assert!(align.get().is_power_of_two()); DstLayout { align, size_info: SizeInfo::Sized { size: 0 }, statically_shallow_unpadded: true, } } /// Constructs a `DstLayout` which describes `T` and assumes `T` may contain /// padding. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. #[doc(hidden)] #[must_use] #[inline] pub const fn for_type<T>() -> DstLayout { // SAFETY: `align` is correct by construction. `T: Sized`, and so it is // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the // `size` field is also correct by construction. `unpadded` can safely // default to `false`. DstLayout { align: match NonZeroUsize::new(mem::align_of::<T>()) { Some(align) => align, None => const_unreachable!(), }, size_info: SizeInfo::Sized { size: mem::size_of::<T>() }, statically_shallow_unpadded: false, } } /// Constructs a `DstLayout` which describes a `T` that does not contain /// padding. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. #[doc(hidden)] #[must_use] #[inline] pub const fn for_unpadded_type<T>() -> DstLayout { Self::for_type::<T>().assume_shallow_unpadded() } /// Constructs a `DstLayout` which describes `[T]`. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`. pub(crate) const fn for_slice<T>() -> DstLayout { // SAFETY: The alignment of a slice is equal to the alignment of its // element type, and so `align` is initialized correctly. // // Since this is just a slice type, there is no offset between the // beginning of the type and the beginning of the slice, so it is // correct to set `offset: 0`. The `elem_size` is correct by // construction. Since `[T]` is a (degenerate case of a) slice DST, it // is correct to initialize `size_info` to `SizeInfo::SliceDst`. DstLayout { align: match NonZeroUsize::new(mem::align_of::<T>()) { Some(align) => align, None => const_unreachable!(), }, size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset: 0, elem_size: mem::size_of::<T>(), }), statically_shallow_unpadded: true, } } /// Constructs a complete `DstLayout` reflecting a `repr(C)` struct with the /// given alignment modifiers and fields. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// For any definition of a `repr(C)` struct, if this method is invoked with /// alignment modifiers and fields corresponding to that definition, the /// resulting `DstLayout` will correctly encode the layout of that struct. /// /// We make no guarantees to the behavior of this method when it is invoked /// with arguments that cannot correspond to a valid `repr(C)` struct. #[must_use] #[inline] pub const fn for_repr_c_struct( repr_align: Option<NonZeroUsize>, repr_packed: Option<NonZeroUsize>, fields: &[DstLayout], ) -> DstLayout { let mut layout = DstLayout::new_zst(repr_align); let mut i = 0; #[allow(clippy::arithmetic_side_effects)] while i < fields.len() { #[allow(clippy::indexing_slicing)] let field = fields[i]; layout = layout.extend(field, repr_packed); i += 1; } layout = layout.pad_to_align(); // SAFETY: `layout` accurately describes the layout of a `repr(C)` // struct with `repr_align` or `repr_packed` alignment modifications and // the given `fields`. The `layout` is constructed using a sequence of // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. The // documentation of these items vows that invocations in this manner // will accurately describe a type, so long as: // // - that type is `repr(C)`, // - its fields are enumerated in the order they appear, // - the presence of `repr_align` and `repr_packed` are correctly accounted for. // // We respect all three of these preconditions above. layout } /// Like `Layout::extend`, this creates a layout that describes a record /// whose layout consists of `self` followed by `next` that includes the /// necessary inter-field padding, but not any trailing padding. /// /// In order to match the layout of a `#[repr(C)]` struct, this method /// should be invoked for each field in declaration order. To add trailing /// padding, call `DstLayout::pad_to_align` after extending the layout for /// all fields. If `self` corresponds to a type marked with /// `repr(packed(N))`, then `repr_packed` should be set to `Some(N)`, /// otherwise `None`. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// If a (potentially hypothetical) valid `repr(C)` Rust type begins with /// fields whose layout are `self`, and those fields are immediately /// followed by a field whose layout is `field`, then unsafe code may rely /// on `self.extend(field, repr_packed)` producing a layout that correctly /// encompasses those two components. /// /// We make no guarantees to the behavior of this method if these fragments /// cannot appear in a valid Rust type (e.g., the concatenation of the /// layouts would lead to a size larger than `isize::MAX`). #[doc(hidden)] #[must_use] #[inline] pub const fn extend(self, field: DstLayout, repr_packed: Option<NonZeroUsize>) -> Self { use util::{max, min, padding_needed_for}; // If `repr_packed` is `None`, there are no alignment constraints, and // the value can be defaulted to `THEORETICAL_MAX_ALIGN`. let max_align = match repr_packed { Some(max_align) => max_align, None => Self::THEORETICAL_MAX_ALIGN, }; const_assert!(max_align.get().is_power_of_two()); // We use Kani to prove that this method is robust to future increases // in Rust's maximum allowed alignment. However, if such a change ever // actually occurs, we'd like to be notified via assertion failures. #[cfg(not(kani))] { const_debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); const_debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); if let Some(repr_packed) = repr_packed { const_debug_assert!(repr_packed.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); } } // The field's alignment is clamped by `repr_packed` (i.e., the // `repr(packed(N))` attribute, if any) [1]. // // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: // // The alignments of each field, for the purpose of positioning // fields, is the smaller of the specified alignment and the alignment // of the field's type. let field_align = min(field.align, max_align); // The struct's alignment is the maximum of its previous alignment and // `field_align`. let align = max(self.align, field_align); let (interfield_padding, size_info) = match self.size_info { // If the layout is already a DST, we panic; DSTs cannot be extended // with additional fields. SizeInfo::SliceDst(..) => const_panic!("Cannot extend a DST with additional fields."), SizeInfo::Sized { size: preceding_size } => { // Compute the minimum amount of inter-field padding needed to // satisfy the field's alignment, and offset of the trailing // field. [1] // // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: // // Inter-field padding is guaranteed to be the minimum // required in order to satisfy each field's (possibly // altered) alignment. let padding = padding_needed_for(preceding_size, field_align); // This will not panic (and is proven to not panic, with Kani) // if the layout components can correspond to a leading layout // fragment of a valid Rust type, but may panic otherwise (e.g., // combining or aligning the components would create a size // exceeding `isize::MAX`). let offset = match preceding_size.checked_add(padding) { Some(offset) => offset, None => const_panic!("Adding padding to `self`'s size overflows `usize`."), }; ( padding, match field.size_info { SizeInfo::Sized { size: field_size } => { // If the trailing field is sized, the resulting layout // will be sized. Its size will be the sum of the // preceding layout, the size of the new field, and the // size of inter-field padding between the two. // // This will not panic (and is proven with Kani to not // panic) if the layout components can correspond to a // leading layout fragment of a valid Rust type, but may // panic otherwise (e.g., combining or aligning the // components would create a size exceeding // `usize::MAX`). let size = match offset.checked_add(field_size) { Some(size) => size, None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"), }; SizeInfo::Sized { size } } SizeInfo::SliceDst(TrailingSliceLayout { offset: trailing_offset, elem_size, }) => { // If the trailing field is dynamically sized, so too // will the resulting layout. The offset of the trailing // slice component is the sum of the offset of the // trailing field and the trailing slice offset within // that field. // // This will not panic (and is proven with Kani to not // panic) if the layout components can correspond to a // leading layout fragment of a valid Rust type, but may // panic otherwise (e.g., combining or aligning the // components would create a size exceeding // `usize::MAX`). let offset = match offset.checked_add(trailing_offset) { Some(offset) => offset, None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"), }; SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) } }, ) } }; let statically_shallow_unpadded = self.statically_shallow_unpadded && field.statically_shallow_unpadded && interfield_padding == 0; DstLayout { align, size_info, statically_shallow_unpadded } } /// Like `Layout::pad_to_align`, this routine rounds the size of this layout /// up to the nearest multiple of this type's alignment or `repr_packed` /// (whichever is less). This method leaves DST layouts unchanged, since the /// trailing padding of DSTs is computed at runtime. /// /// The accompanying boolean is `true` if the resulting composition of /// fields necessitated static (as opposed to dynamic) padding; otherwise /// `false`. /// /// In order to match the layout of a `#[repr(C)]` struct, this method /// should be invoked after the invocations of [`DstLayout::extend`]. If /// `self` corresponds to a type marked with `repr(packed(N))`, then /// `repr_packed` should be set to `Some(N)`, otherwise `None`. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// If a (potentially hypothetical) valid `repr(C)` type begins with fields /// whose layout are `self` followed only by zero or more bytes of trailing /// padding (not included in `self`), then unsafe code may rely on /// `self.pad_to_align(repr_packed)` producing a layout that correctly /// encapsulates the layout of that type. /// /// We make no guarantees to the behavior of this method if `self` cannot /// appear in a valid Rust type (e.g., because the addition of trailing /// padding would lead to a size larger than `isize::MAX`). #[doc(hidden)] #[must_use] #[inline] pub const fn pad_to_align(self) -> Self { use util::padding_needed_for; let (static_padding, size_info) = match self.size_info { // For sized layouts, we add the minimum amount of trailing padding // needed to satisfy alignment. SizeInfo::Sized { size: unpadded_size } => { let padding = padding_needed_for(unpadded_size, self.align); let size = match unpadded_size.checked_add(padding) {Lines 1–500 of 2227. Load more
// SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT // // Copyright 2024 The Fuchsia Authors // // Licensed under the 2-Clause BSD License <LICENSE-BSD or // https://opensource.org/license/bsd-2-clause>, Apache License, Version 2.0 // <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT // license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option. // This file may not be copied, modified, or distributed except according to // those terms. use core::{mem, num::NonZeroUsize}; use crate::util; /// The target pointer width, counted in bits. const POINTER_WIDTH_BITS: usize = mem::size_of::<usize>() * 8; /// The layout of a type which might be dynamically-sized. /// /// `DstLayout` describes the layout of sized types, slice types, and "slice /// DSTs" - ie, those that are known by the type system to have a trailing slice /// (as distinguished from `dyn Trait` types - such types *might* have a /// trailing slice type, but the type system isn't aware of it). /// /// Note that `DstLayout` does not have any internal invariants, so no guarantee /// is made that a `DstLayout` conforms to any of Rust's requirements regarding /// the layout of real Rust types or instances of types. #[doc(hidden)] #[allow(missing_debug_implementations, missing_copy_implementations)] #[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] pub struct DstLayout { pub(crate) align: NonZeroUsize, pub(crate) size_info: SizeInfo, // Is it guaranteed statically (without knowing a value's runtime metadata) // that the top-level type contains no padding? This does *not* apply // recursively - for example, `[(u8, u16)]` has `statically_shallow_unpadded // = true` even though this type likely has padding inside each `(u8, u16)`. pub(crate) statically_shallow_unpadded: bool, } #[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] pub(crate) enum SizeInfo<E = usize> { Sized { size: usize }, SliceDst(TrailingSliceLayout<E>), } #[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))] #[derive(Copy, Clone)] pub(crate) struct TrailingSliceLayout<E = usize> { // The offset of the first byte of the trailing slice field. Note that this // is NOT the same as the minimum size of the type. For example, consider // the following type: // // struct Foo { // a: u16, // b: u8, // c: [u8], // } // // In `Foo`, `c` is at byte offset 3. When `c.len() == 0`, `c` is followed // by a padding byte. pub(crate) offset: usize, // The size of the element type of the trailing slice field. pub(crate) elem_size: E, } impl SizeInfo { /// Attempts to create a `SizeInfo` from `Self` in which `elem_size` is a /// `NonZeroUsize`. If `elem_size` is 0, returns `None`. #[allow(unused)] #[cfg_attr(not(zerocopy_inline_always), inline)] #[cfg_attr(zerocopy_inline_always, inline(always))] const fn try_to_nonzero_elem_size(&self) -> Option<SizeInfo<NonZeroUsize>> { Some(match *self { SizeInfo::Sized { size } => SizeInfo::Sized { size }, SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) => { if let Some(elem_size) = NonZeroUsize::new(elem_size) { SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) } else { return None; } } }) } } #[doc(hidden)] #[derive(Copy, Clone)] #[cfg_attr(test, derive(Debug))] #[allow(missing_debug_implementations)] pub enum CastType { Prefix, Suffix, } #[cfg_attr(test, derive(Debug))] pub(crate) enum MetadataCastError { Alignment, Size, } impl DstLayout { /// The minimum possible alignment of a type. const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) { Some(min_align) => min_align, None => const_unreachable!(), }; /// The maximum theoretic possible alignment of a type. /// /// For compatibility with future Rust versions, this is defined as the /// maximum power-of-two that fits into a `usize`. See also /// [`DstLayout::CURRENT_MAX_ALIGN`]. pub(crate) const THEORETICAL_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) { Some(max_align) => max_align, None => const_unreachable!(), }; /// The current, documented max alignment of a type \[1\]. /// /// \[1\] Per <https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers>: /// /// The alignment value must be a power of two from 1 up to /// 2<sup>29</sup>. #[cfg(not(kani))] #[cfg(not(target_pointer_width = "16"))] pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) { Some(max_align) => max_align, None => const_unreachable!(), }; #[cfg(not(kani))] #[cfg(target_pointer_width = "16")] pub(crate) const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 15) { Some(max_align) => max_align, None => const_unreachable!(), }; /// The maximum size of an allocation \[1\]. /// /// \[1\] Per <https://doc.rust-lang.org/1.91.1/std/ptr/index.html#allocation>: /// /// For any allocation with base `address`, `size`, and a set of `addresses`, /// the following are guaranteed: [..] /// /// - `size <= isize::MAX` /// #[allow(clippy::as_conversions)] pub(crate) const MAX_SIZE: usize = isize::MAX as usize; /// Assumes that this layout lacks static shallow padding. /// /// # Panics /// /// This method does not panic. /// /// # Safety /// /// If `self` describes the size and alignment of type that lacks static /// shallow padding, unsafe code may assume that the result of this method /// accurately reflects the size, alignment, and lack of static shallow /// padding of that type. const fn assume_shallow_unpadded(self) -> Self { Self { statically_shallow_unpadded: true, ..self } } /// Constructs a `DstLayout` for a zero-sized type with `repr_align` /// alignment (or 1). If `repr_align` is provided, then it must be a power /// of two. /// /// # Panics /// /// This function panics if the supplied `repr_align` is not a power of two. /// /// # Safety /// /// Unsafe code may assume that the contract of this function is satisfied. #[doc(hidden)] #[must_use] #[inline] pub const fn new_zst(repr_align: Option<NonZeroUsize>) -> DstLayout { let align = match repr_align { Some(align) => align, None => Self::MIN_ALIGN, }; const_assert!(align.get().is_power_of_two()); DstLayout { align, size_info: SizeInfo::Sized { size: 0 }, statically_shallow_unpadded: true, } } /// Constructs a `DstLayout` which describes `T` and assumes `T` may contain /// padding. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. #[doc(hidden)] #[must_use] #[inline] pub const fn for_type<T>() -> DstLayout { // SAFETY: `align` is correct by construction. `T: Sized`, and so it is // sound to initialize `size_info` to `SizeInfo::Sized { size }`; the // `size` field is also correct by construction. `unpadded` can safely // default to `false`. DstLayout { align: match NonZeroUsize::new(mem::align_of::<T>()) { Some(align) => align, None => const_unreachable!(), }, size_info: SizeInfo::Sized { size: mem::size_of::<T>() }, statically_shallow_unpadded: false, } } /// Constructs a `DstLayout` which describes a `T` that does not contain /// padding. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `T`. #[doc(hidden)] #[must_use] #[inline] pub const fn for_unpadded_type<T>() -> DstLayout { Self::for_type::<T>().assume_shallow_unpadded() } /// Constructs a `DstLayout` which describes `[T]`. /// /// # Safety /// /// Unsafe code may assume that `DstLayout` is the correct layout for `[T]`. pub(crate) const fn for_slice<T>() -> DstLayout { // SAFETY: The alignment of a slice is equal to the alignment of its // element type, and so `align` is initialized correctly. // // Since this is just a slice type, there is no offset between the // beginning of the type and the beginning of the slice, so it is // correct to set `offset: 0`. The `elem_size` is correct by // construction. Since `[T]` is a (degenerate case of a) slice DST, it // is correct to initialize `size_info` to `SizeInfo::SliceDst`. DstLayout { align: match NonZeroUsize::new(mem::align_of::<T>()) { Some(align) => align, None => const_unreachable!(), }, size_info: SizeInfo::SliceDst(TrailingSliceLayout { offset: 0, elem_size: mem::size_of::<T>(), }), statically_shallow_unpadded: true, } } /// Constructs a complete `DstLayout` reflecting a `repr(C)` struct with the /// given alignment modifiers and fields. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// For any definition of a `repr(C)` struct, if this method is invoked with /// alignment modifiers and fields corresponding to that definition, the /// resulting `DstLayout` will correctly encode the layout of that struct. /// /// We make no guarantees to the behavior of this method when it is invoked /// with arguments that cannot correspond to a valid `repr(C)` struct. #[must_use] #[inline] pub const fn for_repr_c_struct( repr_align: Option<NonZeroUsize>, repr_packed: Option<NonZeroUsize>, fields: &[DstLayout], ) -> DstLayout { let mut layout = DstLayout::new_zst(repr_align); let mut i = 0; #[allow(clippy::arithmetic_side_effects)] while i < fields.len() { #[allow(clippy::indexing_slicing)] let field = fields[i]; layout = layout.extend(field, repr_packed); i += 1; } layout = layout.pad_to_align(); // SAFETY: `layout` accurately describes the layout of a `repr(C)` // struct with `repr_align` or `repr_packed` alignment modifications and // the given `fields`. The `layout` is constructed using a sequence of // invocations of `DstLayout::{new_zst,extend,pad_to_align}`. The // documentation of these items vows that invocations in this manner // will accurately describe a type, so long as: // // - that type is `repr(C)`, // - its fields are enumerated in the order they appear, // - the presence of `repr_align` and `repr_packed` are correctly accounted for. // // We respect all three of these preconditions above. layout } /// Like `Layout::extend`, this creates a layout that describes a record /// whose layout consists of `self` followed by `next` that includes the /// necessary inter-field padding, but not any trailing padding. /// /// In order to match the layout of a `#[repr(C)]` struct, this method /// should be invoked for each field in declaration order. To add trailing /// padding, call `DstLayout::pad_to_align` after extending the layout for /// all fields. If `self` corresponds to a type marked with /// `repr(packed(N))`, then `repr_packed` should be set to `Some(N)`, /// otherwise `None`. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// If a (potentially hypothetical) valid `repr(C)` Rust type begins with /// fields whose layout are `self`, and those fields are immediately /// followed by a field whose layout is `field`, then unsafe code may rely /// on `self.extend(field, repr_packed)` producing a layout that correctly /// encompasses those two components. /// /// We make no guarantees to the behavior of this method if these fragments /// cannot appear in a valid Rust type (e.g., the concatenation of the /// layouts would lead to a size larger than `isize::MAX`). #[doc(hidden)] #[must_use] #[inline] pub const fn extend(self, field: DstLayout, repr_packed: Option<NonZeroUsize>) -> Self { use util::{max, min, padding_needed_for}; // If `repr_packed` is `None`, there are no alignment constraints, and // the value can be defaulted to `THEORETICAL_MAX_ALIGN`. let max_align = match repr_packed { Some(max_align) => max_align, None => Self::THEORETICAL_MAX_ALIGN, }; const_assert!(max_align.get().is_power_of_two()); // We use Kani to prove that this method is robust to future increases // in Rust's maximum allowed alignment. However, if such a change ever // actually occurs, we'd like to be notified via assertion failures. #[cfg(not(kani))] { const_debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); const_debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); if let Some(repr_packed) = repr_packed { const_debug_assert!(repr_packed.get() <= DstLayout::CURRENT_MAX_ALIGN.get()); } } // The field's alignment is clamped by `repr_packed` (i.e., the // `repr(packed(N))` attribute, if any) [1]. // // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: // // The alignments of each field, for the purpose of positioning // fields, is the smaller of the specified alignment and the alignment // of the field's type. let field_align = min(field.align, max_align); // The struct's alignment is the maximum of its previous alignment and // `field_align`. let align = max(self.align, field_align); let (interfield_padding, size_info) = match self.size_info { // If the layout is already a DST, we panic; DSTs cannot be extended // with additional fields. SizeInfo::SliceDst(..) => const_panic!("Cannot extend a DST with additional fields."), SizeInfo::Sized { size: preceding_size } => { // Compute the minimum amount of inter-field padding needed to // satisfy the field's alignment, and offset of the trailing // field. [1] // // [1] Per https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers: // // Inter-field padding is guaranteed to be the minimum // required in order to satisfy each field's (possibly // altered) alignment. let padding = padding_needed_for(preceding_size, field_align); // This will not panic (and is proven to not panic, with Kani) // if the layout components can correspond to a leading layout // fragment of a valid Rust type, but may panic otherwise (e.g., // combining or aligning the components would create a size // exceeding `isize::MAX`). let offset = match preceding_size.checked_add(padding) { Some(offset) => offset, None => const_panic!("Adding padding to `self`'s size overflows `usize`."), }; ( padding, match field.size_info { SizeInfo::Sized { size: field_size } => { // If the trailing field is sized, the resulting layout // will be sized. Its size will be the sum of the // preceding layout, the size of the new field, and the // size of inter-field padding between the two. // // This will not panic (and is proven with Kani to not // panic) if the layout components can correspond to a // leading layout fragment of a valid Rust type, but may // panic otherwise (e.g., combining or aligning the // components would create a size exceeding // `usize::MAX`). let size = match offset.checked_add(field_size) { Some(size) => size, None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"), }; SizeInfo::Sized { size } } SizeInfo::SliceDst(TrailingSliceLayout { offset: trailing_offset, elem_size, }) => { // If the trailing field is dynamically sized, so too // will the resulting layout. The offset of the trailing // slice component is the sum of the offset of the // trailing field and the trailing slice offset within // that field. // // This will not panic (and is proven with Kani to not // panic) if the layout components can correspond to a // leading layout fragment of a valid Rust type, but may // panic otherwise (e.g., combining or aligning the // components would create a size exceeding // `usize::MAX`). let offset = match offset.checked_add(trailing_offset) { Some(offset) => offset, None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"), }; SizeInfo::SliceDst(TrailingSliceLayout { offset, elem_size }) } }, ) } }; let statically_shallow_unpadded = self.statically_shallow_unpadded && field.statically_shallow_unpadded && interfield_padding == 0; DstLayout { align, size_info, statically_shallow_unpadded } } /// Like `Layout::pad_to_align`, this routine rounds the size of this layout /// up to the nearest multiple of this type's alignment or `repr_packed` /// (whichever is less). This method leaves DST layouts unchanged, since the /// trailing padding of DSTs is computed at runtime. /// /// The accompanying boolean is `true` if the resulting composition of /// fields necessitated static (as opposed to dynamic) padding; otherwise /// `false`. /// /// In order to match the layout of a `#[repr(C)]` struct, this method /// should be invoked after the invocations of [`DstLayout::extend`]. If /// `self` corresponds to a type marked with `repr(packed(N))`, then /// `repr_packed` should be set to `Some(N)`, otherwise `None`. /// /// This method cannot be used to match the layout of a record with the /// default representation, as that representation is mostly unspecified. /// /// # Safety /// /// If a (potentially hypothetical) valid `repr(C)` type begins with fields /// whose layout are `self` followed only by zero or more bytes of trailing /// padding (not included in `self`), then unsafe code may rely on /// `self.pad_to_align(repr_packed)` producing a layout that correctly /// encapsulates the layout of that type. /// /// We make no guarantees to the behavior of this method if `self` cannot /// appear in a valid Rust type (e.g., because the addition of trailing /// padding would lead to a size larger than `isize::MAX`). #[doc(hidden)] #[must_use] #[inline] pub const fn pad_to_align(self) -> Self { use util::padding_needed_for; let (static_padding, size_info) = match self.size_info { // For sized layouts, we add the minimum amount of trailing padding // needed to satisfy alignment. SizeInfo::Sized { size: unpadded_size } => { let padding = padding_needed_for(unpadded_size, self.align); let size = match unpadded_size.checked_add(padding) {