ab_io_type/
maybe_data.rs

1use crate::trivial_type::TrivialType;
2use crate::{DerefWrapper, IoType, IoTypeOptional};
3use core::mem::MaybeUninit;
4use core::ops::{Deref, DerefMut};
5use core::ptr::NonNull;
6
7/// Wrapper type for `Data` that may or may not be filled with contents.
8///
9/// This is somewhat similar to [`VariableBytes`](crate::variable_bytes::VariableBytes), but instead
10/// of variable size, data structure allows either having it or not having the contents, which is a
11/// simpler and more convenient API that is also sufficient in many cases.
12#[derive(Debug)]
13pub struct MaybeData<Data>
14where
15    Data: TrivialType,
16{
17    data: NonNull<Data>,
18    size: NonNull<u32>,
19}
20
21// SAFETY: Low-level (effectively internal) implementation that upholds safety requirements
22unsafe impl<Data> IoType for MaybeData<Data>
23where
24    Data: TrivialType,
25{
26    const METADATA: &[u8] = Data::METADATA;
27
28    type PointerType = Data;
29
30    #[inline(always)]
31    fn size(&self) -> u32 {
32        // SAFETY: guaranteed to be initialized by constructors
33        unsafe { self.size.read() }
34    }
35
36    #[inline(always)]
37    fn capacity(&self) -> u32 {
38        Data::SIZE
39    }
40
41    #[inline(always)]
42    #[track_caller]
43    unsafe fn set_size(&mut self, size: u32) {
44        debug_assert!(
45            size == 0 || size == Data::SIZE,
46            "`set_size` called with invalid input {size} (self size {})",
47            self.size()
48        );
49
50        // SAFETY: guaranteed to be initialized by constructors
51        unsafe {
52            self.size.write(size);
53        }
54    }
55
56    #[inline(always)]
57    #[track_caller]
58    unsafe fn from_ptr<'a>(
59        data: &'a NonNull<Self::PointerType>,
60        size: &'a u32,
61        capacity: u32,
62    ) -> impl Deref<Target = Self> + 'a {
63        debug_assert!(data.is_aligned(), "Misaligned pointer");
64        debug_assert!(
65            *size == 0 || *size <= capacity,
66            "Invalid size {size} for capacity {capacity}"
67        );
68        // Read-only instance can have 0 capacity if empty
69        debug_assert!(
70            capacity == 0 || capacity >= Data::SIZE,
71            "Invalid capacity {capacity} for size {}",
72            Data::SIZE
73        );
74
75        let size = NonNull::from_ref(size);
76
77        DerefWrapper(MaybeData { data: *data, size })
78    }
79
80    #[inline(always)]
81    #[track_caller]
82    unsafe fn from_mut_ptr<'a>(
83        data: &'a mut NonNull<Self::PointerType>,
84        size: &'a mut u32,
85        capacity: u32,
86    ) -> impl DerefMut<Target = Self> + 'a {
87        debug_assert!(data.is_aligned(), "Misaligned pointer");
88        debug_assert!(
89            *size == 0 || *size <= capacity,
90            "Invalid size {size} for capacity {capacity}"
91        );
92        debug_assert!(
93            capacity >= Data::SIZE,
94            "Invalid capacity {capacity} for size {}",
95            Data::SIZE
96        );
97
98        DerefWrapper(MaybeData {
99            data: *data,
100            size: NonNull::from_mut(size),
101        })
102    }
103
104    #[inline(always)]
105    unsafe fn as_ptr(&self) -> impl Deref<Target = NonNull<Self::PointerType>> {
106        &self.data
107    }
108
109    #[inline(always)]
110    unsafe fn as_mut_ptr(&mut self) -> impl DerefMut<Target = NonNull<Self::PointerType>> {
111        &mut self.data
112    }
113}
114
115impl<Data> IoTypeOptional for MaybeData<Data> where Data: TrivialType {}
116
117impl<Data> MaybeData<Data>
118where
119    Data: TrivialType,
120{
121    /// Create a new shared instance from provided data reference.
122    //
123    // `impl Deref` is used to tie lifetime of returned value to inputs, but still treat it as a
124    // shared reference for most practical purposes.
125    pub const fn from_ref(data: Option<&'_ Data>) -> impl Deref<Target = Self> + '_ {
126        let (data, size) = if let Some(data) = data {
127            (NonNull::from_ref(data), &Data::SIZE)
128        } else {
129            (NonNull::dangling(), &0)
130        };
131
132        DerefWrapper(Self {
133            data,
134            size: NonNull::from_ref(size),
135        })
136    }
137
138    /// Create a new exclusive instance from provided data reference.
139    ///
140    /// `size` can be either `0` or `Data::SIZE`, indicating that value is missing or present
141    /// accordingly.
142    ///
143    /// # Panics
144    /// Panics if `size != 0 && size != Data::SIZE`
145    //
146    // `impl DerefMut` is used to tie lifetime of returned value to inputs, but still treat it as an
147    // exclusive reference for most practical purposes.
148    #[track_caller]
149    pub fn from_mut<'a>(
150        buffer: &'a mut Data,
151        size: &'a mut u32,
152    ) -> impl DerefMut<Target = Self> + 'a {
153        debug_assert!(
154            *size == 0 || *size == Data::SIZE,
155            "Invalid size {size} (self size {})",
156            Data::SIZE
157        );
158
159        DerefWrapper(Self {
160            data: NonNull::from_mut(buffer),
161            size: NonNull::from_mut(size),
162        })
163    }
164
165    /// Create a new shared instance from provided memory buffer.
166    ///
167    /// `size` must be `0`.
168    ///
169    /// # Panics
170    /// Panics if `size != 0`
171    //
172    // `impl Deref` is used to tie lifetime of returned value to inputs, but still treat it as a
173    // shared reference for most practical purposes.
174    // TODO: Change `usize` to `u32` once stabilized `generic_const_exprs` feature allows us to do
175    //  `CAPACITY as usize`
176    #[track_caller]
177    pub fn from_uninit<'a>(
178        uninit: &'a mut MaybeUninit<Data>,
179        size: &'a mut u32,
180    ) -> impl DerefMut<Target = Self> + 'a {
181        debug_assert_eq!(*size, 0, "Invalid size");
182
183        DerefWrapper(Self {
184            data: NonNull::from_mut(uninit).cast::<Data>(),
185            size: NonNull::from_mut(size),
186        })
187    }
188
189    /// Try to get access to initialized `Data`, returns `None` if not initialized
190    #[inline(always)]
191    pub const fn get(&self) -> Option<&Data> {
192        // SAFETY: guaranteed to be initialized by constructors
193        if unsafe { self.size.read() } == Data::SIZE {
194            // SAFETY: initialized
195            Some(unsafe { self.data.as_ref() })
196        } else {
197            None
198        }
199    }
200
201    /// Try to get exclusive access to initialized `Data`, returns `None` if not initialized
202    #[inline(always)]
203    pub fn get_mut(&mut self) -> Option<&mut Data> {
204        // SAFETY: guaranteed to be initialized by constructors
205        if unsafe { self.size.read() } == Data::SIZE {
206            // SAFETY: initialized
207            Some(unsafe { self.data.as_mut() })
208        } else {
209            None
210        }
211    }
212
213    /// Initialize by inserting `Data` by value or replace existing value and return reference to it
214    #[inline(always)]
215    pub fn replace(&mut self, data: Data) -> &mut Data {
216        // SAFETY: guaranteed to be initialized by constructors
217        unsafe {
218            self.size.write(Data::SIZE);
219        }
220        // SAFETY: constructor guarantees that memory is aligned
221        unsafe {
222            self.data.write(data);
223            self.data.as_mut()
224        }
225    }
226
227    /// Remove `Data` inside and turn instance back into uninitialized
228    #[inline(always)]
229    pub fn remove(&mut self) {
230        // SAFETY: guaranteed to be initialized by constructors
231        unsafe {
232            self.size.write(0);
233        }
234    }
235
236    /// Get exclusive access to initialized `Data`, running provided initialization function if
237    /// necessary
238    #[inline(always)]
239    pub fn get_mut_or_init_with<Init>(&mut self, init: Init) -> &mut Data
240    where
241        Init: FnOnce(&mut MaybeUninit<Data>) -> &mut Data,
242    {
243        // SAFETY: guaranteed to be initialized by constructors
244        if unsafe { self.size.read() } == Data::SIZE {
245            // SAFETY: initialized
246            unsafe { self.data.as_mut() }
247        } else {
248            // SAFETY: constructor guarantees that memory is aligned
249            let data = init(unsafe { self.data.as_uninit_mut() });
250            // SAFETY: guaranteed to be initialized by constructors
251            unsafe {
252                self.size.write(Data::SIZE);
253            }
254            data
255        }
256    }
257
258    /// Assume value is initialized
259    ///
260    /// # Safety
261    /// Caller must ensure `Data` is actually properly initialized
262    #[inline(always)]
263    pub unsafe fn assume_init(&mut self) -> &mut Data {
264        // SAFETY: guaranteed to be initialized by constructors
265        unsafe {
266            self.size.write(Data::SIZE);
267        }
268        // SAFETY: guaranteed to be initialized by caller, the rest of guarantees are provided by
269        // constructors
270        unsafe { self.data.as_mut() }
271    }
272}
273
274impl<Data> MaybeData<Data>
275where
276    Data: TrivialType + Default,
277{
278    /// Get exclusive access to initialized `Data`, initializing with default value if necessary
279    #[inline(always)]
280    pub fn get_mut_or_default(&mut self) -> &mut Data {
281        self.get_mut_or_init_with(|data| data.write(Data::default()))
282    }
283}