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