pub(crate) trait Witnessed<Sdk> {}
macro_rules! witness {
(
$test:ident: $shape:tt $ours:ident => $sdk:ident {
$($field:ident: $row:ident $($path:tt).+;)+
}
$(also { $($also:ident: $also_row:ident $($also_path:tt).+;)+ })?
) => {
impl Witnessed<$sdk> for $ours {}
#[test]
fn $test() {
witness!(@shape $shape $ours $sdk $($field)+);
$(witness!(@row $row $ours $sdk $field $($path).+);)+
$($(witness!(@row $also_row $ours $sdk $also $($also_path).+);)+)?
}
};
(@shape struct $ours:ident $sdk:ident $($field:ident)+) => {
let _every_field_has_a_row: fn($ours) = |value| {
let $ours { $($field: _),+ } = value;
};
witness!(@size $ours $sdk);
witness!(@align $ours $sdk);
};
(@shape union $ours:ident $sdk:ident $($field:ident)+) => {
witness!(@size $ours $sdk);
witness!(@align $ours $sdk);
};
(@shape packed $ours:ident $sdk:ident $($field:ident)+) => {
let _every_field_has_a_row: fn($ours) = |value| {
let $ours { $($field: _),+ } = value;
};
witness!(@size $ours $sdk);
assert_eq!(
align_of::<$sdk>(),
1,
concat!(stringify!($sdk), " is byte-packed")
);
};
(@size $ours:ident $sdk:ident) => {
assert_eq!(
size_of::<$ours>(),
size_of::<$sdk>(),
concat!("size of ", stringify!($ours), " against ", stringify!($sdk))
);
};
(@align $ours:ident $sdk:ident) => {
assert_eq!(
align_of::<$ours>(),
align_of::<$sdk>(),
concat!("alignment of ", stringify!($ours), " against ", stringify!($sdk))
);
};
(@row is $ours:ident $sdk:ident $field:ident $($path:tt).+) => {{
fn same_type<A, B, T>(_ours: fn(*const A) -> *const T, _sdk: fn(*const B) -> *const T) {}
same_type(
|value: *const $ours| unsafe { &raw const (*value).$field },
|value: *const $sdk| unsafe { &raw const (*value).$($path).+ },
);
witness!(@offset $ours $sdk $field $($path).+);
}};
(@row nested $ours:ident $sdk:ident $field:ident $($path:tt).+) => {{
fn witnessed<A, B, T: Witnessed<U>, U>(
_ours: fn(*const A) -> *const T,
_sdk: fn(*const B) -> *const U,
) {
}
witnessed(
|value: *const $ours| unsafe { &raw const (*value).$field },
|value: *const $sdk| unsafe { &raw const (*value).$($path).+ },
);
witness!(@offset $ours $sdk $field $($path).+);
}};
(@row width $ours:ident $sdk:ident $field:ident $($path:tt).+) => {{
fn width<A, T>(_select: fn(*const A) -> *const T) -> usize {
size_of::<T>()
}
assert_eq!(
width(|value: *const $ours| unsafe { &raw const (*value).$field }),
width(|value: *const $sdk| unsafe { &raw const (*value).$($path).+ }),
concat!("width of ", stringify!($ours), "::", stringify!($field))
);
witness!(@offset $ours $sdk $field $($path).+);
}};
(@row after $ours:ident $sdk:ident $field:ident $($path:tt).+) => {{
fn width<A, T>(_select: fn(*const A) -> *const T) -> usize {
size_of::<T>()
}
assert_eq!(
core::mem::offset_of!($ours, $field),
core::mem::offset_of!($sdk, $($path).+)
+ width(|value: *const $sdk| unsafe { &raw const (*value).$($path).+ }),
concat!(
stringify!($ours), "::", stringify!($field),
" begins where ", stringify!($sdk), "::", stringify!($($path).+), " ends"
)
);
}};
(@offset $ours:ident $sdk:ident $field:ident $($path:tt).+) => {
assert_eq!(
core::mem::offset_of!($ours, $field),
core::mem::offset_of!($sdk, $($path).+),
concat!(
"offset of ", stringify!($ours), "::", stringify!($field),
" against ", stringify!($sdk), "::", stringify!($($path).+)
)
);
};
}
pub(crate) use witness;