zerocopy 0.7.18

Utilities for zero-copy parsing and serialization
Documentation
// Copyright 2022 The Fuchsia Authors
//
// Licensed under a BSD-style license <LICENSE-BSD>, 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.

//! Utilities used by macros and by `zerocopy-derive`.
//!
//! These are defined here `zerocopy` rather than in code generated by macros or
//! by `zerocopy-derive` so that they can be compiled once rather than
//! recompiled for every invocation (e.g., if they were defined in generated
//! code, then deriving `AsBytes` and `FromBytes` on three different types would
//! result in the code in question being emitted and compiled six different
//! times).

#![allow(missing_debug_implementations)]

use core::{marker::PhantomData, mem::ManuallyDrop};

/// A compile-time check that should be one particular value.
pub trait ShouldBe<const VALUE: bool> {}

/// A struct for checking whether `T` contains padding.
pub struct HasPadding<T: ?Sized, const VALUE: bool>(PhantomData<T>);

impl<T: ?Sized, const VALUE: bool> ShouldBe<VALUE> for HasPadding<T, VALUE> {}

/// A type whose size is equal to `align_of::<T>()`.
#[repr(C)]
pub struct AlignOf<T> {
    // This field ensures that:
    // - The size is always at least 1 (the minimum possible alignment).
    // - If the alignment is greater than 1, Rust has to round up to the next
    //   multiple of it in order to make sure that `Align`'s size is a multiple
    //   of that alignment. Without this field, its size could be 0, which is a
    //   valid multiple of any alignment.
    _u: u8,
    _a: [T; 0],
}

impl<T> AlignOf<T> {
    #[inline(never)] // Make `missing_inline_in_public_items` happy.
    pub fn into_t(self) -> T {
        unreachable!()
    }
}

/// A type whose size is equal to `max(align_of::<T>(), align_of::<U>())`.
#[repr(C)]
pub union MaxAlignsOf<T, U> {
    _t: ManuallyDrop<AlignOf<T>>,
    _u: ManuallyDrop<AlignOf<U>>,
}

impl<T, U> MaxAlignsOf<T, U> {
    #[inline(never)] // Make `missing_inline_in_public_items` happy.
    pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> {
        unreachable!()
    }
}

/// Does the struct type `$t` have padding?
///
/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
/// struct type, or else `struct_has_padding!`'s result may be meaningless.
///
/// Note that `struct_has_padding!`'s results are independent of `repr` since
/// they only consider the size of the type and the sizes of the fields.
/// Whatever the repr, the size of the type already takes into account any
/// padding that the compiler has decided to add. Structs with well-defined
/// representations (such as `repr(C)`) can use this macro to check for padding.
/// Note that while this may yield some consistent value for some `repr(Rust)`
/// structs, it is not guaranteed across platforms or compilations.
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
#[macro_export]
macro_rules! struct_has_padding {
    ($t:ty, $($ts:ty),*) => {
        core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())*
    };
}

/// Does the union type `$t` have padding?
///
/// `$ts` is the list of the type of every field in `$t`. `$t` must be a
/// union type, or else `union_has_padding!`'s result may be meaningless.
///
/// Note that `union_has_padding!`'s results are independent of `repr` since
/// they only consider the size of the type and the sizes of the fields.
/// Whatever the repr, the size of the type already takes into account any
/// padding that the compiler has decided to add. Unions with well-defined
/// representations (such as `repr(C)`) can use this macro to check for padding.
/// Note that while this may yield some consistent value for some `repr(Rust)`
/// unions, it is not guaranteed across platforms or compilations.
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
#[macro_export]
macro_rules! union_has_padding {
    ($t:ty, $($ts:ty),*) => {
        false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())*
    };
}

/// Does `t` have alignment greater than or equal to `u`?  If not, this macro
/// produces a compile error. It must be invoked in a dead codepath. This is
/// used in `transmute_ref!` and `transmute_mut!`.
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
#[macro_export]
macro_rules! assert_align_gt_eq {
    ($t:ident, $u: ident) => {{
        // The comments here should be read in the context of this macro's
        // invocations in `transmute_ref!` and `transmute_mut!`.
        if false {
            // The type wildcard in this bound is inferred to be `T` because
            // `align_of.into_t()` is assigned to `t` (which has type `T`).
            let align_of: $crate::macro_util::AlignOf<_> = unreachable!();
            $t = align_of.into_t();
            // `max_aligns` is inferred to have type `MaxAlignsOf<T, U>` because
            // of the inferred types of `t` and `u`.
            let mut max_aligns = $crate::macro_util::MaxAlignsOf::new($t, $u);

            // This transmute will only compile successfully if
            // `align_of::<T>() == max(align_of::<T>(), align_of::<U>())` - in
            // other words, if `align_of::<T>() >= align_of::<U>()`.
            //
            // SAFETY: This code is never run.
            max_aligns = unsafe { $crate::macro_util::core_reexport::mem::transmute(align_of) };
        } else {
            loop {}
        }
    }};
}

/// Do `t` and `u` have the same size?  If not, this macro produces a compile
/// error. It must be invoked in a dead codepath. This is used in
/// `transmute_ref!` and `transmute_mut!`.
#[doc(hidden)] // `#[macro_export]` bypasses this module's `#[doc(hidden)]`.
#[macro_export]
macro_rules! assert_size_eq {
    ($t:ident, $u: ident) => {{
        // The comments here should be read in the context of this macro's
        // invocations in `transmute_ref!` and `transmute_mut!`.
        if false {
            // SAFETY: This code is never run.
            $u = unsafe {
                // Clippy: It's okay to transmute a type to itself.
                #[allow(clippy::useless_transmute)]
                $crate::macro_util::core_reexport::mem::transmute($t)
            };
        } else {
            loop {}
        }
    }};
}

pub mod core_reexport {
    pub mod mem {
        pub use core::mem::transmute;
    }
}

#[cfg(test)]
mod tests {
    use core::mem;

    use super::*;
    use crate::util::testutil::*;

    #[test]
    fn test_align_of() {
        macro_rules! test {
            ($ty:ty) => {
                assert_eq!(mem::size_of::<AlignOf<$ty>>(), mem::align_of::<$ty>());
            };
        }

        test!(());
        test!(u8);
        test!(AU64);
        test!([AU64; 2]);
    }

    #[test]
    fn test_max_aligns_of() {
        macro_rules! test {
            ($t:ty, $u:ty) => {
                assert_eq!(
                    mem::size_of::<MaxAlignsOf<$t, $u>>(),
                    core::cmp::max(mem::align_of::<$t>(), mem::align_of::<$u>())
                );
            };
        }

        test!(u8, u8);
        test!(u8, AU64);
        test!(AU64, u8);
    }

    #[test]
    fn test_typed_align_check() {
        // Test that the type-based alignment check used in
        // `assert_align_gt_eq!` behaves as expected.

        macro_rules! assert_t_align_gteq_u_align {
            ($t:ty, $u:ty, $gteq:expr) => {
                assert_eq!(
                    mem::size_of::<MaxAlignsOf<$t, $u>>() == mem::size_of::<AlignOf<$t>>(),
                    $gteq
                );
            };
        }

        assert_t_align_gteq_u_align!(u8, u8, true);
        assert_t_align_gteq_u_align!(AU64, AU64, true);
        assert_t_align_gteq_u_align!(AU64, u8, true);
        assert_t_align_gteq_u_align!(u8, AU64, false);
    }

    #[test]
    fn test_struct_has_padding() {
        // Test that, for each provided repr, `struct_has_padding!` reports the
        // expected value.
        macro_rules! test {
            (#[$cfg:meta] ($($ts:ty),*) => $expect:expr) => {{
                #[$cfg]
                struct Test($($ts),*);
                assert_eq!(struct_has_padding!(Test, $($ts),*), $expect);
            }};
            (#[$cfg:meta] $(#[$cfgs:meta])* ($($ts:ty),*) => $expect:expr) => {
                test!(#[$cfg] ($($ts),*) => $expect);
                test!($(#[$cfgs])* ($($ts),*) => $expect);
            };
        }

        test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] () => false);
        test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8) => false);
        test!(#[repr(C)] #[repr(transparent)] #[repr(packed)] (u8, ()) => false);
        test!(#[repr(C)] #[repr(packed)] (u8, u8) => false);

        test!(#[repr(C)] (u8, AU64) => true);
        // Rust won't let you put `#[repr(packed)]` on a type which contains a
        // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here.
        // It's not ideal, but it definitely has align > 1 on /some/ of our CI
        // targets, and this isn't a particularly complex macro we're testing
        // anyway.
        test!(#[repr(packed)] (u8, u64) => false);
    }

    #[test]
    fn test_union_has_padding() {
        // Test that, for each provided repr, `union_has_padding!` reports the
        // expected value.
        macro_rules! test {
            (#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{
                #[$cfg]
                #[allow(unused)] // fields are never read
                union Test{ $($fs: $ts),* }
                assert_eq!(union_has_padding!(Test, $($ts),*), $expect);
            }};
            (#[$cfg:meta] $(#[$cfgs:meta])* {$($fs:ident: $ts:ty),*} => $expect:expr) => {
                test!(#[$cfg] {$($fs: $ts),*} => $expect);
                test!($(#[$cfgs])* {$($fs: $ts),*} => $expect);
            };
        }

        test!(#[repr(C)] #[repr(packed)] {a: u8} => false);
        test!(#[repr(C)] #[repr(packed)] {a: u8, b: u8} => false);

        // Rust won't let you put `#[repr(packed)]` on a type which contains a
        // `#[repr(align(n > 1))]` type (`AU64`), so we have to use `u64` here.
        // It's not ideal, but it definitely has align > 1 on /some/ of our CI
        // targets, and this isn't a particularly complex macro we're testing
        // anyway.
        test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true);
    }
}