#[doc(hidden)]
#[macro_export]
macro_rules! _memoffset__compile_error {
($($inner:tt)*) => {
compile_error! { $($inner)* }
}
}
#[macro_export(local_inner_macros)]
macro_rules! span_of {
(@helper $root:ident, [] ..=) => {
_memoffset__compile_error!("Expected a range, found '..='")
};
(@helper $root:ident, [] ..) => {
_memoffset__compile_error!("Expected a range, found '..'")
};
(@helper $root:ident, $parent:path, [] ..) => {{
($root as usize,
$root as usize + $crate::mem::size_of_val(&(*$root)))
}};
(@helper $root:ident, $parent:path, [] ..= $field:tt) => {{
_memoffset__field_check!($parent, $field);
($root as usize,
&(*$root).$field as *const _ as usize + $crate::mem::size_of_val(&(*$root).$field))
}};
(@helper $root:ident, $parent:path, [] .. $field:tt) => {{
_memoffset__field_check!($parent, $field);
($root as usize, &(*$root).$field as *const _ as usize)
}};
(@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{
_memoffset__field_check!($parent, $begin);
_memoffset__field_check!($parent, $end);
(&(*$root).$begin as *const _ as usize,
&(*$root).$end as *const _ as usize + $crate::mem::size_of_val(&(*$root).$end))
}};
(@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{
_memoffset__field_check!($parent, $begin);
_memoffset__field_check!($parent, $end);
(&(*$root).$begin as *const _ as usize,
&(*$root).$end as *const _ as usize)
}};
(@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{
_memoffset__field_check!($parent, $begin);
(&(*$root).$begin as *const _ as usize,
$root as usize + $crate::mem::size_of_val(&*$root))
}};
(@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{
_memoffset__compile_error!(
"Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?")
}};
(@helper $root:ident, $parent:path, # $begin:tt []) => {{
_memoffset__field_check!($parent, $begin);
(&(*$root).$begin as *const _ as usize,
&(*$root).$begin as *const _ as usize + $crate::mem::size_of_val(&(*$root).$begin))
}};
(@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{
span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*)
}};
(@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{
span_of!(@helper $root, $parent, #$tt [] $($rest)*)
}};
($sty:path, $($exp:tt)+) => ({
unsafe {
_memoffset__let_base_ptr!(root, $sty);
let base = root as usize;
let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*);
begin-base..end-base
}
});
}
#[cfg(test)]
mod tests {
use core::mem;
#[test]
fn span_simple() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!(span_of!(Foo, a), 0..4);
assert_eq!(span_of!(Foo, b), 4..6);
assert_eq!(span_of!(Foo, c), 8..8 + 8);
}
#[test]
#[cfg_attr(miri, ignore)]
fn span_simple_packed() {
#[repr(C, packed)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!(span_of!(Foo, a), 0..4);
assert_eq!(span_of!(Foo, b), 4..6);
assert_eq!(span_of!(Foo, c), 6..6 + 8);
}
#[test]
fn span_forms() {
#[repr(C)]
struct Florp {
a: u32,
}
#[repr(C)]
struct Blarg {
x: u64,
y: [u8; 56],
z: Florp,
egg: [[u8; 4]; 5],
}
assert_eq!(0..8, span_of!(Blarg, x));
assert_eq!(64..68, span_of!(Blarg, z));
assert_eq!(68..mem::size_of::<Blarg>(), span_of!(Blarg, egg));
assert_eq!(8..64, span_of!(Blarg, y..z));
assert_eq!(0..64, span_of!(Blarg, x..=y));
}
#[test]
fn ig_test() {
#[repr(C)]
struct Member {
foo: u32,
}
#[repr(C)]
struct Test {
x: u64,
y: [u8; 56],
z: Member,
egg: [[u8; 4]; 4],
}
assert_eq!(span_of!(Test, ..x), 0..0);
assert_eq!(span_of!(Test, ..=x), 0..8);
assert_eq!(span_of!(Test, ..y), 0..8);
assert_eq!(span_of!(Test, ..=y), 0..64);
assert_eq!(span_of!(Test, ..z), 0..64);
assert_eq!(span_of!(Test, ..=z), 0..68);
assert_eq!(span_of!(Test, ..egg), 0..68);
assert_eq!(span_of!(Test, ..=egg), 0..84);
assert_eq!(span_of!(Test, ..), 0..mem::size_of::<Test>());
assert_eq!(
span_of!(Test, x..),
offset_of!(Test, x)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, y..),
offset_of!(Test, y)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, z..),
offset_of!(Test, z)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, egg..),
offset_of!(Test, egg)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, x..y),
offset_of!(Test, x)..offset_of!(Test, y)
);
assert_eq!(
span_of!(Test, x..=y),
offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>()
);
}
}