@@ -5,94 +5,109 @@ use std::ops::Add;
5
5
use std:: fmt;
6
6
7
7
/// Represents a pointer to a field of type `U` within the type `T`
8
+ #[ repr( transparent) ]
8
9
pub struct FieldOffset < T , U > (
9
10
/// Offset in bytes of the field within the struct
10
11
usize ,
11
12
/// A pointer-to-member can be thought of as a function from
12
13
/// `&T` to `&U` with matching lifetimes
13
- PhantomData < for <' a > Fn ( & ' a T ) -> & ' a U >
14
+ PhantomData < dyn for < ' a > Fn ( & ' a T ) -> & ' a U >
14
15
) ;
15
16
16
17
impl < T , U > FieldOffset < T , U > {
18
+ // Use MaybeUninit to get a fake T
19
+ #[ cfg( fieldoffset_maybe_uninit) ]
20
+ #[ inline]
21
+ fn with_uninit_ptr < R , F : FnOnce ( * const T ) -> R > ( f : F ) -> R {
22
+ let uninit = mem:: MaybeUninit :: < T > :: uninit ( ) ;
23
+ f ( uninit. as_ptr ( ) )
24
+ }
25
+
26
+ // Use a dangling pointer to get a fake T
27
+ #[ cfg( not( fieldoffset_maybe_uninit) ) ]
28
+ #[ inline]
29
+ fn with_uninit_ptr < R , F : FnOnce ( * const T ) -> R > ( f : F ) -> R {
30
+ f ( mem:: align_of :: < T > ( ) as * const T )
31
+ }
32
+
17
33
/// Construct a field offset via a lambda which returns a reference
18
34
/// to the field in question.
19
35
///
20
36
/// The lambda *must not* access the value passed in.
21
- pub unsafe fn new < F : for < ' a > FnOnce ( & ' a T ) -> & ' a U > ( f : F ) -> Self {
22
- // Construct a "fake" T. It's not valid, but the lambda shouldn't
23
- // actually access it (which is why this is unsafe)
24
- let x = mem:: zeroed ( ) ;
25
- let offset = {
26
- let x = & x;
27
- // Pass a reference to the zeroed T to the lambda
28
- // The lambda gives us back a reference to (what we hope is)
29
- // a field of T, of type U
30
- let y = f ( x) ;
31
- // Compute the offset of the field via the difference between the
32
- // references `x` and `y`. Overflow is an error: in debug builds it
33
- // will be caught here, in release it will wrap around and be caught
34
- // on the next line.
35
- ( y as * const U as usize ) - ( x as * const T as usize )
36
- } ;
37
- // Don't run destructor on "fake" T
38
- mem:: forget ( x) ;
37
+ pub unsafe fn new < F : for < ' a > FnOnce ( * const T ) -> * const U > ( f : F ) -> Self {
38
+ let offset = Self :: with_uninit_ptr ( |base_ptr| {
39
+ let field_ptr = f ( base_ptr) ;
40
+ ( field_ptr as usize ) . wrapping_sub ( base_ptr as usize )
41
+ } ) ;
42
+
43
+ // Construct an instance using the offset
44
+ Self :: new_from_offset ( offset)
45
+ }
46
+ /// Construct a field offset directly from a byte offset.
47
+ #[ inline]
48
+ pub unsafe fn new_from_offset ( offset : usize ) -> Self {
39
49
// Sanity check: ensure that the field offset plus the field size
40
50
// is no greater than the size of the containing struct. This is
41
51
// not sufficient to make the function *safe*, but it does catch
42
52
// obvious errors like returning a reference to a boxed value,
43
53
// which is owned by `T` and so has the correct lifetime, but is not
44
54
// actually a field.
45
55
assert ! ( offset + mem:: size_of:: <U >( ) <= mem:: size_of:: <T >( ) ) ;
46
- // Construct an instance using the offset
47
- Self :: new_from_offset ( offset)
48
- }
49
- /// Construct a field offset directly from a byte offset.
50
- pub unsafe fn new_from_offset ( offset : usize ) -> Self {
56
+
51
57
FieldOffset ( offset, PhantomData )
52
58
}
53
59
// Methods for applying the pointer to member
54
60
/// Apply the field offset to a native pointer.
61
+ #[ inline]
55
62
pub fn apply_ptr < ' a > ( & self , x : * const T ) -> * const U {
56
63
( ( x as usize ) + self . 0 ) as * const U
57
64
}
58
65
/// Apply the field offset to a native mutable pointer.
66
+ #[ inline]
59
67
pub fn apply_ptr_mut < ' a > ( & self , x : * mut T ) -> * mut U {
60
68
( ( x as usize ) + self . 0 ) as * mut U
61
69
}
62
70
/// Apply the field offset to a reference.
71
+ #[ inline]
63
72
pub fn apply < ' a > ( & self , x : & ' a T ) -> & ' a U {
64
73
unsafe { & * self . apply_ptr ( x) }
65
74
}
66
75
/// Apply the field offset to a mutable reference.
76
+ #[ inline]
67
77
pub fn apply_mut < ' a > ( & self , x : & ' a mut T ) -> & ' a mut U {
68
78
unsafe { & mut * self . apply_ptr_mut ( x) }
69
79
}
70
80
/// Get the raw byte offset for this field offset.
81
+ #[ inline]
71
82
pub fn get_byte_offset ( & self ) -> usize {
72
83
self . 0
73
84
}
74
85
// Methods for unapplying the pointer to member
75
86
/// Unapply the field offset to a native pointer.
76
87
///
77
88
/// *Warning: very unsafe!*
89
+ #[ inline]
78
90
pub unsafe fn unapply_ptr < ' a > ( & self , x : * const U ) -> * const T {
79
91
( ( x as usize ) - self . 0 ) as * const T
80
92
}
81
93
/// Unapply the field offset to a native mutable pointer.
82
94
///
83
95
/// *Warning: very unsafe!*
96
+ #[ inline]
84
97
pub unsafe fn unapply_ptr_mut < ' a > ( & self , x : * mut U ) -> * mut T {
85
98
( ( x as usize ) - self . 0 ) as * mut T
86
99
}
87
100
/// Unapply the field offset to a reference.
88
101
///
89
102
/// *Warning: very unsafe!*
103
+ #[ inline]
90
104
pub unsafe fn unapply < ' a > ( & self , x : & ' a U ) -> & ' a T {
91
105
& * self . unapply_ptr ( x)
92
106
}
93
107
/// Unapply the field offset to a mutable reference.
94
108
///
95
109
/// *Warning: very unsafe!*
110
+ #[ inline]
96
111
pub unsafe fn unapply_mut < ' a > ( & self , x : & ' a mut U ) -> & ' a mut T {
97
112
& mut * self . unapply_ptr_mut ( x)
98
113
}
@@ -107,6 +122,7 @@ impl<T, U> FieldOffset<T, U> {
107
122
impl < T , U , V > Add < FieldOffset < U , V > > for FieldOffset < T , U > {
108
123
type Output = FieldOffset < T , V > ;
109
124
125
+ #[ inline]
110
126
fn add ( self , other : FieldOffset < U , V > ) -> FieldOffset < T , V > {
111
127
FieldOffset ( self . 0 + other. 0 , PhantomData )
112
128
}
@@ -140,12 +156,20 @@ impl<T, U> Clone for FieldOffset<T, U> {
140
156
/// `offset_of!(Foo => bar: Bar => x)`
141
157
#[ macro_export]
142
158
macro_rules! offset_of {
143
- ( $t: path => $f: ident) => {
144
- unsafe { $crate:: FieldOffset :: <$t, _>:: new( |x| {
145
- let $t { ref $f, .. } = * x;
146
- $f
147
- } ) }
148
- } ;
159
+ ( $t: tt => $f: tt) => { {
160
+ // Make sure the field exists, and is not being accessed via Deref.
161
+ let $t { $f: _, .. } ;
162
+
163
+ // Construct the offset
164
+ #[ allow( unused_unsafe) ]
165
+ unsafe {
166
+ $crate:: FieldOffset :: <$t, _>:: new( |x| {
167
+ // This is UB unless/until the compiler special-cases it to
168
+ // not enforce the validity constraint on `x`.
169
+ & ( * x) . $f as * const _
170
+ } )
171
+ }
172
+ } } ;
149
173
( $t: path => $f: ident: $( $rest: tt) * ) => {
150
174
offset_of!( $t => $f) + offset_of!( $( $rest) * )
151
175
} ;
@@ -167,6 +191,9 @@ mod tests {
167
191
y : Foo ,
168
192
}
169
193
194
+ #[ derive( Debug ) ]
195
+ struct Tuple ( i32 , f64 ) ;
196
+
170
197
#[ test]
171
198
fn test_simple ( ) {
172
199
// Get a pointer to `b` within `Foo`
@@ -193,6 +220,28 @@ mod tests {
193
220
assert ! ( x. b == 42.0 ) ;
194
221
}
195
222
223
+ #[ test]
224
+ fn test_tuple ( ) {
225
+ // Get a pointer to `b` within `Foo`
226
+ let tuple_1 = offset_of ! ( Tuple => 1 ) ;
227
+
228
+ // Construct an example `Foo`
229
+ let mut x = Tuple ( 1 , 42.0 ) ;
230
+
231
+ // Apply the pointer to get at `b` and read it
232
+ {
233
+ let y = tuple_1. apply ( & x) ;
234
+ assert ! ( * y == 42.0 ) ;
235
+ }
236
+
237
+ // Apply the pointer to get at `b` and mutate it
238
+ {
239
+ let y = tuple_1. apply_mut ( & mut x) ;
240
+ * y = 5.0 ;
241
+ }
242
+ assert ! ( x. 1 == 5.0 ) ;
243
+ }
244
+
196
245
#[ test]
197
246
fn test_nested ( ) {
198
247
// Construct an example `Foo`
0 commit comments