Skip to main content

ab_io_type/
maybe_data.rs

1#[cfg(test)]
2mod tests;
3
4use crate::metadata::{IoTypeMetadataKind, MAX_METADATA_CAPACITY, concat_metadata_sources};
5use crate::trivial_type::{NON_ZERO_SIZED, TrivialType};
6use crate::{DerefWrapper, IoType, IoTypeOptional};
7use core::marker::PhantomData;
8use core::mem::MaybeUninit;
9use core::ops::{Deref, DerefMut};
10use core::ptr::NonNull;
11use core::{ptr, slice};
12
13/// Wrapper type for `Data` that may or may not be filled with contents.
14///
15/// This is somewhat similar to [`VariableElements`](crate::variable_elements::VariableElements),
16/// but instead of a variable number of elements, data structure allows either having it or not
17/// having the contents, which is a simpler and more appropriate API in many cases.
18///
19/// It is a view of memory that is borrowed for `'a`.
20#[derive(Debug)]
21pub struct MaybeData<'a, Data>
22where
23    Data: TrivialType,
24{
25    data: NonNull<Data>,
26    size: NonNull<u32>,
27    memory: PhantomData<&'a ()>,
28    // TODO: Technically redundant with the where-clause, which is not enforced in generic code
29    //  due to https://github.com/rust-lang/rust/issues/164019, remove once fixed. Every instance
30    //  evaluates the check through this field. It is initialized in `const` blocks, which are
31    //  evaluated even if optimizations remove the field.
32    _supported: [(); NON_ZERO_SIZED::<Data>],
33}
34
35// SAFETY: Low-level (effectively internal) implementation that upholds safety requirements
36unsafe impl<Data> IoType for MaybeData<'_, Data>
37where
38    Data: TrivialType,
39    [(); NON_ZERO_SIZED::<Data>]:,
40{
41    const METADATA: &[u8] = {
42        const fn metadata(inner_metadata: &[u8]) -> ([u8; MAX_METADATA_CAPACITY], usize) {
43            concat_metadata_sources(&[&[IoTypeMetadataKind::MaybeData as u8], inner_metadata])
44        }
45
46        // The reference extends the lifetime of metadata scratch to that of the constant
47        let (metadata_scratch, metadata_size) = &metadata(Data::METADATA);
48        metadata_scratch.split_at(*metadata_size).0
49    };
50
51    type PointerType = Data;
52
53    type WithLifetime<'b> = MaybeData<'b, Data>;
54
55    type Reborrowed<'b>
56        = MaybeData<'b, Data>
57    where
58        Self: 'b;
59
60    #[inline(always)]
61    fn size(&self) -> u32 {
62        // SAFETY: guaranteed to be initialized by constructors
63        unsafe { self.size.read() }
64    }
65
66    #[inline(always)]
67    fn capacity(&self) -> u32 {
68        Data::SIZE
69    }
70
71    #[inline(always)]
72    #[track_caller]
73    unsafe fn set_size(&mut self, size: u32) {
74        debug_assert!(
75            size == 0 || size == Data::SIZE,
76            "`set_size` called with invalid input {size} (self size {})",
77            self.size()
78        );
79
80        // SAFETY: guaranteed to be initialized by constructors
81        unsafe {
82            self.size.write(size);
83        }
84    }
85
86    #[inline(always)]
87    #[track_caller]
88    unsafe fn from_ptr<'a>(
89        ptr: &'a NonNull<Self::PointerType>,
90        size: &'a u32,
91        capacity: u32,
92    ) -> impl Deref<Target = Self::WithLifetime<'a>> + 'a {
93        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
94        debug_assert!(
95            *size == 0 || *size <= capacity,
96            "Invalid size {size} for capacity {capacity}"
97        );
98        // Read-only instance can have 0 capacity if empty
99        debug_assert!(
100            capacity == 0 || capacity >= Data::SIZE,
101            "Invalid capacity {capacity} for size {}",
102            Data::SIZE
103        );
104
105        let size = NonNull::from_ref(size);
106
107        DerefWrapper(MaybeData {
108            data: *ptr,
109            size,
110            memory: PhantomData,
111            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
112        })
113    }
114
115    #[inline(always)]
116    #[track_caller]
117    unsafe fn from_mut_ptr<'a>(
118        ptr: &'a mut NonNull<Self::PointerType>,
119        size: &'a mut u32,
120        capacity: u32,
121    ) -> impl DerefMut<Target = Self::WithLifetime<'a>> + 'a {
122        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
123        debug_assert!(
124            *size == 0 || *size <= capacity,
125            "Invalid size {size} for capacity {capacity}"
126        );
127        debug_assert!(
128            capacity >= Data::SIZE,
129            "Invalid capacity {capacity} for size {}",
130            Data::SIZE
131        );
132
133        DerefWrapper(MaybeData {
134            data: *ptr,
135            size: NonNull::from_mut(size),
136            memory: PhantomData,
137            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
138        })
139    }
140
141    #[inline(always)]
142    #[track_caller]
143    unsafe fn try_from_ptr<'a>(
144        ptr: &'a NonNull<Self::PointerType>,
145        size: &'a u32,
146        capacity: u32,
147    ) -> Option<impl Deref<Target = Self::WithLifetime<'a>> + 'a> {
148        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
149        debug_assert!(
150            *size == 0 || (*size == Data::SIZE && *size <= capacity),
151            "Invalid size {size} for capacity {capacity}"
152        );
153        // Read-only instance can have 0 capacity if empty
154        debug_assert!(
155            capacity == 0 || capacity >= Data::SIZE,
156            "Invalid capacity {capacity} for size {}",
157            Data::SIZE
158        );
159        if *size == Data::SIZE {
160            // SAFETY: `ptr` is valid for reads of `size` bytes, which is the size of `Data`,
161            // initialized and not written to during the call (function contract)
162            let bytes =
163                unsafe { slice::from_raw_parts(ptr.as_ptr().cast::<u8>(), size_of::<Data>()) };
164            // SAFETY: `ptr` is aligned for `Data` (function contract)
165            unsafe { Data::parse_step::<true>(bytes) }?;
166        }
167
168        // SAFETY: `size` and `capacity` are valid, `ptr` is aligned and the memory is not written
169        // to while the returned value exists (function contract), with size `0` nothing is read,
170        // otherwise the memory holds a valid value of `Data` as checked above
171        Some(unsafe { Self::from_ptr(ptr, size, capacity) })
172    }
173
174    #[inline(always)]
175    #[track_caller]
176    unsafe fn try_from_mut_ptr<'a>(
177        ptr: &'a mut NonNull<Self::PointerType>,
178        size: &'a mut u32,
179        capacity: u32,
180    ) -> Option<impl DerefMut<Target = Self::WithLifetime<'a>> + 'a> {
181        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
182        debug_assert!(
183            *size == 0 || *size == Data::SIZE,
184            "Invalid size {size} for data size {}",
185            Data::SIZE
186        );
187        // Data can be written even if the size is `0`, so the capacity must fit it
188        debug_assert!(
189            capacity >= Data::SIZE,
190            "Invalid capacity {capacity} for size {}",
191            Data::SIZE
192        );
193        if *size == Data::SIZE {
194            // SAFETY: `ptr` is valid for reads of `size` bytes, which is the size of `Data`,
195            // initialized and not accessed otherwise during the call (function contract), the
196            // slice is not used after parsing
197            let bytes =
198                unsafe { slice::from_raw_parts(ptr.as_ptr().cast::<u8>(), size_of::<Data>()) };
199            // SAFETY: `ptr` is aligned for `Data` (function contract)
200            unsafe { Data::parse_step::<true>(bytes) }?;
201        }
202
203        // SAFETY: `size` and `capacity` are valid, `ptr` is aligned, valid for writes of `capacity`
204        // bytes, the memory and `size` are not accessed otherwise for as long as
205        // `Self::from_mut_ptr()` requires (function contract), with size `0` nothing is read,
206        // otherwise the memory holds a valid value of `Data` as checked above
207        Some(unsafe { Self::from_mut_ptr(ptr, size, capacity) })
208    }
209
210    #[inline(always)]
211    fn as_ptr(&self) -> NonNull<Self::PointerType> {
212        self.data
213    }
214
215    #[inline(always)]
216    fn as_mut_ptr(&mut self) -> NonNull<Self::PointerType> {
217        self.data
218    }
219
220    #[inline(always)]
221    fn reborrow_mut(&mut self) -> Self::Reborrowed<'_> {
222        MaybeData {
223            data: self.data,
224            size: self.size,
225            memory: PhantomData,
226            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
227        }
228    }
229}
230
231// SAFETY: Size `0` means data is missing, data is only accessed when the size is the size of data
232unsafe impl<Data> IoTypeOptional for MaybeData<'_, Data>
233where
234    Data: TrivialType,
235    [(); NON_ZERO_SIZED::<Data>]:,
236{
237}
238
239// Absence of data is represented by zero size, so `Data` itself must not be zero-sized, which the
240// `IoType` implementation checks
241impl<'a, Data> MaybeData<'a, Data>
242where
243    Data: TrivialType,
244    Self: IoType,
245{
246    /// Create a new shared instance from provided data reference.
247    //
248    // `impl Deref` is returned instead of `Self`, since methods that take `&mut self` would
249    // otherwise write into shared memory.
250    pub const fn from_ref(data: Option<&'a Data>) -> impl Deref<Target = Self> + 'a {
251        let (data, size) = if let Some(data) = data {
252            (NonNull::from_ref(data), &Data::SIZE)
253        } else {
254            (NonNull::dangling(), &0)
255        };
256
257        DerefWrapper(Self {
258            data,
259            size: NonNull::from_ref(size),
260            memory: PhantomData,
261            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
262        })
263    }
264
265    /// Create a new exclusive instance from provided data reference.
266    ///
267    /// `size` can be either `0` or `Data::SIZE`, indicating that value is missing or present
268    /// accordingly.
269    ///
270    /// Returns `None` if `size` is neither `0` nor `Data::SIZE`.
271    pub fn from_mut(buffer: &'a mut Data, size: &'a mut u32) -> Option<Self> {
272        if !(*size == 0 || *size == Data::SIZE) {
273            return None;
274        }
275
276        Some(Self {
277            data: NonNull::from_mut(buffer),
278            size: NonNull::from_mut(size),
279            memory: PhantomData,
280            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
281        })
282    }
283
284    /// Create a new exclusive instance from provided uninitialized memory.
285    ///
286    /// `size` must be `0` since data is not initialized yet.
287    ///
288    /// Returns `None` if `size != 0`.
289    pub fn from_uninit(uninit: &'a mut MaybeUninit<Data>, size: &'a mut u32) -> Option<Self> {
290        if *size != 0 {
291            return None;
292        }
293
294        Some(Self {
295            data: NonNull::from_mut(uninit).cast::<Data>(),
296            size: NonNull::from_mut(size),
297            memory: PhantomData,
298            _supported: const { [(); NON_ZERO_SIZED::<Data>] },
299        })
300    }
301
302    /// Try to get access to initialized `Data`, returns `None` if not initialized
303    #[inline(always)]
304    pub const fn get(&self) -> Option<&Data> {
305        // SAFETY: guaranteed to be initialized by constructors
306        if unsafe { self.size.read() } == Data::SIZE {
307            // SAFETY: initialized
308            Some(unsafe { self.data.as_ref() })
309        } else {
310            None
311        }
312    }
313
314    /// Try to get exclusive access to initialized `Data`, returns `None` if not initialized
315    #[inline(always)]
316    pub fn get_mut(&mut self) -> Option<&mut Data> {
317        // SAFETY: guaranteed to be initialized by constructors
318        if unsafe { self.size.read() } == Data::SIZE {
319            // SAFETY: initialized
320            Some(unsafe { self.data.as_mut() })
321        } else {
322            None
323        }
324    }
325
326    /// Initialize by inserting `Data` by value or replace existing value and return reference to it
327    #[inline(always)]
328    pub fn replace(&mut self, data: Data) -> &mut Data {
329        // SAFETY: guaranteed to be initialized by constructors
330        unsafe {
331            self.size.write(Data::SIZE);
332        }
333        // SAFETY: constructor guarantees that memory is aligned
334        unsafe {
335            self.data.write(data);
336            self.data.as_mut()
337        }
338    }
339
340    /// Remove `Data` inside and turn instance back into uninitialized
341    #[inline(always)]
342    pub fn remove(&mut self) {
343        // SAFETY: guaranteed to be initialized by constructors
344        unsafe {
345            self.size.write(0);
346        }
347    }
348
349    /// Get exclusive access to initialized `Data`, running provided initialization function if
350    /// necessary.
351    ///
352    /// `init` is expected to initialize provided memory and return a reference to it, like
353    /// [`MaybeUninit::write()`] does. If it returns a reference to some other `Data` instead, that
354    /// value is copied into this instance.
355    #[inline(always)]
356    pub fn get_mut_or_init_with<Init>(&mut self, init: Init) -> &mut Data
357    where
358        Init: FnOnce(&mut MaybeUninit<Data>) -> &mut Data,
359    {
360        // SAFETY: guaranteed to be initialized by constructors
361        if unsafe { self.size.read() } == Data::SIZE {
362            // SAFETY: initialized
363            unsafe { self.data.as_mut() }
364        } else {
365            // SAFETY: constructor guarantees that memory is aligned
366            let data = init(unsafe { self.data.as_uninit_mut() });
367            if !ptr::eq(data, self.data.as_ptr()) {
368                // SAFETY: constructor guarantees that memory is aligned and valid for writes
369                unsafe {
370                    self.data.write(*data);
371                }
372            }
373            // SAFETY: guaranteed to be initialized by constructors
374            unsafe {
375                self.size.write(Data::SIZE);
376            }
377            // SAFETY: Provided memory is initialized, either by `init`, which can only get a
378            // reference to it by initializing it, or by the copy above
379            unsafe { self.data.as_mut() }
380        }
381    }
382
383    /// Assume value is initialized
384    ///
385    /// # Safety
386    /// Caller must ensure `Data` is actually properly initialized
387    #[inline(always)]
388    pub unsafe fn assume_init(&mut self) -> &mut Data {
389        // SAFETY: guaranteed to be initialized by constructors
390        unsafe {
391            self.size.write(Data::SIZE);
392        }
393        // SAFETY: guaranteed to be initialized by caller, the rest of guarantees are provided by
394        // constructors
395        unsafe { self.data.as_mut() }
396    }
397}
398
399impl<Data> MaybeData<'_, Data>
400where
401    Data: TrivialType + Default,
402    Self: IoType,
403{
404    /// Get exclusive access to initialized `Data`, initializing with default value if necessary
405    #[inline(always)]
406    pub fn get_mut_or_default(&mut self) -> &mut Data {
407        self.get_mut_or_init_with(|data| data.write(Data::default()))
408    }
409}