2227 lines · 98.6 KB · Rust
RawDownload
// 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.