1// Copyright (c) 2017 Gilad Naaman
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21/// Macro to create a local `base_ptr` raw pointer of the given type, avoiding UB as
22/// much as is possible currently.
23#[cfg(maybe_uninit)]
24#[macro_export]
25#[doc(hidden)]
26macro_rules! _memoffset__let_base_ptr {
27 ($name:ident, $type:ty) => {
28 // No UB here, and the pointer does not dangle, either.
29 // But we have to make sure that `uninit` lives long enough,
30 // so it has to be in the same scope as `$name`. That's why
31 // `let_base_ptr` declares a variable (several, actually)
32 // instead of returning one.
33 let uninit = $crate::__priv::mem::MaybeUninit::<$type>::uninit();
34 let $name: *const $type = uninit.as_ptr();
35 };
36}
37#[cfg(not(maybe_uninit))]
38#[macro_export]
39#[doc(hidden)]
40macro_rules! _memoffset__let_base_ptr {
41 ($name:ident, $type:ty) => {
42 // No UB right here, but we will later dereference this pointer to
43 // offset into a field, and that is UB because the pointer is dangling.
44 let $name = $crate::__priv::mem::align_of::<$type>() as *const $type;
45 };
46}
47
48/// Macro to compute the distance between two pointers.
49#[cfg(any(feature = "unstable_const", stable_const))]
50#[macro_export]
51#[doc(hidden)]
52macro_rules! _memoffset_offset_from_unsafe {
53 ($field:expr, $base:expr) => {{
54 let field = $field; // evaluate $field outside the `unsafe` block
55 let base = $base; // evaluate $base outside the `unsafe` block
56 // Compute offset, with unstable `offset_from` for const-compatibility.
57 // (Requires the pointers to not dangle, but we already need that for `raw_field!` anyway.)
58 unsafe { (field as *const u8).offset_from(base as *const u8) as usize }
59 }};
60}
61#[cfg(not(any(feature = "unstable_const", stable_const)))]
62#[macro_export]
63#[doc(hidden)]
64macro_rules! _memoffset_offset_from_unsafe {
65 ($field:expr, $base:expr) => {
66 // Compute offset.
67 ($field as usize) - ($base as usize)
68 };
69}
70#[cfg(not(feature = "unstable_offset_of"))]
71#[macro_export(local_inner_macros)]
72#[doc(hidden)]
73macro_rules! _memoffset__offset_of_impl {
74 ($parent:path, $field:tt) => {{
75 // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
76 _memoffset__let_base_ptr!(base_ptr, $parent);
77 // Get field pointer.
78 let field_ptr = raw_field!(base_ptr, $parent, $field);
79 // Compute offset.
80 _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
81 }};
82}
83#[cfg(feature = "unstable_offset_of")]
84#[macro_export]
85#[doc(hidden)]
86#[allow_internal_unstable(offset_of)]
87macro_rules! _memoffset__offset_of_impl {
88 ($parent:path, $field:tt) => {{
89 $crate::__priv::mem::offset_of!($parent, $field)
90 }};
91}
92
93/// Calculates the offset of the specified field from the start of the named struct.
94///
95/// ## Examples
96/// ```
97/// use memoffset::offset_of;
98///
99/// #[repr(C, packed)]
100/// struct Foo {
101/// a: u32,
102/// b: u64,
103/// c: [u8; 5]
104/// }
105///
106/// fn main() {
107/// assert_eq!(offset_of!(Foo, a), 0);
108/// assert_eq!(offset_of!(Foo, b), 4);
109/// }
110/// ```
111///
112/// ## Notes
113/// Rust's ABI is unstable, and [type layout can be changed with each
114/// compilation](https://doc.rust-lang.org/reference/type-layout.html).
115///
116/// Using `offset_of!` with a `repr(Rust)` struct will return the correct offset of the
117/// specified `field` for a particular compilation, but the exact value may change
118/// based on the compiler version, concrete struct type, time of day, or rustc's mood.
119///
120/// As a result, the value should not be retained and used between different compilations.
121#[macro_export(local_inner_macros)]
122macro_rules! offset_of {
123 ($parent:path, $field:tt) => {
124 _memoffset__offset_of_impl!($parent, $field)
125 };
126}
127
128/// Calculates the offset of the specified field from the start of the tuple.
129///
130/// ## Examples
131/// ```
132/// use memoffset::offset_of_tuple;
133///
134/// fn main() {
135/// assert!(offset_of_tuple!((u8, u32), 1) >= 0, "Tuples do not have a defined layout");
136/// }
137/// ```
138#[cfg(tuple_ty)]
139#[macro_export(local_inner_macros)]
140macro_rules! offset_of_tuple {
141 ($parent:ty, $field:tt) => {{
142 // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
143 _memoffset__let_base_ptr!(base_ptr, $parent);
144 // Get field pointer.
145 let field_ptr = raw_field_tuple!(base_ptr, $parent, $field);
146 // Compute offset.
147 _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
148 }};
149}
150
151#[cfg(not(feature = "unstable_offset_of"))]
152#[macro_export(local_inner_macros)]
153#[doc(hidden)]
154macro_rules! _memoffset__offset_of_union_impl {
155 ($parent:path, $field:tt) => {{
156 // Get a base pointer (non-dangling if rustc supports `MaybeUninit`).
157 _memoffset__let_base_ptr!(base_ptr, $parent);
158 // Get field pointer.
159 let field_ptr = raw_field_union!(base_ptr, $parent, $field);
160 // Compute offset.
161 _memoffset_offset_from_unsafe!(field_ptr, base_ptr)
162 }};
163}
164
165#[cfg(feature = "unstable_offset_of")]
166#[macro_export(local_inner_macros)]
167#[doc(hidden)]
168#[allow_internal_unstable(offset_of)]
169macro_rules! _memoffset__offset_of_union_impl {
170 ($parent:path, $field:tt) => {{
171 $crate::__priv::mem::offset_of!($parent, $field)
172 }};
173}
174
175/// Calculates the offset of the specified union member from the start of the union.
176///
177/// ## Examples
178/// ```
179/// use memoffset::offset_of_union;
180///
181/// #[repr(C, packed)]
182/// union Foo {
183/// foo32: i32,
184/// foo64: i64,
185/// }
186///
187/// fn main() {
188/// assert!(offset_of_union!(Foo, foo64) == 0);
189/// }
190/// ```
191///
192/// ## Note
193/// Due to macro_rules limitations, this macro will accept structs with a single field as well as unions.
194/// This is not a stable guarantee, and future versions of this crate might fail
195/// on any use of this macro with a struct, without a semver bump.
196#[macro_export(local_inner_macros)]
197macro_rules! offset_of_union {
198 ($parent:path, $field:tt) => {{
199 _memoffset__offset_of_union_impl!($parent, $field)
200 }};
201}
202
203#[cfg(test)]
204mod tests {
205 #[test]
206 fn offset_simple() {
207 #[repr(C)]
208 struct Foo {
209 a: u32,
210 b: [u8; 2],
211 c: i64,
212 }
213
214 assert_eq!(offset_of!(Foo, a), 0);
215 assert_eq!(offset_of!(Foo, b), 4);
216 assert_eq!(offset_of!(Foo, c), 8);
217 }
218
219 #[test]
220 #[cfg_attr(miri, ignore)] // this creates unaligned references
221 fn offset_simple_packed() {
222 #[repr(C, packed)]
223 struct Foo {
224 a: u32,
225 b: [u8; 2],
226 c: i64,
227 }
228
229 assert_eq!(offset_of!(Foo, a), 0);
230 assert_eq!(offset_of!(Foo, b), 4);
231 assert_eq!(offset_of!(Foo, c), 6);
232 }
233
234 #[test]
235 fn tuple_struct() {
236 #[repr(C)]
237 struct Tup(i32, i32);
238
239 assert_eq!(offset_of!(Tup, 0), 0);
240 assert_eq!(offset_of!(Tup, 1), 4);
241 }
242
243 #[test]
244 fn offset_union() {
245 // Since we're specifying repr(C), all fields are supposed to be at offset 0
246 #[repr(C)]
247 union Foo {
248 a: u32,
249 b: [u8; 2],
250 c: i64,
251 }
252
253 assert_eq!(offset_of_union!(Foo, a), 0);
254 assert_eq!(offset_of_union!(Foo, b), 0);
255 assert_eq!(offset_of_union!(Foo, c), 0);
256 }
257
258 #[test]
259 fn path() {
260 mod sub {
261 #[repr(C)]
262 pub struct Foo {
263 pub x: u32,
264 }
265 }
266
267 assert_eq!(offset_of!(sub::Foo, x), 0);
268 }
269
270 #[test]
271 fn inside_generic_method() {
272 struct Pair<T, U>(T, U);
273
274 fn foo<T, U>(_: Pair<T, U>) -> usize {
275 offset_of!(Pair<T, U>, 1)
276 }
277
278 assert_eq!(foo(Pair(0, 0)), 4);
279 }
280
281 #[cfg(tuple_ty)]
282 #[test]
283 fn test_tuple_offset() {
284 let f = (0i32, 0.0f32, 0u8);
285 let f_ptr = &f as *const _;
286 let f1_ptr = &f.1 as *const _;
287
288 assert_eq!(
289 f1_ptr as usize - f_ptr as usize,
290 offset_of_tuple!((i32, f32, u8), 1)
291 );
292 }
293
294 #[test]
295 fn test_raw_field() {
296 #[repr(C)]
297 struct Foo {
298 a: u32,
299 b: [u8; 2],
300 c: i64,
301 }
302
303 let f: Foo = Foo {
304 a: 0,
305 b: [0, 0],
306 c: 0,
307 };
308 let f_ptr = &f as *const _;
309 assert_eq!(f_ptr as usize + 0, raw_field!(f_ptr, Foo, a) as usize);
310 assert_eq!(f_ptr as usize + 4, raw_field!(f_ptr, Foo, b) as usize);
311 assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize);
312 }
313
314 #[cfg(tuple_ty)]
315 #[test]
316 fn test_raw_field_tuple() {
317 let t = (0u32, 0u8, false);
318 let t_ptr = &t as *const _;
319 let t_addr = t_ptr as usize;
320
321 assert_eq!(
322 &t.0 as *const _ as usize - t_addr,
323 raw_field_tuple!(t_ptr, (u32, u8, bool), 0) as usize - t_addr
324 );
325 assert_eq!(
326 &t.1 as *const _ as usize - t_addr,
327 raw_field_tuple!(t_ptr, (u32, u8, bool), 1) as usize - t_addr
328 );
329 assert_eq!(
330 &t.2 as *const _ as usize - t_addr,
331 raw_field_tuple!(t_ptr, (u32, u8, bool), 2) as usize - t_addr
332 );
333 }
334
335 #[test]
336 fn test_raw_field_union() {
337 #[repr(C)]
338 union Foo {
339 a: u32,
340 b: [u8; 2],
341 c: i64,
342 }
343
344 let f = Foo { a: 0 };
345 let f_ptr = &f as *const _;
346 assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, a) as usize);
347 assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, b) as usize);
348 assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, c) as usize);
349 }
350
351 #[cfg(any(
352 feature = "unstable_const",
353 feature = "unstable_offset_of",
354 stable_const
355 ))]
356 #[test]
357 fn const_offset() {
358 #[repr(C)]
359 struct Foo {
360 a: u32,
361 b: [u8; 2],
362 c: i64,
363 }
364
365 assert_eq!([0; offset_of!(Foo, b)].len(), 4);
366 }
367
368 #[cfg(feature = "unstable_const")]
369 #[test]
370 fn const_offset_interior_mutable() {
371 #[repr(C)]
372 struct Foo {
373 a: u32,
374 b: core::cell::Cell<u32>,
375 }
376
377 assert_eq!([0; offset_of!(Foo, b)].len(), 4);
378 }
379
380 #[cfg(any(
381 feature = "unstable_const",
382 feature = "unstable_offset_of",
383 stable_const
384 ))]
385 #[test]
386 fn const_fn_offset() {
387 const fn test_fn() -> usize {
388 #[repr(C)]
389 struct Foo {
390 a: u32,
391 b: [u8; 2],
392 c: i64,
393 }
394
395 offset_of!(Foo, b)
396 }
397
398 assert_eq!([0; test_fn()].len(), 4);
399 }
400}
401