// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

//! Utilities used by `zerocopy-derive`.
//!
//! These are defined in `zerocopy` rather than in code generated by
//! `zerocopy-derive` so that they can be compiled once rather than recompiled
//! for every pair of type and trait (in other words, 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;

/// 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> {}

/// 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>())*
    };
}
