Skip to content

Commit 1808393

Browse files
committed
Add an offset_of macro
We can't yet use the standard library's macro since it isn't in our MSRV, but there are a couple of applicatons for having `offset_of` available. Add a polyfill for now.
1 parent bf2bd19 commit 1808393

File tree

1 file changed

+37
-0
lines changed

1 file changed

+37
-0
lines changed

src/macros.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,23 @@ macro_rules! __item {
389389
};
390390
}
391391

392+
/// Polyfill for std's `offset_of`.
393+
// FIXME(msrv): stabilized in std in 1.77
394+
macro_rules! offset_of {
395+
($Ty:path, $field:ident) => {{
396+
// Taken from bytemuck, avoids accidentally calling on deref
397+
#[allow(clippy::unneeded_field_pattern)]
398+
let $Ty { $field: _, .. };
399+
let data = core::mem::MaybeUninit::<$Ty>::uninit();
400+
let ptr = data.as_ptr();
401+
// SAFETY: computed address is inbounds since we have a stack alloc for T
402+
let fptr = unsafe { core::ptr::addr_of!((*ptr).$field) };
403+
let off = (fptr as usize).checked_sub(ptr as usize).unwrap();
404+
assert!(off <= core::mem::size_of::<$Ty>());
405+
off
406+
}};
407+
}
408+
392409
#[cfg(test)]
393410
mod tests {
394411
use core::any::TypeId;
@@ -490,6 +507,26 @@ mod tests {
490507
fn type_id_of_val<T: 'static>(_: &T) -> TypeId {
491508
TypeId::of::<T>()
492509
}
510+
511+
#[test]
512+
fn test_offset_of() {
513+
#[repr(C)]
514+
struct Off1 {
515+
a: u8,
516+
b: u32,
517+
c: Off2,
518+
d: u64,
519+
}
520+
521+
#[repr(C)]
522+
#[repr(align(128))]
523+
struct Off2 {}
524+
525+
assert_eq!(core::mem::offset_of!(Off1, a), offset_of!(Off1, a));
526+
assert_eq!(core::mem::offset_of!(Off1, b), offset_of!(Off1, b));
527+
assert_eq!(core::mem::offset_of!(Off1, c), offset_of!(Off1, c));
528+
assert_eq!(core::mem::offset_of!(Off1, d), offset_of!(Off1, d));
529+
}
493530
}
494531

495532
#[cfg(test)]

0 commit comments

Comments
 (0)