1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
2 | // SPDX-License-Identifier: MIT OR Apache-2.0 |
3 | |
4 | /*! |
5 | This crate expose the [`FieldOffsets`] derive macro and the types it uses. |
6 | |
7 | The macro allows to get const FieldOffset for member of a `#[repr(C)]` struct. |
8 | |
9 | The [`FieldOffset`] type is re-exported from the `field-offset` crate. |
10 | */ |
11 | #![no_std ] |
12 | |
13 | #[cfg (test)] |
14 | extern crate alloc; |
15 | |
16 | use core::pin::Pin; |
17 | |
18 | #[doc (inline)] |
19 | pub use const_field_offset_macro::FieldOffsets; |
20 | |
21 | pub use field_offset::{AllowPin, FieldOffset, NotPinned}; |
22 | |
23 | /// This trait needs to be implemented if you use the `#[pin_drop]` attribute. It enables |
24 | /// you to implement Drop for your type safely. |
25 | pub trait PinnedDrop { |
26 | /// This is the equivalent to the regular Drop trait with the difference that self |
27 | /// is pinned. |
28 | fn drop(self: Pin<&mut Self>); |
29 | |
30 | #[doc (hidden)] |
31 | fn do_safe_pinned_drop(&mut self) { |
32 | let p: Pin<&mut Self> = unsafe { Pin::new_unchecked(self) }; |
33 | p.drop() |
34 | } |
35 | } |
36 | |
37 | #[cfg (test)] |
38 | mod tests { |
39 | use super::*; |
40 | use crate as const_field_offset; |
41 | // ### Structures were change to repr(c) and to inherit FieldOffsets |
42 | |
43 | // Example structures |
44 | #[derive (Debug, FieldOffsets)] |
45 | #[repr (C)] |
46 | struct Foo { |
47 | a: u32, |
48 | b: f64, |
49 | c: bool, |
50 | } |
51 | |
52 | #[derive (Debug, FieldOffsets)] |
53 | #[repr (C)] |
54 | struct Bar { |
55 | x: u32, |
56 | y: Foo, |
57 | } |
58 | |
59 | #[test ] |
60 | #[allow (clippy::float_cmp)] // We want bit-wise equality here |
61 | fn test_simple() { |
62 | // Get a pointer to `b` within `Foo` |
63 | let foo_b = Foo::FIELD_OFFSETS.b; |
64 | |
65 | // Construct an example `Foo` |
66 | let mut x = Foo { a: 1, b: 2.0, c: false }; |
67 | |
68 | // Apply the pointer to get at `b` and read it |
69 | { |
70 | let y = foo_b.apply(&x); |
71 | assert_eq!(*y, 2.0); |
72 | } |
73 | |
74 | // Apply the pointer to get at `b` and mutate it |
75 | { |
76 | let y = foo_b.apply_mut(&mut x); |
77 | *y = 42.0; |
78 | } |
79 | assert_eq!(x.b, 42.0); |
80 | } |
81 | |
82 | #[test ] |
83 | #[allow (clippy::float_cmp)] // We want bit-wise equality here |
84 | fn test_nested() { |
85 | // Construct an example `Foo` |
86 | let mut x = Bar { x: 0, y: Foo { a: 1, b: 2.0, c: false } }; |
87 | |
88 | // Combine the pointer-to-members |
89 | let bar_y_b = Bar::FIELD_OFFSETS.y + Foo::FIELD_OFFSETS.b; |
90 | |
91 | // Apply the pointer to get at `b` and mutate it |
92 | { |
93 | let y = bar_y_b.apply_mut(&mut x); |
94 | *y = 42.0; |
95 | } |
96 | assert_eq!(x.y.b, 42.0); |
97 | } |
98 | |
99 | #[test ] |
100 | #[allow (clippy::float_cmp)] // We want bit-wise equality here |
101 | fn test_pin() { |
102 | use ::alloc::boxed::Box; |
103 | // Get a pointer to `b` within `Foo` |
104 | let foo_b = Foo::FIELD_OFFSETS.b; |
105 | let foo_b_pin = unsafe { foo_b.as_pinned_projection() }; |
106 | let foo_object = Box::pin(Foo { a: 21, b: 22.0, c: true }); |
107 | let pb: Pin<&f64> = foo_b_pin.apply_pin(foo_object.as_ref()); |
108 | assert_eq!(*pb, 22.0); |
109 | |
110 | let mut x = Box::pin(Bar { x: 0, y: Foo { a: 1, b: 52.0, c: false } }); |
111 | let bar_y_b = Bar::FIELD_OFFSETS.y + foo_b_pin; |
112 | assert_eq!(*bar_y_b.apply(&*x), 52.0); |
113 | |
114 | let bar_y_pin = unsafe { Bar::FIELD_OFFSETS.y.as_pinned_projection() }; |
115 | *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.; |
116 | assert_eq!(x.y.b, 12.0); |
117 | } |
118 | } |
119 | |
120 | /** |
121 | Test that one can't implement Unpin for pinned struct |
122 | |
123 | This should work: |
124 | |
125 | ``` |
126 | #[derive(const_field_offset::FieldOffsets)] |
127 | #[repr(C)] |
128 | #[pin] |
129 | struct MyStructPin { a: u32 } |
130 | ``` |
131 | |
132 | But this not: |
133 | |
134 | ```compile_fail |
135 | #[derive(const_field_offset::FieldOffsets)] |
136 | #[repr(C)] |
137 | #[pin] |
138 | struct MyStructPin { a: u32 } |
139 | impl Unpin for MyStructPin {}; |
140 | ``` |
141 | |
142 | */ |
143 | #[cfg (doctest)] |
144 | const NO_IMPL_UNPIN: u32 = 0; |
145 | |
146 | #[doc (hidden)] |
147 | #[cfg (feature = "field-offset-trait" )] |
148 | mod internal { |
149 | use super::*; |
150 | pub trait CombineFlag { |
151 | type Output; |
152 | } |
153 | impl CombineFlag for (AllowPin, AllowPin) { |
154 | type Output = AllowPin; |
155 | } |
156 | impl CombineFlag for (NotPinned, AllowPin) { |
157 | type Output = NotPinned; |
158 | } |
159 | impl CombineFlag for (AllowPin, NotPinned) { |
160 | type Output = NotPinned; |
161 | } |
162 | impl CombineFlag for (NotPinned, NotPinned) { |
163 | type Output = NotPinned; |
164 | } |
165 | } |
166 | |
167 | #[cfg (feature = "field-offset-trait" )] |
168 | pub trait ConstFieldOffset: Copy { |
169 | /// The type of the container |
170 | type Container; |
171 | /// The type of the field |
172 | type Field; |
173 | |
174 | /// Can be AllowPin or NotPinned |
175 | type PinFlag; |
176 | |
177 | const OFFSET: FieldOffset<Self::Container, Self::Field, Self::PinFlag>; |
178 | |
179 | fn as_field_offset(self) -> FieldOffset<Self::Container, Self::Field, Self::PinFlag> { |
180 | Self::OFFSET |
181 | } |
182 | fn get_byte_offset(self) -> usize { |
183 | Self::OFFSET.get_byte_offset() |
184 | } |
185 | fn apply(self, x: &Self::Container) -> &Self::Field { |
186 | Self::OFFSET.apply(x) |
187 | } |
188 | fn apply_mut(self, x: &mut Self::Container) -> &mut Self::Field { |
189 | Self::OFFSET.apply_mut(x) |
190 | } |
191 | |
192 | fn apply_pin(self, x: Pin<&Self::Container>) -> Pin<&Self::Field> |
193 | where |
194 | Self: ConstFieldOffset<PinFlag = AllowPin>, |
195 | { |
196 | Self::OFFSET.apply_pin(x) |
197 | } |
198 | fn apply_pin_mut(self, x: Pin<&mut Self::Container>) -> Pin<&mut Self::Field> |
199 | where |
200 | Self: ConstFieldOffset<PinFlag = AllowPin>, |
201 | { |
202 | Self::OFFSET.apply_pin_mut(x) |
203 | } |
204 | } |
205 | |
206 | /// This can be used to transmute a FieldOffset from a NotPinned to any pin flag. |
207 | /// This is only valid if we know that the offset is actually valid for this Flag. |
208 | #[cfg (feature = "field-offset-trait" )] |
209 | union TransmutePinFlag<Container, Field, PinFlag> { |
210 | x: FieldOffset<Container, Field, PinFlag>, |
211 | y: FieldOffset<Container, Field>, |
212 | } |
213 | |
214 | /// Helper class used as the result of the addition of two types that implement the `ConstFieldOffset` trait |
215 | #[derive (Copy, Clone)] |
216 | #[cfg (feature = "field-offset-trait" )] |
217 | pub struct ConstFieldOffsetSum<A: ConstFieldOffset, B: ConstFieldOffset>(pub A, pub B); |
218 | |
219 | #[cfg (feature = "field-offset-trait" )] |
220 | impl<A: ConstFieldOffset, B: ConstFieldOffset> ConstFieldOffset for ConstFieldOffsetSum<A, B> |
221 | where |
222 | A: ConstFieldOffset<Field = B::Container>, |
223 | (A::PinFlag, B::PinFlag): internal::CombineFlag, |
224 | { |
225 | type Container = A::Container; |
226 | type Field = B::Field; |
227 | type PinFlag = <(A::PinFlag, B::PinFlag) as internal::CombineFlag>::Output; |
228 | const OFFSET: FieldOffset<Self::Container, Self::Field, Self::PinFlag> = unsafe { |
229 | TransmutePinFlag { |
230 | y: FieldOffset::new_from_offset( |
231 | A::OFFSET.get_byte_offset() + B::OFFSET.get_byte_offset(), |
232 | ), |
233 | } |
234 | .x |
235 | }; |
236 | } |
237 | |
238 | #[cfg (feature = "field-offset-trait" )] |
239 | impl<A: ConstFieldOffset, B: ConstFieldOffset, Other> ::core::ops::Add<Other> |
240 | for ConstFieldOffsetSum<A, B> |
241 | where |
242 | Self: ConstFieldOffset, |
243 | Other: ConstFieldOffset<Container = <Self as ConstFieldOffset>::Field>, |
244 | { |
245 | type Output = ConstFieldOffsetSum<Self, Other>; |
246 | fn add(self, other: Other) -> Self::Output { |
247 | ConstFieldOffsetSum(self, other) |
248 | } |
249 | } |
250 | |