facet_core/types/def/pointer.rs
1use crate::{PtrConst, PtrMut, PtrUninit, Shape, bitflags};
2
3/// Describes a pointer — including a vtable to query and alter its state,
4/// and the inner shape (the pointee type in the pointer).
5#[derive(Clone, Copy, Debug)]
6#[repr(C)]
7pub struct PointerDef {
8 /// vtable for interacting with the pointer
9 pub vtable: &'static PointerVTable,
10
11 /// shape of the inner type of the pointer, if not opaque
12 pub pointee: Option<&'static Shape>,
13
14 /// shape of the corresponding strong pointer, if this pointer is weak
15 ///
16 /// the layer of indirection is to break the strong <-> weak reference cycle,
17 /// since consts may not have cycles in their definitions.
18 pub weak: Option<fn() -> &'static Shape>,
19
20 /// shape of the corresponding weak pointer, if this pointer is strong
21 pub strong: Option<&'static Shape>,
22
23 /// Flags representing various characteristics of the pointer
24 pub flags: PointerFlags,
25
26 /// An optional field to identify the kind of pointer
27 pub known: Option<KnownPointer>,
28}
29
30impl PointerDef {
31 /// Returns shape of the inner type of the pointer, if not opaque
32 pub const fn pointee(&self) -> Option<&'static Shape> {
33 self.pointee
34 }
35
36 /// Returns shape of the corresponding strong pointer, if this pointer is weak
37 pub fn weak(&self) -> Option<&'static Shape> {
38 self.weak.map(|f| f())
39 }
40
41 /// Returns shape of the corresponding weak pointer, if this pointer is strong
42 pub const fn strong(&self) -> Option<&'static Shape> {
43 self.strong
44 }
45
46 /// Whether a new pointer can be constructed from an owned value of its pointee type.
47 pub const fn constructible_from_pointee(&self) -> bool {
48 self.vtable.new_into_fn.is_some()
49 || matches!(
50 self.known,
51 Some(KnownPointer::Box | KnownPointer::Rc | KnownPointer::Arc)
52 )
53 }
54}
55
56bitflags! {
57 /// Flags to represent various characteristics of pointers
58 pub struct PointerFlags: u8 {
59 /// An empty set of flags
60 const EMPTY = 0;
61
62 /// Whether the pointer is weak (like `std::sync::Weak`)
63 const WEAK = 1 << 0;
64 /// Whether the pointer is atomic (like `std::sync::Arc`)
65 const ATOMIC = 1 << 1;
66 /// Whether the pointer is a lock (like `std::sync::Mutex`)
67 const LOCK = 1 << 2;
68 }
69}
70
71/// Tries to upgrade the weak pointer to a strong one.
72///
73/// If the upgrade succeeds, initializes the pointer into the given `strong`, and returns a
74/// copy of `strong`, which has been guaranteed to be initialized. If the upgrade fails, `None` is
75/// returned and `strong` is not initialized.
76///
77/// `weak` is not moved out of.
78///
79/// # Safety
80///
81/// `weak` must be a valid weak pointer (like [`alloc::sync::Weak`] or [`alloc::rc::Weak`]).
82///
83/// `strong` must be allocated, and of the right layout for the corresponding pointer.
84///
85/// `strong` must not have been initialized yet.
86pub type UpgradeIntoFn = unsafe fn(weak: PtrMut, strong: PtrUninit) -> Option<PtrMut>;
87
88/// Downgrades a strong pointer to a weak one.
89///
90/// Initializes the pointer into the given `weak`, and returns a copy of `weak`, which has
91/// been guaranteed to be initialized.
92///
93/// Only strong pointers can be downgraded (like [`alloc::sync::Arc`] or [`alloc::rc::Rc`]).
94///
95/// # Safety
96///
97/// `strong` must be a valid strong pointer (like [`alloc::sync::Arc`] or [`alloc::rc::Rc`]).
98///
99/// `weak` must be allocated, and of the right layout for the corresponding weak pointer.
100///
101/// `weak` must not have been initialized yet.
102pub type DowngradeIntoFn = unsafe fn(strong: PtrMut, weak: PtrUninit) -> PtrMut;
103
104/// Tries to obtain a reference to the inner value of the pointer.
105///
106/// This can only be used with strong pointers (like [`alloc::sync::Arc`] or [`alloc::rc::Rc`]).
107///
108/// # Safety
109///
110/// `this` must be a valid strong pointer (like [`alloc::sync::Arc`] or [`alloc::rc::Rc`]).
111pub type BorrowFn = unsafe fn(this: PtrConst) -> PtrConst;
112
113/// Creates a new pointer wrapping the given value.
114///
115/// Initializes the pointer into the given `this`, and returns a copy of `this`, which has
116/// been guaranteed to be initialized.
117///
118/// This can only be used with strong pointers (like [`alloc::sync::Arc`] or [`alloc::rc::Rc`]).
119///
120/// # Safety
121///
122/// `this` must be allocated, and of the right layout for the corresponding pointer.
123///
124/// `this` must not have been initialized yet.
125///
126/// `ptr` must point to a value of type `T`.
127///
128/// `ptr` is moved out of (with [`core::ptr::read`]) — it should be deallocated afterwards (e.g.
129/// with [`core::mem::forget`]) but NOT dropped).
130pub type NewIntoFn = unsafe fn(this: PtrUninit, ptr: PtrMut) -> PtrMut;
131
132/// Type-erased result of locking a mutex-like or reader-writer lock pointer.
133///
134/// The type parameter `P` determines the capability of the returned pointer:
135/// - `PtrConst` for read locks (shared access)
136/// - `PtrMut` for write/mutex locks (exclusive access)
137pub struct LockResult<P> {
138 /// The data that was locked
139 data: P,
140 /// The guard that protects the data
141 guard: PtrConst,
142 /// The vtable for the guard
143 guard_vtable: &'static LockGuardVTable,
144}
145
146/// Result of acquiring a read lock (shared access) - data is immutable
147pub type ReadLockResult = LockResult<PtrConst>;
148
149/// Result of acquiring a write/mutex lock (exclusive access) - data is mutable
150pub type WriteLockResult = LockResult<PtrMut>;
151
152impl<P> LockResult<P> {
153 /// Creates a new `LockResult` from its components.
154 ///
155 /// # Safety
156 ///
157 /// - `data` must point to valid data protected by the guard
158 /// - `guard` must point to a valid guard that, when dropped via `guard_vtable.drop_in_place`,
159 /// will release the lock
160 /// - The guard must outlive any use of `data`
161 #[must_use]
162 pub const unsafe fn new(data: P, guard: PtrConst, guard_vtable: &'static LockGuardVTable) -> Self {
163 Self {
164 data,
165 guard,
166 guard_vtable,
167 }
168 }
169
170 /// Returns a reference to the locked data
171 #[must_use]
172 pub const fn data(&self) -> &P {
173 &self.data
174 }
175}
176
177impl WriteLockResult {
178 /// Returns a const pointer to the locked data (convenience for write locks)
179 #[must_use]
180 pub const fn data_const(&self) -> PtrConst {
181 self.data.as_const()
182 }
183}
184
185impl<P> Drop for LockResult<P> {
186 fn drop(&mut self) {
187 unsafe {
188 (self.guard_vtable.drop_in_place)(self.guard);
189 }
190 }
191}
192
193/// Functions for manipulating a guard
194pub struct LockGuardVTable {
195 /// Drops the guard in place
196 pub drop_in_place: unsafe fn(guard: PtrConst),
197}
198
199/// Acquires a lock on a mutex-like pointer (exclusive access)
200pub type LockFn = unsafe fn(opaque: PtrConst) -> Result<WriteLockResult, ()>;
201
202/// Acquires a read lock on a reader-writer lock-like pointer (shared access)
203pub type ReadFn = unsafe fn(opaque: PtrConst) -> Result<ReadLockResult, ()>;
204
205/// Acquires a write lock on a reader-writer lock-like pointer (exclusive access)
206pub type WriteFn = unsafe fn(opaque: PtrConst) -> Result<WriteLockResult, ()>;
207
208/// Creates a new slice builder for a pointer: ie. for `Arc<[u8]>`, it builds a
209/// `Vec<u8>` internally, to which you can push, and then turn into an `Arc<[u8]>` at
210/// the last stage.
211///
212/// This works for any `U` in `Arc<[U]>` because those have separate concrete implementations, and
213/// thus, separate concrete vtables.
214pub type SliceBuilderNewFn = fn() -> PtrMut;
215
216/// Pushes a value into a slice builder.
217///
218/// # Safety
219///
220/// Item must point to a valid value of type `U` and must be initialized.
221/// Function is infallible as it uses the global allocator.
222pub type SliceBuilderPushFn = unsafe fn(builder: PtrMut, item: PtrMut);
223
224/// Converts a slice builder into a pointer. This takes ownership of the builder
225/// and frees the backing storage.
226///
227/// # Safety
228///
229/// The builder must be valid and must not be used after this function is called.
230/// Function is infallible as it uses the global allocator.
231pub type SliceBuilderConvertFn = unsafe fn(builder: PtrMut) -> PtrConst;
232
233/// Frees a slice builder without converting it into a pointer
234///
235/// # Safety
236///
237/// The builder must be valid and must not be used after this function is called.
238pub type SliceBuilderFreeFn = unsafe fn(builder: PtrMut);
239
240/// Functions for creating and manipulating slice builders.
241#[derive(Debug, Clone, Copy)]
242pub struct SliceBuilderVTable {
243 /// See [`SliceBuilderNewFn`]
244 pub new_fn: SliceBuilderNewFn,
245 /// See [`SliceBuilderPushFn`]
246 pub push_fn: SliceBuilderPushFn,
247 /// See [`SliceBuilderConvertFn`]
248 pub convert_fn: SliceBuilderConvertFn,
249 /// See [`SliceBuilderFreeFn`]
250 pub free_fn: SliceBuilderFreeFn,
251}
252
253impl SliceBuilderVTable {
254 /// Const ctor for slice builder vtable; all hooks required.
255 #[must_use]
256 pub const fn new(
257 new_fn: SliceBuilderNewFn,
258 push_fn: SliceBuilderPushFn,
259 convert_fn: SliceBuilderConvertFn,
260 free_fn: SliceBuilderFreeFn,
261 ) -> Self {
262 Self {
263 new_fn,
264 push_fn,
265 convert_fn,
266 free_fn,
267 }
268 }
269}
270
271/// Functions for interacting with a pointer
272#[derive(Debug, Clone, Copy)]
273pub struct PointerVTable {
274 /// See [`UpgradeIntoFn`]
275 pub upgrade_into_fn: Option<UpgradeIntoFn>,
276
277 /// See [`DowngradeIntoFn`]
278 pub downgrade_into_fn: Option<DowngradeIntoFn>,
279
280 /// See [`BorrowFn`]
281 pub borrow_fn: Option<BorrowFn>,
282
283 /// See [`NewIntoFn`]
284 pub new_into_fn: Option<NewIntoFn>,
285
286 /// See [`LockFn`]
287 pub lock_fn: Option<LockFn>,
288
289 /// See [`ReadFn`]
290 pub read_fn: Option<ReadFn>,
291
292 /// See [`WriteFn`]
293 pub write_fn: Option<WriteFn>,
294
295 /// See [`SliceBuilderVTable`]
296 pub slice_builder_vtable: Option<&'static SliceBuilderVTable>,
297}
298
299impl Default for PointerVTable {
300 fn default() -> Self {
301 Self::new()
302 }
303}
304
305impl PointerVTable {
306 /// Const ctor with all entries set to `None`.
307 #[must_use]
308 pub const fn new() -> Self {
309 Self {
310 upgrade_into_fn: None,
311 downgrade_into_fn: None,
312 borrow_fn: None,
313 new_into_fn: None,
314 lock_fn: None,
315 read_fn: None,
316 write_fn: None,
317 slice_builder_vtable: None,
318 }
319 }
320}
321
322/// Represents common standard library pointer kinds
323#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
324pub enum KnownPointer {
325 /// [`Box<T>`](alloc::boxed::Box), heap-allocated values with single ownership
326 Box,
327 /// [`Rc<T>`](alloc::rc::Rc), reference-counted values with multiple ownership
328 Rc,
329 /// [`Weak<T>`](alloc::rc::Weak), a weak reference to an `Rc`-managed value
330 RcWeak,
331 /// [`Arc<T>`](alloc::sync::Arc), thread-safe reference-counted values with multiple ownership
332 Arc,
333 /// [`Weak<T>`](alloc::sync::Weak), a weak reference to an `Arc`-managed value
334 ArcWeak,
335 /// [`Cow<'a, T>`](alloc::borrow::Cow), a clone-on-write smart pointer
336 Cow,
337 /// [`Pin<P>`](core::pin::Pin), a type that pins values behind a pointer
338 Pin,
339 /// [`Cell<T>`](core::cell::Cell), a mutable memory location with interior mutability
340 Cell,
341 /// [`RefCell<T>`](core::cell::RefCell), a mutable memory location with dynamic borrowing rules
342 RefCell,
343 /// [`OnceCell<T>`](core::cell::OnceCell), a cell that can be written to only once
344 OnceCell,
345 /// `Mutex<T>`, a mutual exclusion primitive (requires std)
346 Mutex,
347 /// `RwLock<T>`, a reader-writer lock (requires std)
348 RwLock,
349 /// `OnceLock<T>`, a cell that can be written to only once (requires std)
350 OnceLock,
351 /// `LazyLock<T, F>`, a lazy-initialized value (requires std)
352 LazyLock,
353 /// [`NonNull<T>`](core::ptr::NonNull), a wrapper around a raw pointer that is not null
354 NonNull,
355 /// `&T`
356 SharedReference,
357 /// `&mut T`
358 ExclusiveReference,
359}