maybe_cell/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2
3/// Data structure variants with run-time sanity checks for debugging
4pub mod checked {
5    use core::{
6        cell::UnsafeCell,
7        mem::MaybeUninit,
8        ptr,
9        sync::atomic::{AtomicBool, Ordering},
10    };
11
12    /// Equivalent to an `UnsafeCell<MaybeUninit<T>>`, this cell may hold uninitialized data.
13    #[derive(Debug)]
14    pub struct Maybe<T> {
15        data: UnsafeCell<MaybeUninit<T>>,
16        loaded: AtomicBool,
17    }
18
19    impl<T> Maybe<T> {
20        /// Create a new, empty `Maybe<T>`.
21        #[inline]
22        pub const fn empty() -> Self {
23            Self {
24                data: UnsafeCell::new(MaybeUninit::uninit()),
25                loaded: AtomicBool::new(false),
26            }
27        }
28
29        /// Create a new, populated `Maybe<T>`.
30        #[inline]
31        pub const fn new(data: T) -> Self {
32            Self {
33                data: UnsafeCell::new(MaybeUninit::new(data)),
34                loaded: AtomicBool::new(true),
35            }
36        }
37
38        /// Access the contained value as a constant pointer.
39        #[inline]
40        pub unsafe fn as_ptr(&self) -> *const T {
41            assert_eq!(
42                self.loaded.load(Ordering::Relaxed),
43                true,
44                "as_ptr for uninitialized cell"
45            );
46
47            (&*self.data.get()).as_ptr()
48        }
49
50        /// Access the contained value as a mutable pointer.
51        #[inline]
52        pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
53            assert_eq!(
54                self.loaded.load(Ordering::Relaxed),
55                true,
56                "as_mut_ptr for uninitialized cell"
57            );
58
59            (&mut *self.data.get()).as_mut_ptr()
60        }
61
62        /// Obtain a reference to the contained value. This method is `unsafe` because
63        /// the value may not have been initialized.
64        #[inline]
65        pub unsafe fn as_ref(&self) -> &T {
66            assert_eq!(
67                self.loaded.load(Ordering::Relaxed),
68                true,
69                "as_ref for uninitialized cell"
70            );
71
72            &*(&*self.data.get()).as_ptr()
73        }
74
75        /// Obtain a mutable reference to the contained value.
76        #[inline(always)]
77        pub unsafe fn as_mut(&mut self) -> &mut T {
78            assert_eq!(
79                self.loaded.load(Ordering::Relaxed),
80                true,
81                "as_mut for uninitialized cell"
82            );
83
84            &mut *(&mut *self.data.get()).as_mut_ptr()
85        }
86
87        /// Override the `loaded` flag of the cell. This method is a no-op when using the
88        /// unchecked implementation. It may be used when a value is inserted manually,
89        /// for example by assigning to the dereferenced pointer.
90        #[inline]
91        pub fn set_loaded(&self, loaded: bool) {
92            self.loaded.store(loaded, Ordering::Relaxed);
93        }
94
95        /// Drop the contained value in place.
96        #[inline]
97        pub unsafe fn clear(&self) {
98            assert_eq!(
99                self.loaded.swap(false, Ordering::Relaxed),
100                true,
101                "cleared uninitialized cell"
102            );
103
104            ptr::drop_in_place((&mut *self.data.get()).as_mut_ptr())
105        }
106
107        /// Load the contained value.
108        #[inline]
109        pub unsafe fn load(&self) -> T {
110            assert_eq!(
111                self.loaded.swap(false, Ordering::Relaxed),
112                true,
113                "duplicate load"
114            );
115
116            ptr::read(self.data.get()).assume_init()
117        }
118
119        /// Store a new value in an occupied cell.
120        #[inline]
121        pub unsafe fn replace(&self, value: T) -> T {
122            assert_eq!(
123                self.loaded.load(Ordering::Relaxed),
124                true,
125                "replace on empty store"
126            );
127            let result = ptr::read(self.data.get()).assume_init();
128            self.data.get().write(MaybeUninit::new(value));
129            result
130        }
131
132        /// Store a new value in an empty cell.
133        #[inline]
134        pub unsafe fn store(&self, value: T) {
135            assert_eq!(
136                self.loaded.swap(true, Ordering::Relaxed),
137                false,
138                "duplicate store"
139            );
140
141            self.data.get().write(MaybeUninit::new(value))
142        }
143    }
144
145    impl<T> Drop for Maybe<T> {
146        fn drop(&mut self) {
147            assert_eq!(
148                self.loaded.load(Ordering::Relaxed),
149                false,
150                "cell not cleared before drop"
151            );
152        }
153    }
154
155    impl<T> From<T> for Maybe<T> {
156        fn from(value: T) -> Self {
157            Self::new(value)
158        }
159    }
160
161    /// Equivalent to an `UnsafeCell<MaybeUninit<T>>`, this cell may hold uninitialized data.
162    /// Unlike `Maybe<T>` this structure is suited for data types with no `Drop` implementation.
163    #[derive(Debug)]
164    pub struct MaybeCopy<T> {
165        data: UnsafeCell<MaybeUninit<T>>,
166        loaded: AtomicBool,
167    }
168
169    // FIXME require Copy when const_fn is stable
170    impl<T> MaybeCopy<T> {
171        /// Create a new, empty `MaybeCopy<T>`.
172        #[inline]
173        pub const fn empty() -> Self {
174            Self {
175                data: UnsafeCell::new(MaybeUninit::uninit()),
176                loaded: AtomicBool::new(false),
177            }
178        }
179
180        /// Create a new, populated `MaybeCopy<T>`.
181        #[inline]
182        pub const fn new(data: T) -> Self {
183            Self {
184                data: UnsafeCell::new(MaybeUninit::new(data)),
185                loaded: AtomicBool::new(true),
186            }
187        }
188    }
189
190    impl<T: Copy> MaybeCopy<T> {
191        /// Access the contained value as a constant pointer.
192        #[inline]
193        pub unsafe fn as_ptr(&self) -> *const T {
194            assert_eq!(
195                self.loaded.load(Ordering::Relaxed),
196                true,
197                "as_ptr for uninitialized cell"
198            );
199
200            (&*self.data.get()).as_ptr()
201        }
202
203        /// Access the contained value as a mutable pointer.
204        #[inline]
205        pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
206            assert_eq!(
207                self.loaded.load(Ordering::Relaxed),
208                true,
209                "as_mut_ptr for uninitialized cell"
210            );
211
212            (&mut *self.data.get()).as_mut_ptr()
213        }
214
215        /// Obtain a reference to the contained value. This method is `unsafe` because
216        /// the value may not have been initialized.
217        #[inline]
218        pub unsafe fn as_ref(&self) -> &T {
219            assert_eq!(
220                self.loaded.load(Ordering::Relaxed),
221                true,
222                "as_ref for uninitialized cell"
223            );
224
225            &*(&*self.data.get()).as_ptr()
226        }
227
228        /// Obtain a mutable reference to the contained value.
229        #[inline(always)]
230        pub unsafe fn as_mut(&mut self) -> &mut T {
231            assert_eq!(
232                self.loaded.load(Ordering::Relaxed),
233                true,
234                "as_mut for uninitialized cell"
235            );
236
237            &mut *(&mut *self.data.get()).as_mut_ptr()
238        }
239
240        /// Override the `loaded` flag of the cell. This method is a no-op when using the
241        /// unchecked implementation. It may be used when a value is inserted manually,
242        /// for example by assigning to the dereferenced pointer.
243        #[inline]
244        pub fn set_loaded(&self, loaded: bool) {
245            self.loaded.store(loaded, Ordering::Relaxed);
246        }
247
248        /// Load the contained value.
249        #[inline]
250        pub unsafe fn load(&self) -> T {
251            assert_eq!(
252                self.loaded.load(Ordering::Relaxed),
253                true,
254                "load of uninitialized cell"
255            );
256
257            ptr::read(self.data.get()).assume_init()
258        }
259
260        /// Store a new value in an occupied cell.
261        #[inline]
262        pub unsafe fn replace(&self, value: T) -> T {
263            assert_eq!(
264                self.loaded.load(Ordering::Relaxed),
265                true,
266                "replace on empty store"
267            );
268            let result = ptr::read(self.data.get()).assume_init();
269            self.data.get().write(MaybeUninit::new(value));
270            result
271        }
272
273        /// Store a new value in the cell.
274        #[inline]
275        pub unsafe fn store(&self, value: T) {
276            self.loaded.store(true, Ordering::Relaxed);
277
278            self.data.get().write(MaybeUninit::new(value))
279        }
280    }
281
282    impl<T: Copy> From<T> for MaybeCopy<T> {
283        fn from(value: T) -> Self {
284            Self::new(value)
285        }
286    }
287}
288
289/// Data structure variants without run-time checks
290pub mod unchecked {
291    use core::{cell::UnsafeCell, mem::MaybeUninit, ptr};
292
293    /// Equivalent to an `UnsafeCell<MaybeUninit<T>>`, this cell may hold uninitialized data.
294    #[derive(Debug)]
295    #[repr(transparent)]
296    pub struct Maybe<T> {
297        data: UnsafeCell<MaybeUninit<T>>,
298    }
299
300    impl<T> Maybe<T> {
301        /// Create a new, empty `Maybe<T>`.
302        #[inline(always)]
303        pub const fn empty() -> Self {
304            Self {
305                data: UnsafeCell::new(MaybeUninit::uninit()),
306            }
307        }
308
309        /// Create a new, populated `Maybe<T>`.
310        #[inline(always)]
311        pub const fn new(data: T) -> Self {
312            Self {
313                data: UnsafeCell::new(MaybeUninit::new(data)),
314            }
315        }
316
317        /// Access the contained value as a constant pointer.
318        #[inline(always)]
319        pub unsafe fn as_ptr(&self) -> *const T {
320            (&*self.data.get()).as_ptr()
321        }
322
323        /// Access the contained value as a mutable pointer.
324        #[inline(always)]
325        pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
326            (&mut *self.data.get()).as_mut_ptr()
327        }
328
329        /// Obtain a reference to the contained value. This method is `unsafe` because
330        /// the value may not have been initialized.
331        #[inline(always)]
332        pub unsafe fn as_ref(&self) -> &T {
333            &*(&*self.data.get()).as_ptr()
334        }
335
336        /// Obtain a mutable reference to the contained value.
337        #[inline(always)]
338        pub unsafe fn as_mut(&mut self) -> &mut T {
339            &mut *(&mut *self.data.get()).as_mut_ptr()
340        }
341
342        /// Override the `loaded` flag of the cell. This method is a no-op when using the
343        /// unchecked implementation. It may be used when a value is inserted manually,
344        /// for example by assigning to the dereferenced pointer.
345        #[inline]
346        pub fn set_loaded(&self, _loaded: bool) {}
347
348        /// Drop the contained value in place.
349        #[inline(always)]
350        pub unsafe fn clear(&self) {
351            ptr::drop_in_place((&mut *self.data.get()).as_mut_ptr())
352        }
353
354        /// Load the contained value.
355        #[inline(always)]
356        pub unsafe fn load(&self) -> T {
357            ptr::read(self.data.get()).assume_init()
358        }
359
360        /// Store a new value in an occupied cell.
361        #[inline(always)]
362        pub unsafe fn replace(&self, value: T) -> T {
363            let result = self.load();
364            self.data.get().write(MaybeUninit::new(value));
365            result
366        }
367
368        /// Store a new value in an empty cell.
369        #[inline(always)]
370        pub unsafe fn store(&self, value: T) {
371            self.data.get().write(MaybeUninit::new(value))
372        }
373    }
374
375    impl<T> From<T> for Maybe<T> {
376        fn from(value: T) -> Self {
377            Self::new(value)
378        }
379    }
380
381    /// Equivalent to an `UnsafeCell<MaybeUninit<T>>`, this cell may hold uninitialized data.
382    /// Unlike `Maybe<T>` this structure is suited for data types with no `Drop` implementation.
383    #[derive(Debug)]
384    #[repr(transparent)]
385    pub struct MaybeCopy<T> {
386        data: UnsafeCell<MaybeUninit<T>>,
387    }
388
389    // FIXME require Copy when const_fn is stable
390    impl<T> MaybeCopy<T> {
391        /// Create a new, empty `MaybeCopy<T>`.
392        #[inline]
393        pub const fn empty() -> Self {
394            Self {
395                data: UnsafeCell::new(MaybeUninit::uninit()),
396            }
397        }
398
399        /// Create a new, populated `MaybeCopy<T>`.
400        #[inline]
401        pub const fn new(data: T) -> Self {
402            Self {
403                data: UnsafeCell::new(MaybeUninit::new(data)),
404            }
405        }
406    }
407
408    impl<T: Copy> MaybeCopy<T> {
409        /// Access the contained value as a constant pointer.
410        #[inline(always)]
411        pub unsafe fn as_ptr(&self) -> *const T {
412            (&*self.data.get()).as_ptr()
413        }
414
415        /// Access the contained value as a mutable pointer.
416        #[inline(always)]
417        pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
418            (&mut *self.data.get()).as_mut_ptr()
419        }
420
421        /// Obtain a reference to the contained value. This method is `unsafe` because
422        /// the value may not have been initialized.
423        #[inline(always)]
424        pub unsafe fn as_ref(&self) -> &T {
425            &*(&*self.data.get()).as_ptr()
426        }
427
428        /// Obtain a mutable reference to the contained value.
429        #[inline(always)]
430        pub unsafe fn as_mut(&mut self) -> &mut T {
431            &mut *(&mut *self.data.get()).as_mut_ptr()
432        }
433
434        /// Override the `loaded` flag of the cell. This method is a no-op when using the
435        /// unchecked implementation. It may be used when a value is inserted manually,
436        /// for example by assigning to the dereferenced pointer.
437        #[inline]
438        pub fn set_loaded(&self, _loaded: bool) {}
439
440        /// Load the contained value.
441        #[inline(always)]
442        pub unsafe fn load(&self) -> T {
443            ptr::read(self.data.get()).assume_init()
444        }
445
446        /// Store a new value in an occupied cell.
447        pub unsafe fn replace(&self, value: T) -> T {
448            let result = self.load();
449            self.data.get().write(MaybeUninit::new(value));
450            result
451        }
452
453        /// Store a new value in an empty cell.
454        #[inline(always)]
455        pub unsafe fn store(&self, value: T) {
456            self.data.get().write(MaybeUninit::new(value))
457        }
458    }
459
460    impl<T: Copy> From<T> for MaybeCopy<T> {
461        fn from(value: T) -> Self {
462            Self::new(value)
463        }
464    }
465}
466
467#[cfg(test)]
468mod tests {
469    use super::{checked, unchecked};
470
471    #[test]
472    fn checked_init_read_write() {
473        unsafe {
474            let cell = checked::Maybe::new(1);
475            assert_eq!(cell.load(), 1);
476            cell.store(2);
477            assert_eq!(cell.load(), 2);
478        }
479    }
480
481    #[test]
482    fn unchecked_init_read_write() {
483        unsafe {
484            let cell = unchecked::Maybe::new(1);
485            assert_eq!(cell.load(), 1);
486            cell.store(2);
487            assert_eq!(cell.load(), 2);
488        }
489    }
490}