1 | #![no_std ] |
2 | #![cfg_attr (fieldoffset_assert_in_const_fn, feature(const_panic))] |
3 | // Explicit lifetimes are clearer when we are working with raw pointers, |
4 | // as the compiler will not warn us if we specify lifetime constraints |
5 | // which are too lax. |
6 | #![allow (clippy::needless_lifetimes)] |
7 | |
8 | #[cfg (all(test, fieldoffset_has_alloc))] |
9 | extern crate alloc; |
10 | |
11 | use core::fmt; |
12 | use core::marker::PhantomData; |
13 | use core::mem; |
14 | use core::ops::Add; |
15 | use core::pin::Pin; |
16 | |
17 | #[doc (hidden)] |
18 | pub extern crate memoffset as __memoffset; // `pub` for macro availability |
19 | |
20 | /// Represents a pointer to a field of type `U` within the type `T` |
21 | /// |
22 | /// The `PinFlag` parameter can be set to `AllowPin` to enable the projection |
23 | /// from Pin<&T> to Pin<&U> |
24 | #[repr (transparent)] |
25 | pub struct FieldOffset<T, U, PinFlag = NotPinned>( |
26 | /// Offset in bytes of the field within the struct |
27 | usize, |
28 | /// A pointer-to-member can be thought of as a function from |
29 | /// `&T` to `&U` with matching lifetimes |
30 | /// |
31 | /// ```compile_fail |
32 | /// use field_offset::FieldOffset; |
33 | /// struct Foo<'a>(&'a str); |
34 | /// fn test<'a>(foo: &Foo<'a>, of: FieldOffset<Foo<'static>, &'static str>) -> &'static str { |
35 | /// let of2 : FieldOffset<Foo<'a>, &'static str> = of; // This must not compile |
36 | /// of2.apply(foo) |
37 | /// } |
38 | /// ``` |
39 | /// That should compile: |
40 | /// ``` |
41 | /// use field_offset::FieldOffset; |
42 | /// struct Foo<'a>(&'a str, &'static str); |
43 | /// fn test<'a>(foo: &'a Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'a str { |
44 | /// let of2 : FieldOffset<Foo<'static>, &'static str> = of; |
45 | /// of.apply(foo) |
46 | /// } |
47 | /// fn test2(foo: &Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'static str { |
48 | /// let of2 : FieldOffset<Foo<'static>, &'static str> = of; |
49 | /// of.apply(foo) |
50 | /// } |
51 | /// fn test3<'a>(foo: &'a Foo, of: FieldOffset<Foo<'a>, &'a str>) -> &'a str { |
52 | /// of.apply(foo) |
53 | /// } |
54 | /// ``` |
55 | PhantomData<(PhantomContra<T>, U, PinFlag)>, |
56 | ); |
57 | |
58 | /// `fn` cannot appear directly in a type that need to be const. |
59 | /// Workaround that with an indirection |
60 | struct PhantomContra<T>(fn(T)); |
61 | |
62 | /// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that |
63 | /// this projection is valid on Pin types. |
64 | /// See documentation of `FieldOffset::new_from_offset_pinned` |
65 | pub enum AllowPin {} |
66 | |
67 | /// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that |
68 | /// this projection is not valid on Pin types. |
69 | pub enum NotPinned {} |
70 | |
71 | impl<T, U> FieldOffset<T, U, NotPinned> { |
72 | // Use MaybeUninit to get a fake T |
73 | #[cfg (fieldoffset_maybe_uninit)] |
74 | #[inline ] |
75 | fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R { |
76 | let uninit = mem::MaybeUninit::<T>::uninit(); |
77 | f(uninit.as_ptr()) |
78 | } |
79 | |
80 | // Use a dangling pointer to get a fake T |
81 | #[cfg (not(fieldoffset_maybe_uninit))] |
82 | #[inline ] |
83 | fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R { |
84 | f(mem::align_of::<T>() as *const T) |
85 | } |
86 | |
87 | /// Construct a field offset via a lambda which returns a reference |
88 | /// to the field in question. |
89 | /// |
90 | /// # Safety |
91 | /// |
92 | /// The lambda *must not* dereference the provided pointer or access the |
93 | /// inner value in any way as it may point to uninitialized memory. |
94 | /// |
95 | /// For the returned `FieldOffset` to be safe to use, the returned pointer |
96 | /// must be valid for *any* instance of `T`. For example, returning a pointer |
97 | /// to a field from an enum with multiple variants will produce a `FieldOffset` |
98 | /// which is unsafe to use. |
99 | pub unsafe fn new<F: for<'a> FnOnce(*const T) -> *const U>(f: F) -> Self { |
100 | let offset = Self::with_uninit_ptr(|base_ptr| { |
101 | let field_ptr = f(base_ptr); |
102 | (field_ptr as usize).wrapping_sub(base_ptr as usize) |
103 | }); |
104 | |
105 | // Construct an instance using the offset |
106 | Self::new_from_offset(offset) |
107 | } |
108 | /// Construct a field offset directly from a byte offset. |
109 | /// |
110 | /// # Safety |
111 | /// |
112 | /// For the returned `FieldOffset` to be safe to use, the field offset |
113 | /// must be valid for *any* instance of `T`. For example, returning the offset |
114 | /// to a field from an enum with multiple variants will produce a `FieldOffset` |
115 | /// which is unsafe to use. |
116 | #[inline ] |
117 | pub const unsafe fn new_from_offset(offset: usize) -> Self { |
118 | // Sanity check: ensure that the field offset plus the field size |
119 | // is no greater than the size of the containing struct. This is |
120 | // not sufficient to make the function *safe*, but it does catch |
121 | // obvious errors like returning a reference to a boxed value, |
122 | // which is owned by `T` and so has the correct lifetime, but is not |
123 | // actually a field. |
124 | #[cfg (fieldoffset_assert_in_const_fn)] |
125 | assert!(offset + mem::size_of::<U>() <= mem::size_of::<T>()); |
126 | // On stable rust, we can still get an assert in debug mode, |
127 | // relying on the checked overflow behaviour |
128 | let _ = mem::size_of::<T>() - (offset + mem::size_of::<U>()); |
129 | |
130 | FieldOffset(offset, PhantomData) |
131 | } |
132 | } |
133 | |
134 | // Methods for applying the pointer to member |
135 | impl<T, U, PinFlag> FieldOffset<T, U, PinFlag> { |
136 | /// Apply the field offset to a native pointer. |
137 | #[inline ] |
138 | pub fn apply_ptr(self, x: *const T) -> *const U { |
139 | ((x as usize) + self.0) as *const U |
140 | } |
141 | /// Apply the field offset to a native mutable pointer. |
142 | #[inline ] |
143 | pub fn apply_ptr_mut(self, x: *mut T) -> *mut U { |
144 | ((x as usize) + self.0) as *mut U |
145 | } |
146 | /// Apply the field offset to a reference. |
147 | #[inline ] |
148 | pub fn apply<'a>(self, x: &'a T) -> &'a U { |
149 | unsafe { &*self.apply_ptr(x) } |
150 | } |
151 | /// Apply the field offset to a mutable reference. |
152 | #[inline ] |
153 | pub fn apply_mut<'a>(self, x: &'a mut T) -> &'a mut U { |
154 | unsafe { &mut *self.apply_ptr_mut(x) } |
155 | } |
156 | /// Get the raw byte offset for this field offset. |
157 | #[inline ] |
158 | pub const fn get_byte_offset(self) -> usize { |
159 | self.0 |
160 | } |
161 | |
162 | // Methods for unapplying the pointer to member |
163 | |
164 | /// Unapply the field offset to a native pointer. |
165 | /// |
166 | /// # Safety |
167 | /// |
168 | /// *Warning: very unsafe!* |
169 | /// |
170 | /// This applies a negative offset to a pointer. If the safety |
171 | /// implications of this are not already clear to you, then *do |
172 | /// not* use this method. Also be aware that Rust has stronger |
173 | /// aliasing rules than other languages, so it may be UB to |
174 | /// dereference the resulting pointer even if it points to a valid |
175 | /// location, due to the presence of other live references. |
176 | #[inline ] |
177 | pub unsafe fn unapply_ptr(self, x: *const U) -> *const T { |
178 | ((x as usize) - self.0) as *const T |
179 | } |
180 | /// Unapply the field offset to a native mutable pointer. |
181 | /// |
182 | /// # Safety |
183 | /// |
184 | /// *Warning: very unsafe!* |
185 | /// |
186 | /// This applies a negative offset to a pointer. If the safety |
187 | /// implications of this are not already clear to you, then *do |
188 | /// not* use this method. Also be aware that Rust has stronger |
189 | /// aliasing rules than other languages, so it may be UB to |
190 | /// dereference the resulting pointer even if it points to a valid |
191 | /// location, due to the presence of other live references. |
192 | #[inline ] |
193 | pub unsafe fn unapply_ptr_mut(self, x: *mut U) -> *mut T { |
194 | ((x as usize) - self.0) as *mut T |
195 | } |
196 | /// Unapply the field offset to a reference. |
197 | /// |
198 | /// # Safety |
199 | /// |
200 | /// *Warning: very unsafe!* |
201 | /// |
202 | /// This applies a negative offset to a reference. If the safety |
203 | /// implications of this are not already clear to you, then *do |
204 | /// not* use this method. Also be aware that Rust has stronger |
205 | /// aliasing rules than other languages, so this method may cause UB |
206 | /// even if the resulting reference points to a valid location, due |
207 | /// to the presence of other live references. |
208 | #[inline ] |
209 | pub unsafe fn unapply<'a>(self, x: &'a U) -> &'a T { |
210 | &*self.unapply_ptr(x) |
211 | } |
212 | /// Unapply the field offset to a mutable reference. |
213 | /// |
214 | /// # Safety |
215 | /// |
216 | /// *Warning: very unsafe!* |
217 | /// |
218 | /// This applies a negative offset to a reference. If the safety |
219 | /// implications of this are not already clear to you, then *do |
220 | /// not* use this method. Also be aware that Rust has stronger |
221 | /// aliasing rules than other languages, so this method may cause UB |
222 | /// even if the resulting reference points to a valid location, due |
223 | /// to the presence of other live references. |
224 | #[inline ] |
225 | pub unsafe fn unapply_mut<'a>(self, x: &'a mut U) -> &'a mut T { |
226 | &mut *self.unapply_ptr_mut(x) |
227 | } |
228 | |
229 | /// Convert this offset to an offset that is allowed to go from `Pin<&T>` |
230 | /// to `Pin<&U>` |
231 | /// |
232 | /// # Safety |
233 | /// |
234 | /// The Pin safety rules for projection must be respected. These rules are |
235 | /// explained in the |
236 | /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field) |
237 | pub const unsafe fn as_pinned_projection(self) -> FieldOffset<T, U, AllowPin> { |
238 | FieldOffset::new_from_offset_pinned(self.get_byte_offset()) |
239 | } |
240 | |
241 | /// Remove the AllowPin flag |
242 | pub const fn as_unpinned_projection(self) -> FieldOffset<T, U, NotPinned> { |
243 | unsafe { FieldOffset::new_from_offset(self.get_byte_offset()) } |
244 | } |
245 | } |
246 | |
247 | impl<T, U> FieldOffset<T, U, AllowPin> { |
248 | /// Construct a field offset directly from a byte offset, which can be projected from |
249 | /// a pinned. |
250 | /// |
251 | /// # Safety |
252 | /// |
253 | /// In addition to the safety rules of FieldOffset::new_from_offset, the projection |
254 | /// from `Pin<&T>` to `Pin<&U>` must also be allowed. The rules are explained in the |
255 | /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field) |
256 | #[inline ] |
257 | pub const unsafe fn new_from_offset_pinned(offset: usize) -> Self { |
258 | FieldOffset(offset, PhantomData) |
259 | } |
260 | |
261 | /// Apply the field offset to a pinned reference and return a pinned |
262 | /// reference to the field |
263 | #[inline ] |
264 | pub fn apply_pin<'a>(self, x: Pin<&'a T>) -> Pin<&'a U> { |
265 | unsafe { x.map_unchecked(|x| self.apply(x)) } |
266 | } |
267 | /// Apply the field offset to a pinned mutable reference and return a |
268 | /// pinned mutable reference to the field |
269 | #[inline ] |
270 | pub fn apply_pin_mut<'a>(self, x: Pin<&'a mut T>) -> Pin<&'a mut U> { |
271 | unsafe { x.map_unchecked_mut(|x| self.apply_mut(x)) } |
272 | } |
273 | } |
274 | |
275 | impl<T, U> From<FieldOffset<T, U, AllowPin>> for FieldOffset<T, U, NotPinned> { |
276 | fn from(other: FieldOffset<T, U, AllowPin>) -> Self { |
277 | other.as_unpinned_projection() |
278 | } |
279 | } |
280 | |
281 | /// Allow chaining pointer-to-members. |
282 | /// |
283 | /// Applying the resulting field offset is equivalent to applying the first |
284 | /// field offset, then applying the second field offset. |
285 | /// |
286 | /// The requirements on the generic type parameters ensure this is a safe operation. |
287 | impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U> { |
288 | type Output = FieldOffset<T, V>; |
289 | #[inline ] |
290 | fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> { |
291 | FieldOffset(self.0 + other.0, PhantomData) |
292 | } |
293 | } |
294 | impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U, AllowPin> { |
295 | type Output = FieldOffset<T, V, AllowPin>; |
296 | #[inline ] |
297 | fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V, AllowPin> { |
298 | FieldOffset(self.0 + other.0, PhantomData) |
299 | } |
300 | } |
301 | impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U, AllowPin> { |
302 | type Output = FieldOffset<T, V>; |
303 | #[inline ] |
304 | fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> { |
305 | FieldOffset(self.0 + other.0, PhantomData) |
306 | } |
307 | } |
308 | impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U> { |
309 | type Output = FieldOffset<T, V>; |
310 | #[inline ] |
311 | fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V> { |
312 | FieldOffset(self.0 + other.0, PhantomData) |
313 | } |
314 | } |
315 | |
316 | /// The debug implementation prints the byte offset of the field in hexadecimal. |
317 | impl<T, U, Flag> fmt::Debug for FieldOffset<T, U, Flag> { |
318 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { |
319 | write!(f, "FieldOffset( {:#x})" , self.0) |
320 | } |
321 | } |
322 | |
323 | impl<T, U, Flag> Copy for FieldOffset<T, U, Flag> {} |
324 | impl<T, U, Flag> Clone for FieldOffset<T, U, Flag> { |
325 | fn clone(&self) -> Self { |
326 | *self |
327 | } |
328 | } |
329 | |
330 | /// This macro allows safe construction of a FieldOffset, |
331 | /// by generating a known to be valid lambda to pass to the |
332 | /// constructor. It takes a type, and the identifier of a field |
333 | /// within that type as input. |
334 | /// |
335 | /// Examples: |
336 | /// |
337 | /// Offset of field `Foo.bar` |
338 | /// |
339 | /// ```rust |
340 | /// # #[macro_use ] |
341 | /// # extern crate field_offset; |
342 | /// # fn main() { |
343 | /// #[repr(C)] |
344 | /// struct Foo { foo: i32, bar: i32 } |
345 | /// assert_eq!(offset_of!(Foo => bar).get_byte_offset(), 4); |
346 | /// # } |
347 | /// ``` |
348 | /// |
349 | /// Offset of nested field `Foo.bar.x` |
350 | /// |
351 | /// ```rust |
352 | /// # #[macro_use ] |
353 | /// # extern crate field_offset; |
354 | /// # fn main() { |
355 | /// struct Bar { a: u8, x: u8 } |
356 | /// struct Foo { foo: i32, bar: Bar } |
357 | /// assert_eq!(offset_of!(Foo => bar: Bar => x).get_byte_offset(), 5); |
358 | /// # } |
359 | /// ``` |
360 | #[macro_export ] |
361 | macro_rules! offset_of { |
362 | ($t: path => $f: tt) => {{ |
363 | // Construct the offset |
364 | #[allow(unused_unsafe)] |
365 | unsafe { |
366 | $crate::FieldOffset::<$t, _>::new(|x| { |
367 | $crate::__memoffset::raw_field!(x, $t, $f) |
368 | }) |
369 | } |
370 | }}; |
371 | ($t: path => $f: ident: $($rest: tt)*) => { |
372 | offset_of!($t => $f) + offset_of!($($rest)*) |
373 | }; |
374 | } |
375 | |
376 | #[cfg (test)] |
377 | mod tests { |
378 | // Example structs |
379 | #[derive (Debug)] |
380 | struct Foo { |
381 | a: u32, |
382 | b: f64, |
383 | c: bool, |
384 | } |
385 | |
386 | #[derive (Debug)] |
387 | struct Bar { |
388 | x: u32, |
389 | y: Foo, |
390 | } |
391 | |
392 | #[derive (Debug)] |
393 | struct Tuple(i32, f64); |
394 | |
395 | #[test ] |
396 | fn test_simple() { |
397 | // Get a pointer to `b` within `Foo` |
398 | let foo_b = offset_of!(Foo => b); |
399 | |
400 | // Construct an example `Foo` |
401 | let mut x = Foo { |
402 | a: 1, |
403 | b: 2.0, |
404 | c: false, |
405 | }; |
406 | |
407 | // Apply the pointer to get at `b` and read it |
408 | { |
409 | let y = foo_b.apply(&x); |
410 | assert_eq!(*y, 2.0); |
411 | } |
412 | |
413 | // Apply the pointer to get at `b` and mutate it |
414 | { |
415 | let y = foo_b.apply_mut(&mut x); |
416 | *y = 42.0; |
417 | } |
418 | assert_eq!(x.b, 42.0); |
419 | } |
420 | |
421 | #[test ] |
422 | fn test_tuple() { |
423 | // Get a pointer to `b` within `Foo` |
424 | let tuple_1 = offset_of!(Tuple => 1); |
425 | |
426 | // Construct an example `Foo` |
427 | let mut x = Tuple(1, 42.0); |
428 | |
429 | // Apply the pointer to get at `b` and read it |
430 | { |
431 | let y = tuple_1.apply(&x); |
432 | assert_eq!(*y, 42.0); |
433 | } |
434 | |
435 | // Apply the pointer to get at `b` and mutate it |
436 | { |
437 | let y = tuple_1.apply_mut(&mut x); |
438 | *y = 5.0; |
439 | } |
440 | assert_eq!(x.1, 5.0); |
441 | } |
442 | |
443 | #[test ] |
444 | fn test_nested() { |
445 | // Construct an example `Foo` |
446 | let mut x = Bar { |
447 | x: 0, |
448 | y: Foo { |
449 | a: 1, |
450 | b: 2.0, |
451 | c: false, |
452 | }, |
453 | }; |
454 | |
455 | // Combine the pointer-to-members |
456 | let bar_y_b = offset_of!(Bar => y: Foo => b); |
457 | |
458 | // Apply the pointer to get at `b` and mutate it |
459 | { |
460 | let y = bar_y_b.apply_mut(&mut x); |
461 | *y = 42.0; |
462 | } |
463 | assert_eq!(x.y.b, 42.0); |
464 | } |
465 | |
466 | struct Parameterized<T, U> { |
467 | x: T, |
468 | _y: U, |
469 | } |
470 | #[test ] |
471 | fn test_type_parameter() { |
472 | let _ = offset_of!(Parameterized<Parameterized<bool, bool>, bool> => x: Parameterized<bool, bool> => x); |
473 | } |
474 | |
475 | #[test ] |
476 | fn test_const() { |
477 | use crate::FieldOffset; |
478 | #[repr (C)] |
479 | struct SomeStruct { |
480 | a: u8, |
481 | b: u32, |
482 | } |
483 | const CONST_FIELD_OFFSET: FieldOffset<SomeStruct, u32> = |
484 | unsafe { FieldOffset::new_from_offset(4) }; |
485 | const CONST_VALUE: usize = CONST_FIELD_OFFSET.get_byte_offset(); |
486 | assert_eq!(offset_of!(SomeStruct => b).get_byte_offset(), CONST_VALUE); |
487 | |
488 | static STATIC_FIELD_OFFSET: FieldOffset<SomeStruct, u32> = |
489 | unsafe { FieldOffset::new_from_offset(4) }; |
490 | assert_eq!( |
491 | offset_of!(SomeStruct => b).get_byte_offset(), |
492 | STATIC_FIELD_OFFSET.get_byte_offset() |
493 | ); |
494 | } |
495 | |
496 | #[cfg (fieldoffset_has_alloc)] |
497 | #[test ] |
498 | fn test_pin() { |
499 | use alloc::boxed::Box; |
500 | use core::pin::Pin; |
501 | |
502 | // Get a pointer to `b` within `Foo` |
503 | let foo_b = offset_of!(Foo => b); |
504 | let foo_b_pin = unsafe { foo_b.as_pinned_projection() }; |
505 | let foo = Box::pin(Foo { |
506 | a: 21, |
507 | b: 22.0, |
508 | c: true, |
509 | }); |
510 | let pb: Pin<&f64> = foo_b_pin.apply_pin(foo.as_ref()); |
511 | assert_eq!(*pb, 22.0); |
512 | |
513 | let mut x = Box::pin(Bar { |
514 | x: 0, |
515 | y: Foo { |
516 | a: 1, |
517 | b: 52.0, |
518 | c: false, |
519 | }, |
520 | }); |
521 | let bar_y_b = offset_of!(Bar => y: Foo => b); |
522 | assert!(*bar_y_b.apply(&*x) == 52.0); |
523 | |
524 | let bar_y_pin = unsafe { offset_of!(Bar => y).as_pinned_projection() }; |
525 | *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.; |
526 | assert_eq!(x.y.b, 12.0); |
527 | } |
528 | } |
529 | |