#[cfg(maybe_uninit)]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:path) => {
let uninit = $crate::mem::MaybeUninit::<$type>::uninit();
let $name = uninit.as_ptr();
};
}
#[cfg(not(maybe_uninit))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__let_base_ptr {
($name:ident, $type:path) => {
let $name = $crate::mem::align_of::<$type>() as *const $type;
};
}
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset__field_check {
($type:path, $field:tt) => {
#[cfg_attr(allow_clippy, allow(clippy::unneeded_field_pattern))]
let $type { $field: _, .. };
};
}
#[macro_export(local_inner_macros)]
macro_rules! raw_field {
($base:expr, $parent:path, $field:tt) => {{
_memoffset__field_check!($parent, $field);
let base_ptr: *const $parent = $base;
#[allow(unused_unsafe)] unsafe {
&(*base_ptr).$field as *const _
}
}};
}
#[cfg(not(feature = "unstable_const"))]
#[macro_export(local_inner_macros)]
macro_rules! offset_of {
($parent:path, $field:tt) => {{
_memoffset__let_base_ptr!(base_ptr, $parent);
let field_ptr = raw_field!(base_ptr, $parent, $field);
(field_ptr as usize) - (base_ptr as usize)
}};
}
#[cfg(feature = "unstable_const")]
#[macro_export(local_inner_macros)]
macro_rules! offset_of {
($parent:path, $field:tt) => {{
let uninit = $crate::mem::MaybeUninit::<$parent>::uninit();
#[allow(unused_unsafe)] unsafe {
let base_ptr = $crate::mem::transmute::<_, &$parent>(&uninit) as *const $parent;
let field_ptr = raw_field!(base_ptr, $parent, $field);
(field_ptr as *const u8).offset_from(base_ptr as *const u8) as usize
}
}};
}
#[cfg(test)]
mod tests {
#[test]
fn offset_simple() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!(offset_of!(Foo, a), 0);
assert_eq!(offset_of!(Foo, b), 4);
assert_eq!(offset_of!(Foo, c), 8);
}
#[test]
#[cfg_attr(miri, ignore)] fn offset_simple_packed() {
#[repr(C, packed)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!(offset_of!(Foo, a), 0);
assert_eq!(offset_of!(Foo, b), 4);
assert_eq!(offset_of!(Foo, c), 6);
}
#[test]
fn tuple_struct() {
#[repr(C)]
struct Tup(i32, i32);
assert_eq!(offset_of!(Tup, 0), 0);
assert_eq!(offset_of!(Tup, 1), 4);
}
#[test]
fn path() {
mod sub {
#[repr(C)]
pub struct Foo {
pub x: u32,
}
}
assert_eq!(offset_of!(sub::Foo, x), 0);
}
#[test]
fn inside_generic_method() {
struct Pair<T, U>(T, U);
fn foo<T, U>(_: Pair<T, U>) -> usize {
offset_of!(Pair<T, U>, 1)
}
assert_eq!(foo(Pair(0, 0)), 4);
}
#[test]
fn test_raw_field() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
let f: Foo = Foo {
a: 0,
b: [0, 0],
c: 0,
};
let f_ptr = &f as *const _;
assert_eq!(f_ptr as usize + 0, raw_field!(f_ptr, Foo, a) as usize);
assert_eq!(f_ptr as usize + 4, raw_field!(f_ptr, Foo, b) as usize);
assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize);
}
#[cfg(feature = "unstable_const")]
#[test]
fn const_offset() {
#[repr(C)]
struct Foo {
a: u32,
b: [u8; 2],
c: i64,
}
assert_eq!([0; offset_of!(Foo, b)].len(), 4);
}
}