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() {}