#![allow(missing_debug_implementations)]
use core::{marker::PhantomData, mem::ManuallyDrop};
pub trait ShouldBe<const VALUE: bool> {}
pub struct HasPadding<T: ?Sized, const VALUE: bool>(PhantomData<T>);
impl<T: ?Sized, const VALUE: bool> ShouldBe<VALUE> for HasPadding<T, VALUE> {}
#[repr(C)]
pub struct AlignOf<T> {
_u: u8,
_a: [T; 0],
}
impl<T> AlignOf<T> {
#[inline(never)] pub fn into_t(self) -> T {
unreachable!()
}
}
#[repr(C)]
pub union MaxAlignsOf<T, U> {
_t: ManuallyDrop<AlignOf<T>>,
_u: ManuallyDrop<AlignOf<U>>,
}
impl<T, U> MaxAlignsOf<T, U> {
#[inline(never)] pub fn new(_t: T, _u: U) -> MaxAlignsOf<T, U> {
unreachable!()
}
}
#[doc(hidden)] #[macro_export]
macro_rules! struct_has_padding {
($t:ty, $($ts:ty),*) => {
core::mem::size_of::<$t>() > 0 $(+ core::mem::size_of::<$ts>())*
};
}
#[doc(hidden)] #[macro_export]
macro_rules! union_has_padding {
($t:ty, $($ts:ty),*) => {
false $(|| core::mem::size_of::<$t>() != core::mem::size_of::<$ts>())*
};
}
#[doc(hidden)] #[macro_export]
macro_rules! assert_align_gt_eq {
($t:ident, $u: ident) => {{
if false {
let align_of: $crate::macro_util::AlignOf<_> = unreachable!();
$t = align_of.into_t();
let mut max_aligns = $crate::macro_util::MaxAlignsOf::new($t, $u);
max_aligns = unsafe { $crate::macro_util::core_reexport::mem::transmute(align_of) };
} else {
loop {}
}
}};
}
#[doc(hidden)] #[macro_export]
macro_rules! assert_size_eq {
($t:ident, $u: ident) => {{
if false {
$u = unsafe {
#[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() {
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() {
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);
test!(#[repr(packed)] (u8, u64) => false);
}
#[test]
fn test_union_has_padding() {
macro_rules! test {
(#[$cfg:meta] {$($fs:ident: $ts:ty),*} => $expect:expr) => {{
#[$cfg]
#[allow(unused)] 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);
test!(#[repr(C)] #[repr(packed)] {a: u8, b: u64} => true);
}
}