tux_owned_alloc/
maybe_uninit.rs

1use super::{OwnedAlloc, UninitAlloc};
2use std::fmt;
3
4/// Pointer to memory allocaation that might be either initialized or
5/// uninitialized. For the drop checker, the type acts as if it contains a `T`
6/// due to usage of `PhantomData<T>`.
7pub enum MaybeUninitAlloc<T>
8where
9    T: ?Sized,
10{
11    /// Initialized allocation.
12    Init(OwnedAlloc<T>),
13
14    /// Uninitialized allocation.
15    Uninit(UninitAlloc<T>),
16}
17
18impl<T> MaybeUninitAlloc<T> {
19    /// If the allocation was initialized, this is a no-op. If it wasn't, the
20    /// passed function is called and its return value is used to initialize the
21    /// memory. In both cases, an allocation considered initialized is returned.
22    pub fn or_init<F>(self, init: F) -> OwnedAlloc<T>
23    where
24        F: FnOnce() -> T,
25    {
26        match self {
27            MaybeUninitAlloc::Init(ptr) => ptr,
28            MaybeUninitAlloc::Uninit(ptr) => ptr.init(init()),
29        }
30    }
31}
32
33impl<T> MaybeUninitAlloc<T>
34where
35    T: ?Sized,
36{
37    /// If the allocation was initialized, this is a no-op. If it wasn't, the
38    /// passed function is called with a mutable reference to the uninitialized
39    /// memory and the function is expected to initialize the memory. In both
40    /// cases, an allocation considered initialized is returned.
41    ///
42    /// # Safety
43    /// This function is `unsafe` because the passed function might not
44    /// initialize the memory correctly.
45    pub unsafe fn or_init_in_place<F>(self, init: F) -> OwnedAlloc<T>
46    where
47        F: FnOnce(&mut T),
48    {
49        match self {
50            MaybeUninitAlloc::Init(ptr) => ptr,
51            MaybeUninitAlloc::Uninit(ptr) => ptr.init_in_place(init),
52        }
53    }
54
55    /// Tests if the allocation is initialized.
56    pub fn is_initialized(&self) -> bool {
57        match self {
58            MaybeUninitAlloc::Init(_) => true,
59            MaybeUninitAlloc::Uninit(_) => false,
60        }
61    }
62
63    /// Tests if the allocation is uninitialized.
64    pub fn is_uninitialized(&self) -> bool {
65        match self {
66            MaybeUninitAlloc::Init(_) => true,
67            MaybeUninitAlloc::Uninit(_) => false,
68        }
69    }
70
71    /// If the memory is initialized, this function drops its content. In any
72    /// case, the allocation now with uninitialized content is returned.
73    pub fn drop_in_place(self) -> UninitAlloc<T> {
74        match self {
75            MaybeUninitAlloc::Init(ptr) => ptr.drop_in_place(),
76            MaybeUninitAlloc::Uninit(ptr) => ptr,
77        }
78    }
79
80    /// Encodes this type as a `Result` with an `OwnedAlloc` as `Ok`.
81    pub fn init_as_ok(self) -> Result<OwnedAlloc<T>, UninitAlloc<T>> {
82        match self {
83            MaybeUninitAlloc::Init(ptr) => Ok(ptr),
84            MaybeUninitAlloc::Uninit(ptr) => Err(ptr),
85        }
86    }
87
88    /// Encodes this type as a `Result` with an `UninitAlloc` as `Ok`.
89    pub fn uninit_as_ok(self) -> Result<UninitAlloc<T>, OwnedAlloc<T>> {
90        match self {
91            MaybeUninitAlloc::Init(ptr) => Err(ptr),
92            MaybeUninitAlloc::Uninit(ptr) => Ok(ptr),
93        }
94    }
95
96    /// If the memory is uninitialized, `None` is returned. If it is
97    /// initialized, the passed function is called with a mutable reference to
98    /// the allocation, and its return value is wrapped into a `Some`.
99    pub fn modify<F, A>(&mut self, visit: F) -> Option<A>
100    where
101        F: FnOnce(&mut T) -> A,
102    {
103        match self {
104            MaybeUninitAlloc::Init(ptr) => Some(visit(&mut **ptr)),
105            MaybeUninitAlloc::Uninit(_) => None,
106        }
107    }
108}
109
110impl<T> From<T> for MaybeUninitAlloc<T> {
111    fn from(val: T) -> Self {
112        MaybeUninitAlloc::Init(OwnedAlloc::new(val))
113    }
114}
115
116impl<T> From<OwnedAlloc<T>> for MaybeUninitAlloc<T>
117where
118    T: ?Sized,
119{
120    fn from(alloc: OwnedAlloc<T>) -> Self {
121        MaybeUninitAlloc::Init(alloc)
122    }
123}
124
125impl<T> From<UninitAlloc<T>> for MaybeUninitAlloc<T>
126where
127    T: ?Sized,
128{
129    fn from(alloc: UninitAlloc<T>) -> Self {
130        MaybeUninitAlloc::Uninit(alloc)
131    }
132}
133
134impl<T> fmt::Debug for MaybeUninitAlloc<T>
135where
136    T: ?Sized,
137{
138    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
139        match self {
140            MaybeUninitAlloc::Init(ptr) => write!(fmtr, "Init({:?})", ptr),
141            MaybeUninitAlloc::Uninit(ptr) => write!(fmtr, "Uninit({:?})", ptr),
142        }
143    }
144}
145
146#[cfg(test)]
147mod test {
148    use super::{super::UninitAlloc, MaybeUninitAlloc};
149
150    #[test]
151    fn or_init_is_noop_if_initialized() {
152        let init = MaybeUninitAlloc::from(90);
153
154        assert_eq!(*init.or_init(|| 50), 90);
155    }
156
157    #[test]
158    fn or_init_calls_if_uninit() {
159        let init = MaybeUninitAlloc::from(UninitAlloc::new());
160
161        assert_eq!(*init.or_init(|| 50), 50);
162    }
163
164    #[test]
165    fn modifies() {
166        let mut init = MaybeUninitAlloc::from(20);
167
168        assert!(init.modify(|addr| *addr = 2).is_some());
169        assert_eq!(*init.init_as_ok().unwrap(), 2);
170    }
171}