| 1 | // Copyright 2019 Developers of the Rand project. |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| 6 | // option. This file may not be copied, modified, or distributed |
| 7 | // except according to those terms. |
| 8 | #![allow (dead_code)] |
| 9 | use core::{ |
| 10 | mem::MaybeUninit, |
| 11 | ptr, |
| 12 | sync::atomic::{AtomicUsize, Ordering::Relaxed}, |
| 13 | }; |
| 14 | |
| 15 | // This structure represents a lazily initialized static usize value. Useful |
| 16 | // when it is preferable to just rerun initialization instead of locking. |
| 17 | // Both unsync_init and sync_init will invoke an init() function until it |
| 18 | // succeeds, then return the cached value for future calls. |
| 19 | // |
| 20 | // Both methods support init() "failing". If the init() method returns UNINIT, |
| 21 | // that value will be returned as normal, but will not be cached. |
| 22 | // |
| 23 | // Users should only depend on the _value_ returned by init() functions. |
| 24 | // Specifically, for the following init() function: |
| 25 | // fn init() -> usize { |
| 26 | // a(); |
| 27 | // let v = b(); |
| 28 | // c(); |
| 29 | // v |
| 30 | // } |
| 31 | // the effects of c() or writes to shared memory will not necessarily be |
| 32 | // observed and additional synchronization methods with be needed. |
| 33 | pub struct LazyUsize(AtomicUsize); |
| 34 | |
| 35 | impl LazyUsize { |
| 36 | pub const fn new() -> Self { |
| 37 | Self(AtomicUsize::new(Self::UNINIT)) |
| 38 | } |
| 39 | |
| 40 | // The initialization is not completed. |
| 41 | pub const UNINIT: usize = usize::max_value(); |
| 42 | |
| 43 | // Runs the init() function at least once, returning the value of some run |
| 44 | // of init(). Multiple callers can run their init() functions in parallel. |
| 45 | // init() should always return the same value, if it succeeds. |
| 46 | pub fn unsync_init(&self, init: impl FnOnce() -> usize) -> usize { |
| 47 | // Relaxed ordering is fine, as we only have a single atomic variable. |
| 48 | let mut val = self.0.load(Relaxed); |
| 49 | if val == Self::UNINIT { |
| 50 | val = init(); |
| 51 | self.0.store(val, Relaxed); |
| 52 | } |
| 53 | val |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // Identical to LazyUsize except with bool instead of usize. |
| 58 | pub struct LazyBool(LazyUsize); |
| 59 | |
| 60 | impl LazyBool { |
| 61 | pub const fn new() -> Self { |
| 62 | Self(LazyUsize::new()) |
| 63 | } |
| 64 | |
| 65 | pub fn unsync_init(&self, init: impl FnOnce() -> bool) -> bool { |
| 66 | self.0.unsync_init(|| init() as usize) != 0 |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | /// Polyfill for `maybe_uninit_slice` feature's |
| 71 | /// `MaybeUninit::slice_assume_init_mut`. Every element of `slice` must have |
| 72 | /// been initialized. |
| 73 | #[inline (always)] |
| 74 | pub unsafe fn slice_assume_init_mut<T>(slice: &mut [MaybeUninit<T>]) -> &mut [T] { |
| 75 | // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`. |
| 76 | &mut *(slice as *mut [MaybeUninit<T>] as *mut [T]) |
| 77 | } |
| 78 | |
| 79 | #[inline ] |
| 80 | pub fn uninit_slice_fill_zero(slice: &mut [MaybeUninit<u8>]) -> &mut [u8] { |
| 81 | unsafe { ptr::write_bytes(slice.as_mut_ptr(), 0, slice.len()) }; |
| 82 | unsafe { slice_assume_init_mut(slice) } |
| 83 | } |
| 84 | |
| 85 | #[inline (always)] |
| 86 | pub fn slice_as_uninit<T>(slice: &[T]) -> &[MaybeUninit<T>] { |
| 87 | // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`. |
| 88 | // There is no risk of writing a `MaybeUninit<T>` into the result since |
| 89 | // the result isn't mutable. |
| 90 | unsafe { &*(slice as *const [T] as *const [MaybeUninit<T>]) } |
| 91 | } |
| 92 | |
| 93 | /// View an mutable initialized array as potentially-uninitialized. |
| 94 | /// |
| 95 | /// This is unsafe because it allows assigning uninitialized values into |
| 96 | /// `slice`, which would be undefined behavior. |
| 97 | #[inline (always)] |
| 98 | pub unsafe fn slice_as_uninit_mut<T>(slice: &mut [T]) -> &mut [MaybeUninit<T>] { |
| 99 | // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T`. |
| 100 | &mut *(slice as *mut [T] as *mut [MaybeUninit<T>]) |
| 101 | } |
| 102 | |