late_struct/descriptor.rs
1use std::{
2 alloc::Layout,
3 any::{TypeId, type_name},
4 marker::PhantomData,
5 mem::MaybeUninit,
6 ptr, slice,
7 sync::atomic::{AtomicPtr, AtomicUsize, Ordering::*},
8};
9
10use crate::{LateField, LateLayoutInitToken, LateStruct};
11
12// === Raw === //
13
14/// A raw (untyped) descriptor for a [`LateStruct`].
15///
16/// You can obtain a reference to this descriptor using [`LateStruct::raw_descriptor`].
17///
18/// Unlike [`LateStructDescriptor`], the type of the `LateStruct` to which this descriptor belongs
19/// is erased.
20#[derive(Debug)]
21pub struct RawLateStructDescriptor {
22 // These need to be accessed in `init`.
23 pub(crate) size: AtomicUsize,
24 pub(crate) align: AtomicUsize,
25 pub(crate) fields: AtomicPtr<&'static [&'static RawLateFieldDescriptor]>,
26
27 type_name: fn() -> &'static str,
28 type_id: fn() -> TypeId,
29}
30
31impl RawLateStructDescriptor {
32 // This needs to be accessed in `traits`
33 pub(crate) const fn new<S: LateStruct>() -> Self {
34 Self {
35 size: AtomicUsize::new(0),
36 align: AtomicUsize::new(0),
37 fields: AtomicPtr::new(ptr::null_mut()),
38 type_name: type_name::<S>,
39 type_id: TypeId::of::<S>,
40 }
41 }
42
43 /// Fetches the name of the type for which [`LateStruct`] is implemented.
44 pub fn type_name(&self) -> &'static str {
45 (self.type_name)()
46 }
47
48 /// Fetches the [`TypeId`] of the type for which [`LateStruct`] is implemented.
49 pub fn type_id(&self) -> TypeId {
50 (self.type_id)()
51 }
52
53 /// Fetches the overall layout of the structure generated dynamically for this structure.
54 pub fn layout(&self, token: LateLayoutInitToken) -> Layout {
55 let _ = token;
56
57 unsafe {
58 Layout::from_size_align_unchecked(self.size.load(Relaxed), self.align.load(Relaxed))
59 }
60 }
61
62 /// Fetches the list of fields contained within this structure.
63 ///
64 /// The order of this list is in no way stable across multiple compilations.
65 pub fn fields(&self, token: LateLayoutInitToken) -> &'static [&'static RawLateFieldDescriptor] {
66 let _ = token;
67
68 unsafe { &**self.fields.load(Relaxed) }
69 }
70
71 /// Downcasts the descriptor to a typed [`LateStructDescriptor`] instance.
72 ///
73 /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing or the
74 /// method will panic.
75 pub fn typed<S: LateStruct>(&self) -> &LateStructDescriptor<S> {
76 LateStructDescriptor::wrap(self)
77 }
78
79 /// Downcasts the descriptor to a typed [`LateStructDescriptor`] instance.
80 ///
81 /// This operation can be safely performed with [`RawLateStructDescriptor::typed`].
82 ///
83 /// ## Safety
84 ///
85 /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing. Failing
86 /// to do so will not cause immediate undefined behavior but could cause latent undefined
87 /// behavior if passed to a method expecting the descriptor to belong to a certain
88 /// [`LateStruct`] type.
89 pub unsafe fn typed_unchecked<S: LateStruct>(&self) -> &LateStructDescriptor<S> {
90 unsafe { LateStructDescriptor::wrap_unchecked(self) }
91 }
92}
93
94/// A raw (untyped) descriptor for a [`LateField`].
95///
96/// You can obtain a reference to this descriptor using [`LateField::raw_descriptor`].
97///
98/// Unlike [`LateFieldDescriptor`], the type of the [`LateStruct`] to which this descriptor belongs
99/// is erased. There is no descriptor which also specifies the type of the [`LateField`] since it
100/// would be redundant.
101#[derive(Debug)]
102pub struct RawLateFieldDescriptor {
103 /// The index of this field in the struct.
104 // This needs to be accessed in `init`.
105 pub(crate) index: AtomicUsize,
106
107 /// The byte offset of this field in the struct.
108 // This needs to be accessed in `init`.
109 pub(crate) offset: AtomicUsize,
110
111 /// The layout of this field's type.
112 layout: Layout,
113
114 /// Initializes the supplied `*mut MaybeUninit<Key::Value>`.
115 init: unsafe fn(*mut u8),
116
117 /// Drops the supplied `*mut Key::Value` in place.
118 drop: unsafe fn(*mut u8),
119
120 /// Transforms the supplied `*mut Key::Value` in the first parameter into a
121 /// `*mut Struct::EraseTo` and writes it out into the `*mut *mut Struct::EraseTo` pointee in the
122 /// second parameter.
123 as_erased: fn(*mut u8, *mut ()),
124
125 /// Fetches the type name of `Key`.
126 key_type_name: fn() -> &'static str,
127
128 /// Fetches the type ID of `Key`.
129 key_type_id: fn() -> TypeId,
130
131 /// Fetches the type ID of `Struct`.
132 parent_struct: fn() -> &'static RawLateStructDescriptor,
133}
134
135impl RawLateFieldDescriptor {
136 pub(crate) const fn new<S, F>() -> Self
137 where
138 S: LateStruct,
139 F: LateField<S>,
140 {
141 Self {
142 index: AtomicUsize::new(usize::MAX),
143 offset: AtomicUsize::new(usize::MAX),
144 layout: Layout::new::<F::Value>(),
145 init: |ptr| unsafe { ptr.cast::<F::Value>().write(<F::Value>::default()) },
146 drop: |ptr| unsafe { ptr.cast::<F::Value>().drop_in_place() },
147 as_erased: |ptr, write_out| unsafe {
148 write_out
149 .cast::<*mut S::EraseTo>()
150 .write(F::coerce(ptr.cast::<F::Value>()));
151 },
152 key_type_name: type_name::<F>,
153 key_type_id: TypeId::of::<F>,
154 parent_struct: S::raw_descriptor,
155 }
156 }
157
158 /// Fetches the descriptor of the struct to which this field belongs.
159 pub fn parent_struct(&self) -> &'static RawLateStructDescriptor {
160 (self.parent_struct)()
161 }
162
163 /// Fetches the index of the field within the [RawLateStructDescriptor::fields] list of the
164 /// struct to which this field belongs. The ordering of these indices is not stable across
165 /// compilations.
166 pub fn index(&self, token: LateLayoutInitToken) -> usize {
167 let _ = token;
168
169 self.index.load(Relaxed)
170 }
171
172 /// Fetches the offset of this field's value within the overall struct.
173 pub fn offset(&self, token: LateLayoutInitToken) -> usize {
174 let _ = token;
175
176 self.offset.load(Relaxed)
177 }
178
179 /// Fetches the layout of the field's value.
180 pub fn layout(&self) -> Layout {
181 self.layout
182 }
183
184 /// Initializes `value` to an instance of the field's value obtained by calling its [`Default`]
185 /// implementation.
186 ///
187 /// ## Safety
188 ///
189 /// `value` must point to an uninitialized location in memory large enough to accommodate the
190 /// field's value.
191 pub unsafe fn init(&self, value: *mut u8) {
192 unsafe { (self.init)(value) }
193 }
194
195 /// Drops the instance of the field's value pointed to by `value`.
196 ///
197 /// ## Safety
198 ///
199 /// `value` must point to an initialized instance of the field's value.
200 pub unsafe fn drop(&self, value: *mut u8) {
201 unsafe { (self.drop)(value) }
202 }
203
204 /// Adorns an untyped pointer in memory with the metadata required to become a pointer to
205 /// the specified [`LateStruct`]'s [`S::EraseTo`](LateStruct::EraseTo) type.
206 ///
207 /// `value` need not point to a valid location in memory.
208 ///
209 /// This operation can be performed safely by converting this descriptor into a
210 /// [`LateFieldDescriptor`] using the [`RawLateFieldDescriptor::typed`] method and calling its
211 /// [`LateFieldDescriptor::erase_value`] method.
212 ///
213 /// ## Safety
214 ///
215 /// `S` must match the type of the [`LateStruct`] marker this descriptor is describing.
216 ///
217 pub unsafe fn erase_value<S: LateStruct>(&self, value: *mut u8) -> *mut S::EraseTo {
218 debug_assert_eq!(TypeId::of::<S>(), self.parent_struct().type_id());
219
220 let mut out = MaybeUninit::<*mut S::EraseTo>::uninit();
221
222 unsafe {
223 (self.as_erased)(value, out.as_mut_ptr().cast());
224 out.assume_init()
225 }
226 }
227
228 /// Fetches the [`type_name`] of the [`LateField`] instance—that is, the name of the marker type.
229 pub fn key_type_name(&self) -> &'static str {
230 (self.key_type_name)()
231 }
232
233 /// Fetches the [`TypeId`] of the [`LateField`] instance—that is, the ID of the marker type.
234 pub fn key_type_id(&self) -> TypeId {
235 (self.key_type_id)()
236 }
237
238 /// Downcasts the descriptor to a typed [`LateFieldDescriptor`] instance.
239 ///
240 /// `S` must match the type of the [`LateStruct`] marker to which this descriptor belongs or the
241 /// method will panic.
242 pub fn typed<S: LateStruct>(&self) -> &LateFieldDescriptor<S> {
243 LateFieldDescriptor::wrap(self)
244 }
245
246 /// Downcasts the descriptor to a typed [`LateFieldDescriptor`] instance.
247 ///
248 /// This operation can be safely performed with [`RawLateFieldDescriptor::typed`].
249 ///
250 /// ## Safety
251 ///
252 /// `S` must match the type of the [`LateStruct`] marker to which this descriptor belongs.
253 /// Failing to do so will not cause immediate undefined behavior but could cause latent
254 /// undefined behavior if passed to a method expecting the descriptor to belong to a certain
255 /// [`LateStruct`] type.
256 pub unsafe fn typed_unchecked<S: LateStruct>(&self) -> &LateFieldDescriptor<S> {
257 unsafe { LateFieldDescriptor::wrap_unchecked(self) }
258 }
259}
260
261// === Newtypes === //
262
263/// A typed descriptor for a [`LateStruct`].
264///
265/// You can obtain a reference to this descriptor using [`LateStruct::descriptor`].
266///
267/// Unlike [`RawLateStructDescriptor`], this descriptor encodes the type of the `LateStruct` it's
268/// describing.
269#[derive(Debug)]
270#[repr(transparent)]
271pub struct LateStructDescriptor<S: LateStruct> {
272 _ty: PhantomData<fn(S) -> S>,
273 raw: RawLateStructDescriptor,
274}
275
276// Conversions
277impl<S: LateStruct> LateStructDescriptor<S> {
278 fn wrap(raw: &RawLateStructDescriptor) -> &LateStructDescriptor<S> {
279 assert_eq!(raw.type_id(), TypeId::of::<S>());
280
281 unsafe { Self::wrap_unchecked(raw) }
282 }
283
284 const unsafe fn wrap_unchecked(raw: &RawLateStructDescriptor) -> &LateStructDescriptor<S> {
285 unsafe { &*(raw as *const RawLateStructDescriptor as *const LateStructDescriptor<S>) }
286 }
287
288 /// Erases the type information from this descriptor, producing its equivalent
289 /// [`RawLateStructDescriptor`].
290 pub const fn raw(&self) -> &RawLateStructDescriptor {
291 &self.raw
292 }
293}
294
295// Forwards
296impl<S: LateStruct> LateStructDescriptor<S> {
297 /// Forwards to [`RawLateStructDescriptor::layout`].
298 pub fn layout(&self, token: LateLayoutInitToken) -> Layout {
299 self.raw.layout(token)
300 }
301
302 /// Forwards to [`RawLateStructDescriptor::fields`].
303 pub fn fields(&self, token: LateLayoutInitToken) -> &'static [&'static LateFieldDescriptor<S>] {
304 unsafe { LateFieldDescriptor::wrap_slice_unchecked(self.raw.fields(token)) }
305 }
306}
307
308/// A typed descriptor for a [`LateField`].
309///
310/// You can obtain a reference to this descriptor using [`LateField::descriptor`].
311///
312/// Unlike [`LateFieldDescriptor`], this descriptor encodes the type of the `LateStruct` to which
313/// the field belongs.
314#[derive(Debug)]
315#[repr(transparent)]
316pub struct LateFieldDescriptor<S: LateStruct> {
317 _ty: PhantomData<fn(S) -> S>,
318 raw: RawLateFieldDescriptor,
319}
320
321// Conversions
322impl<S: LateStruct> LateFieldDescriptor<S> {
323 fn wrap(raw: &RawLateFieldDescriptor) -> &LateFieldDescriptor<S> {
324 assert_eq!(raw.parent_struct().type_id(), TypeId::of::<S>());
325
326 unsafe { Self::wrap_unchecked(raw) }
327 }
328
329 const unsafe fn wrap_unchecked(raw: &RawLateFieldDescriptor) -> &LateFieldDescriptor<S> {
330 unsafe { &*(raw as *const RawLateFieldDescriptor as *const LateFieldDescriptor<S>) }
331 }
332
333 const unsafe fn wrap_slice_unchecked<'a, 'b>(
334 raw: &'a [&'b RawLateFieldDescriptor],
335 ) -> &'a [&'b LateFieldDescriptor<S>] {
336 unsafe {
337 slice::from_raw_parts(raw.as_ptr().cast::<&'b LateFieldDescriptor<S>>(), raw.len())
338 }
339 }
340
341 /// Erases the type information from this descriptor, producing its equivalent
342 /// [`RawLateFieldDescriptor`].
343 pub const fn raw(&self) -> &RawLateFieldDescriptor {
344 &self.raw
345 }
346}
347
348// Forwards
349impl<S: LateStruct> LateFieldDescriptor<S> {
350 /// Forwards to [`RawLateFieldDescriptor::index`].
351 pub fn index(&self, token: LateLayoutInitToken) -> usize {
352 self.raw.index(token)
353 }
354
355 /// Forwards to [`RawLateFieldDescriptor::offset`].
356 pub fn offset(&self, token: LateLayoutInitToken) -> usize {
357 self.raw.offset(token)
358 }
359
360 /// Forwards to [`RawLateFieldDescriptor::layout`].
361 pub fn layout(&self) -> Layout {
362 self.raw.layout()
363 }
364
365 /// Forwards to [`RawLateFieldDescriptor::init`].
366 ///
367 /// ## Safety
368 ///
369 /// See safety docs of `RawLateFieldDescriptor::init`.
370 ///
371 pub unsafe fn init(&self, value: *mut u8) {
372 unsafe { self.raw.init(value) }
373 }
374
375 /// Forwards to [`RawLateFieldDescriptor::drop`].
376 ///
377 /// ## Safety
378 ///
379 /// See safety docs of `RawLateFieldDescriptor::drop`.
380 ///
381 pub unsafe fn drop(&self, value: *mut u8) {
382 unsafe { self.raw.drop(value) }
383 }
384
385 /// Forwards to [`RawLateFieldDescriptor::erase_value`] but uses type information to make the
386 /// operation safe.
387 #[allow(clippy::not_unsafe_ptr_arg_deref)] // We don't dereference `value`
388 pub fn erase_value(&self, value: *mut u8) -> *mut S::EraseTo {
389 unsafe { self.raw.erase_value::<S>(value) }
390 }
391
392 /// Forwards to [`RawLateFieldDescriptor::key_type_name`].
393 pub fn key_type_name(&self) -> &'static str {
394 self.raw.key_type_name()
395 }
396
397 /// Forwards to [`RawLateFieldDescriptor::key_type_id`].
398 pub fn key_type_id(&self) -> TypeId {
399 self.raw.key_type_id()
400 }
401
402 /// Forwards to [`RawLateFieldDescriptor::parent_struct`] but preserves the type annotation.
403 pub fn parent_struct(&self) -> &'static LateStructDescriptor<S> {
404 unsafe { self.raw.parent_struct().typed_unchecked() }
405 }
406}