dataview/
offset_of.rs

1
2/// Returns the offset of a field.
3///
4/// ```
5/// #[repr(C)]
6/// struct Data {
7/// 	byte: u8,
8/// 	float: f32,
9/// }
10///
11/// let offset = dataview::offset_of!(Data.float);
12/// assert_eq!(offset, 4);
13///
14/// const OFFSET: usize = dataview::offset_of!(Data.float);
15/// assert_eq!(OFFSET, 4);
16/// ```
17///
18/// The syntax is `$ty.$field`.
19#[macro_export]
20macro_rules! offset_of {
21	($($tt:tt)*) => {
22		$crate::__offset_of2!([] $($tt)*)
23	};
24}
25
26#[doc(hidden)]
27#[macro_export]
28macro_rules! __offset_of2 {
29	([$($ty:tt)*] . $($field:tt)*) => {
30		::core::mem::offset_of!($($ty)*, $($field)*)
31	};
32	([$($ty:tt)*] $tt:tt $($tail:tt)*) => {
33		$crate::__offset_of2!([$($ty)* $tt] $($tail)*)
34	};
35	([$($ty:tt)*]) => {
36		compile_error!("missing field access")
37	};
38}
39
40/// Returns the `start..end` offsets of a field.
41///
42/// ```
43/// #[repr(C)]
44/// struct Data {
45/// 	byte: u8,
46/// 	float: f32,
47/// }
48///
49/// let span = dataview::span_of!(Data.float);
50/// assert_eq!(span, 4..8);
51/// assert_eq!(span.len(), 4);
52///
53/// const SPAN: std::ops::Range<usize> = dataview::span_of!(Data.float);
54/// assert_eq!(SPAN, 4..8);
55/// assert_eq!(SPAN.len(), 4);
56/// ```
57///
58/// The syntax is `$ty.$field`.
59///
60/// No support for tuples, tuple structs or unions.
61///
62/// No support for projecting through multiple fields.
63#[macro_export]
64macro_rules! span_of {
65	($($tt:tt)*) => {
66		$crate::__span_of!([] $($tt)*)
67	};
68}
69
70#[doc(hidden)]
71#[macro_export]
72macro_rules! __span_of {
73	([$($ty:tt)*] . $($field:ident)?) => {const {
74		type Ty = $($ty)*;
75		// Assert that field exists on the type
76		// This prevents auto-Deref from causing UB
77		let Ty { $($field)?: _, .. };
78		// Use MaybeUninit as the subject of the field offset
79		let uninit = ::core::mem::MaybeUninit::<Ty>::uninit();
80		let uninit_ptr = uninit.as_ptr();
81		// We've asserted that the field exists on the type
82		// No Deref coercion or dereferencing a reference
83		// Hope that's enough to keep the code safe
84		#[allow(unused_unsafe)]
85		unsafe {
86			let field_ptr = ::core::ptr::addr_of!((*uninit_ptr).$($field)?);
87			let start = (field_ptr as *const u8).offset_from(uninit_ptr as *const u8) as usize;
88			let end = (field_ptr.offset(1) as *const u8).offset_from(uninit_ptr as *const u8) as usize;
89			start..end
90		}
91	}};
92	([$($ty:tt)*] . $($field:tt)?) => {
93		compile_error!("offset of tuple field not supported")
94	};
95	([$($ty:tt)*] $tt:tt $($tail:tt)*) => {
96		$crate::__span_of!([$($ty)* $tt] $($tail)*)
97	};
98	([$($ty:tt)*]) => {
99		compile_error!("missing field access")
100	};
101}
102
103#[test]
104fn nested_fields() {
105	#[repr(C)]
106	struct Foo<T> { byte: u8, value: T }
107
108	assert_eq!(offset_of!(Foo<i32>.value), 4);
109	assert_eq!(span_of!(Foo<i32>.value), 4..8);
110}
111
112#[cfg(doc)]
113/**
114```compile_fail
115use std::ops;
116struct Target {
117	target: f32,
118}
119struct Subject {
120	field: i32,
121	deref: Target,
122}
123impl ops::Deref for Subject {
124	type Target = Target;
125	fn deref(&self) -> &Target {
126		&self.deref
127	}
128}
129impl ops::DerefMut for Subject {
130	fn deref_mut(&mut self) -> &mut Target {
131		&mut self.deref
132	}
133}
134let _ = dataview::offset_of!(Subject.target);
135```
136*/
137fn deref_protection() {}