1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158

/// Returns the offset of a field.
///
/// ```
/// #[repr(C)]
/// struct Data {
/// 	byte: u8,
/// 	float: f32,
/// }
///
/// let offset = dataview::offset_of!(Data.float);
/// assert_eq!(offset, 4);
///
/// const OFFSET: usize = dataview::offset_of!(Data.float);
/// assert_eq!(OFFSET, 4);
/// ```
///
/// The syntax is `$ty.$field`.
///
/// No support for tuples, tuple structs or unions.
///
/// No support for projecting through multiple fields.
#[macro_export]
macro_rules! offset_of {
	($($tt:tt)*) => {
		$crate::__offset_of!([] $($tt)*)
	};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __offset_of {
	([$($ty:tt)*] . $($field:ident)?) => {{
		type Ty = $($ty)*;
		// Assert that field exists on the type
		// This prevents auto-Deref from causing UB
		let Ty { $($field)?: _, .. };
		// Use MaybeUninit as the subject of the field offset
		let uninit = ::core::mem::MaybeUninit::<Ty>::uninit();
		let uninit_ptr = uninit.as_ptr();
		// We've asserted that the field exists on the type
		// No Deref coercion or dereferencing a reference
		// Hope that's enough to keep the code safe
		#[allow(unused_unsafe)]
		unsafe {
			let field_ptr = ::core::ptr::addr_of!((*uninit_ptr).$($field)?);
			(field_ptr as *const u8).offset_from(uninit_ptr as *const u8) as usize
		}
	}};
	([$($ty:tt)*] . $($field:tt)?) => {
		compile_error!("offset of tuple field not supported")
	};
	([$($ty:tt)*] $tt:tt $($tail:tt)*) => {
		$crate::__offset_of!([$($ty)* $tt] $($tail)*)
	};
	([$($ty:tt)*]) => {
		compile_error!("missing field access")
	};
}

/// Returns the `start..end` offsets of a field.
///
/// ```
/// #[repr(C)]
/// struct Data {
/// 	byte: u8,
/// 	float: f32,
/// }
///
/// let span = dataview::span_of!(Data.float);
/// assert_eq!(span, 4..8);
/// assert_eq!(span.len(), 4);
///
/// const SPAN: std::ops::Range<usize> = dataview::span_of!(Data.float);
/// assert_eq!(SPAN, 4..8);
/// assert_eq!(SPAN.len(), 4);
/// ```
///
/// The syntax is `$ty.$field`.
///
/// No support for tuples, tuple structs or unions.
///
/// No support for projecting through multiple fields.
#[macro_export]
macro_rules! span_of {
	($($tt:tt)*) => {
		$crate::__span_of!([] $($tt)*)
	};
}

#[doc(hidden)]
#[macro_export]
macro_rules! __span_of {
	([$($ty:tt)*] . $($field:ident)?) => {{
		type Ty = $($ty)*;
		// Assert that field exists on the type
		// This prevents auto-Deref from causing UB
		let Ty { $($field)?: _, .. };
		// Use MaybeUninit as the subject of the field offset
		let uninit = ::core::mem::MaybeUninit::<Ty>::uninit();
		let uninit_ptr = uninit.as_ptr();
		// We've asserted that the field exists on the type
		// No Deref coercion or dereferencing a reference
		// Hope that's enough to keep the code safe
		#[allow(unused_unsafe)]
		unsafe {
			let field_ptr = ::core::ptr::addr_of!((*uninit_ptr).$($field)?);
			let start = (field_ptr as *const u8).offset_from(uninit_ptr as *const u8) as usize;
			let end = (field_ptr.offset(1) as *const u8).offset_from(uninit_ptr as *const u8) as usize;
			start..end
		}
	}};
	([$($ty:tt)*] . $($field:tt)?) => {
		compile_error!("offset of tuple field not supported")
	};
	([$($ty:tt)*] $tt:tt $($tail:tt)*) => {
		$crate::__span_of!([$($ty)* $tt] $($tail)*)
	};
	([$($ty:tt)*]) => {
		compile_error!("missing field access")
	};
}

#[test]
fn nested_fields() {
	#[repr(C)]
	struct Foo<T> { byte: u8, value: T }

	assert_eq!(offset_of!(Foo<i32>.value), 4);
	assert_eq!(span_of!(Foo<i32>.value), 4..8);
}

#[cfg(doc)]
/**
```compile_fail
use std::ops;
struct Target {
	target: f32,
}
struct Subject {
	field: i32,
	deref: Target,
}
impl ops::Deref for Subject {
	type Target = Target;
	fn deref(&self) -> &Target {
		&self.deref
	}
}
impl ops::DerefMut for Subject {
	fn deref_mut(&mut self) -> &mut Target {
		&mut self.deref
	}
}
let _ = dataview::offset_of!(Subject.target);
```
*/
fn deref_protection() {}