Skip to main content

ab_io_type/
variable_elements.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, try_skip_valid};
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/// Whether the first `size` bytes behind `ptr` are valid elements of [`VariableElements`].
14///
15/// # Safety
16/// `ptr` is aligned and valid for reads of `size` bytes, which are initialized and not written to
17/// during the call.
18#[inline(always)]
19unsafe fn contents_are_valid<Element>(ptr: NonNull<Element>, size: u32) -> bool
20where
21    Element: TrivialType,
22    [(); NON_ZERO_SIZED::<Element>]:,
23{
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
26    if size_of::<Element>() == 0 {
27        return false;
28    }
29    // SAFETY: `ptr` is valid for reads of `size` initialized bytes, which are not written to
30    // during the call (function contract)
31    let bytes = unsafe { slice::from_raw_parts(ptr.as_ptr().cast::<u8>(), size as usize) };
32    // SAFETY: `ptr` is aligned (function contract)
33    unsafe { try_skip_valid::<Element, true>(bytes, bytes.len() / size_of::<Element>()) }.is_some()
34}
35
36/// Container for storing variable number of elements.
37///
38/// It is a view of memory that is borrowed for `'a`.
39///
40/// `RECOMMENDED_ALLOCATION` is what is being used when a host needs to allocate memory for call
41/// into guest, but guest may receive an allocation with more or less memory in practice depending
42/// on other circumstances, like when called from another contract with specific allocation
43/// specified.
44#[derive(Debug)]
45#[repr(C)]
46pub struct VariableElements<'a, Element, const RECOMMENDED_ALLOCATION: u32 = 0>
47where
48    Element: TrivialType,
49{
50    elements: NonNull<Element>,
51    size: NonNull<u32>,
52    capacity: u32,
53    memory: PhantomData<&'a ()>,
54    // TODO: Technically redundant with the where-clause, which is not enforced in generic code
55    //  due to https://github.com/rust-lang/rust/issues/164019, remove once fixed. Every instance
56    //  evaluates the check through this field. It is initialized in `const` blocks, which are
57    //  evaluated even if optimizations remove the field.
58    _supported: [(); NON_ZERO_SIZED::<Element>],
59}
60
61// SAFETY: Low-level (effectively internal) implementation that upholds safety requirements
62unsafe impl<Element, const RECOMMENDED_ALLOCATION: u32> IoType
63    for VariableElements<'_, Element, RECOMMENDED_ALLOCATION>
64where
65    Element: TrivialType,
66    [(); NON_ZERO_SIZED::<Element>]:,
67{
68    const METADATA: &[u8] = {
69        const fn metadata(
70            recommended_allocation: u32,
71            inner_metadata: &[u8],
72        ) -> ([u8; MAX_METADATA_CAPACITY], usize) {
73            concat_metadata_sources(&[
74                &[IoTypeMetadataKind::VariableElements as u8],
75                &recommended_allocation.to_le_bytes(),
76                inner_metadata,
77            ])
78        }
79
80        // The reference extends the lifetime of metadata scratch to that of the constant
81        let (metadata_scratch, metadata_size) =
82            &metadata(RECOMMENDED_ALLOCATION, Element::METADATA);
83        metadata_scratch.split_at(*metadata_size).0
84    };
85
86    // TODO: Use `[Element; U32_TO_USIZE::<RECOMMENDED_ALLOCATION>]` with `gca_const_items`
87    type PointerType = Element;
88
89    type WithLifetime<'b> = VariableElements<'b, Element, RECOMMENDED_ALLOCATION>;
90
91    type Reborrowed<'b>
92        = VariableElements<'b, Element, RECOMMENDED_ALLOCATION>
93    where
94        Self: 'b;
95
96    #[inline(always)]
97    fn size(&self) -> u32 {
98        self.size()
99    }
100
101    #[inline(always)]
102    fn capacity(&self) -> u32 {
103        self.capacity
104    }
105
106    #[inline(always)]
107    #[track_caller]
108    unsafe fn set_size(&mut self, size: u32) {
109        debug_assert!(
110            size <= self.capacity,
111            "`set_size` called with invalid input {size} for capacity {}",
112            self.capacity
113        );
114        debug_assert!(
115            size.is_multiple_of(Element::SIZE),
116            "`set_size` called with invalid input {size} for element size {}",
117            Element::SIZE
118        );
119
120        // SAFETY: guaranteed to be initialized by constructors
121        unsafe {
122            self.size.write(size);
123        }
124    }
125
126    #[inline(always)]
127    #[track_caller]
128    unsafe fn from_ptr<'a>(
129        ptr: &'a NonNull<Self::PointerType>,
130        size: &'a u32,
131        capacity: u32,
132    ) -> impl Deref<Target = Self::WithLifetime<'a>> + 'a {
133        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
134        debug_assert!(
135            *size <= capacity,
136            "Size {size} must not exceed capacity {capacity}"
137        );
138        debug_assert!(
139            size.is_multiple_of(Element::SIZE),
140            "Size {size} is invalid for element size {}",
141            Element::SIZE
142        );
143
144        DerefWrapper(VariableElements {
145            elements: *ptr,
146            size: NonNull::from_ref(size),
147            capacity,
148            memory: PhantomData,
149            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
150        })
151    }
152
153    #[inline(always)]
154    #[track_caller]
155    unsafe fn from_mut_ptr<'a>(
156        ptr: &'a mut NonNull<Self::PointerType>,
157        size: &'a mut u32,
158        capacity: u32,
159    ) -> impl DerefMut<Target = Self::WithLifetime<'a>> + 'a {
160        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
161        debug_assert!(
162            *size <= capacity,
163            "Size {size} must not exceed capacity {capacity}"
164        );
165        debug_assert!(
166            size.is_multiple_of(Element::SIZE),
167            "Size {size} is invalid for element size {}",
168            Element::SIZE
169        );
170
171        DerefWrapper(VariableElements {
172            elements: *ptr,
173            size: NonNull::from_mut(size),
174            capacity,
175            memory: PhantomData,
176            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
177        })
178    }
179
180    #[inline(always)]
181    #[track_caller]
182    unsafe fn try_from_ptr<'a>(
183        ptr: &'a NonNull<Self::PointerType>,
184        size: &'a u32,
185        capacity: u32,
186    ) -> Option<impl Deref<Target = Self::WithLifetime<'a>> + 'a> {
187        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
188        debug_assert!(
189            *size <= capacity,
190            "Size {size} must not exceed capacity {capacity}"
191        );
192        debug_assert!(
193            size.is_multiple_of(Element::SIZE),
194            "Size {size} is invalid for element size {}",
195            Element::SIZE
196        );
197        // SAFETY: `ptr` is aligned and valid for reads of `size` initialized bytes, which are not
198        // written to during the call (function contract)
199        if !unsafe { contents_are_valid(*ptr, *size) } {
200            return None;
201        }
202
203        // SAFETY: `size` and `capacity` are valid, `ptr` is aligned and the memory is not written
204        // to while the returned value exists (function contract), and the first `size` bytes are
205        // valid elements as checked above
206        Some(unsafe { Self::from_ptr(ptr, size, capacity) })
207    }
208
209    #[inline(always)]
210    #[track_caller]
211    unsafe fn try_from_mut_ptr<'a>(
212        ptr: &'a mut NonNull<Self::PointerType>,
213        size: &'a mut u32,
214        capacity: u32,
215    ) -> Option<impl DerefMut<Target = Self::WithLifetime<'a>> + 'a> {
216        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
217        debug_assert!(
218            *size <= capacity,
219            "Size {size} must not exceed capacity {capacity}"
220        );
221        debug_assert!(
222            size.is_multiple_of(Element::SIZE),
223            "Size {size} is invalid for element size {}",
224            Element::SIZE
225        );
226        // SAFETY: `ptr` is aligned and valid for reads of the first `size` bytes, which are
227        // initialized, and the memory is not accessed otherwise during the call (function contract)
228        if !unsafe { contents_are_valid(*ptr, *size) } {
229            return None;
230        }
231
232        // SAFETY: `size` and `capacity` are valid, `ptr` is aligned, valid for writes of `capacity`
233        // bytes, the memory and `size` are not accessed otherwise for as long as
234        // `Self::from_mut_ptr()` requires (function contract), and the first `size` bytes are
235        // valid elements as checked above
236        Some(unsafe { Self::from_mut_ptr(ptr, size, capacity) })
237    }
238
239    #[inline(always)]
240    fn as_ptr(&self) -> NonNull<Self::PointerType> {
241        self.elements
242    }
243
244    #[inline(always)]
245    fn as_mut_ptr(&mut self) -> NonNull<Self::PointerType> {
246        self.elements
247    }
248
249    #[inline(always)]
250    fn reborrow_mut(&mut self) -> Self::Reborrowed<'_> {
251        VariableElements {
252            elements: self.elements,
253            size: self.size,
254            capacity: self.capacity,
255            memory: PhantomData,
256            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
257        }
258    }
259}
260
261// SAFETY: Size `0` means there are no initialized elements, contents are only accessed up to the
262// size
263unsafe impl<Element, const RECOMMENDED_ALLOCATION: u32> IoTypeOptional
264    for VariableElements<'_, Element, RECOMMENDED_ALLOCATION>
265where
266    Element: TrivialType,
267    [(); NON_ZERO_SIZED::<Element>]:,
268{
269}
270
271// Sizes are converted to the number of elements by dividing by the element size, so elements must
272// not be zero-sized, which the `IoType` implementation checks
273impl<'a, Element, const RECOMMENDED_ALLOCATION: u32>
274    VariableElements<'a, Element, RECOMMENDED_ALLOCATION>
275where
276    Element: TrivialType,
277    Self: IoType,
278{
279    /// Create a new shared instance from provided memory buffer.
280    ///
281    /// NOTE: size is specified in bytes, not elements.
282    ///
283    /// Returns `None` if `size_of_val(buffer) != size`.
284    //
285    // `impl Deref` is returned instead of `Self`, since methods that take `&mut self` would
286    // otherwise write into shared memory.
287    #[inline(always)]
288    pub const fn from_buffer(
289        buffer: &'a [Element],
290        size: &'a u32,
291    ) -> Option<impl Deref<Target = Self> + 'a> {
292        if size_of_val(buffer) != *size as usize {
293            return None;
294        }
295
296        Some(DerefWrapper(Self {
297            elements: NonNull::new(buffer.as_ptr().cast_mut()).expect("Not null; qed"),
298            size: NonNull::from_ref(size),
299            capacity: *size,
300            memory: PhantomData,
301            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
302        }))
303    }
304
305    /// Create a new exclusive instance from provided memory buffer.
306    ///
307    /// NOTE: size is specified in bytes, not elements.
308    ///
309    /// Returns `None` if `size_of_val(buffer) != size`.
310    #[inline(always)]
311    pub fn from_buffer_mut(buffer: &'a mut [Element], size: &'a mut u32) -> Option<Self> {
312        if size_of_val(buffer) != *size as usize {
313            return None;
314        }
315
316        Some(Self {
317            elements: NonNull::new(buffer.as_mut_ptr()).expect("Not null; qed"),
318            size: NonNull::from_mut(size),
319            capacity: *size,
320            memory: PhantomData,
321            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
322        })
323    }
324
325    /// Create a new exclusive instance from provided uninitialized memory buffer.
326    ///
327    /// `size` must be `0` since none of the elements are initialized yet, the whole buffer is used
328    /// as capacity.
329    ///
330    /// Returns `None` if `size != 0` or if the buffer is larger than `u32::MAX` bytes.
331    #[inline(always)]
332    pub fn from_uninit(uninit: &'a mut [MaybeUninit<Element>], size: &'a mut u32) -> Option<Self> {
333        if *size != 0 {
334            return None;
335        }
336        let capacity = u32::try_from(size_of_val(uninit)).ok()?;
337
338        Some(Self {
339            elements: NonNull::new(uninit.as_mut_ptr().cast_init()).expect("Not null; qed"),
340            size: NonNull::from_mut(size),
341            capacity,
342            memory: PhantomData,
343            _supported: const { [(); NON_ZERO_SIZED::<Element>] },
344        })
345    }
346
347    /// Number of elements
348    #[inline(always)]
349    pub const fn count(&self) -> u32 {
350        self.size() / Element::SIZE
351    }
352
353    /// Try to get access to initialized elements
354    #[inline(always)]
355    pub const fn get_initialized(&self) -> &[Element] {
356        let size = self.size();
357        let ptr = self.elements.as_ptr();
358        // SAFETY: guaranteed by constructor and explicit methods by the user
359        unsafe { slice::from_raw_parts(ptr, (size / Element::SIZE) as usize) }
360    }
361
362    /// Try to get exclusive access to initialized `Data`, returns `None` if not initialized
363    #[inline(always)]
364    pub fn get_initialized_mut(&mut self) -> &mut [Element] {
365        let size = self.size();
366        let ptr = self.elements.as_ptr();
367        // SAFETY: guaranteed by constructor and explicit methods by the user
368        unsafe { slice::from_raw_parts_mut(ptr, (size / Element::SIZE) as usize) }
369    }
370
371    /// Append some elements by using more of allocated, but currently unused elements.
372    ///
373    /// `true` is returned on success, but if there isn't enough unused elements left, `false` is.
374    #[inline(always)]
375    #[must_use = "Operation may fail"]
376    pub fn append(&mut self, elements: &[Element]) -> bool {
377        let size = self.size();
378        let Some(new_size) = u32::try_from(size_of_val(elements))
379            .ok()
380            .and_then(|appended_size| size.checked_add(appended_size))
381        else {
382            return false;
383        };
384        if new_size > self.capacity {
385            return false;
386        }
387
388        // May overflow, which is not allowed
389        let Ok(offset) = isize::try_from(size / Element::SIZE) else {
390            return false;
391        };
392
393        // SAFETY: allocation range and offset are checked above, the allocation itself is
394        // guaranteed by constructors
395        let start = unsafe { self.elements.offset(offset) };
396        // SAFETY: Alignment is the same, writing happens in properly allocated memory guaranteed by
397        // constructors, number of elements is checked above, Rust ownership rules will prevent any
398        // overlap here (creating reference to non-initialized part of allocation would already be
399        // undefined behavior anyway). The new size covers initialized elements only and is checked
400        // to be within capacity above.
401        unsafe {
402            ptr::copy_nonoverlapping(elements.as_ptr(), start.as_ptr(), elements.len());
403            self.size.write(new_size);
404        }
405
406        true
407    }
408
409    /// Truncate internal initialized bytes to this size.
410    ///
411    /// Returns `true` on success or `false` if `new_size` is larger than [`Self::size()`] or not a
412    /// multiple of `Element::SIZE`.
413    #[inline(always)]
414    #[must_use = "Operation may fail"]
415    pub fn truncate(&mut self, new_size: u32) -> bool {
416        if new_size > self.size() || !new_size.is_multiple_of(Element::SIZE) {
417            return false;
418        }
419
420        // SAFETY: guaranteed to be initialized by constructors
421        unsafe {
422            self.size.write(new_size);
423        }
424
425        true
426    }
427
428    /// Copy contents from another instance.
429    ///
430    /// Returns `false` if actual capacity of the instance is not enough to copy contents of `src`
431    #[inline(always)]
432    #[must_use = "Operation may fail"]
433    pub fn copy_from(
434        &mut self,
435        src: &VariableElements<'_, Element, RECOMMENDED_ALLOCATION>,
436    ) -> bool {
437        let src_size = src.size();
438        if src_size > self.capacity {
439            return false;
440        }
441
442        // SAFETY: `src` can't be the same as `&mut self` if invariants of constructor arguments
443        // were upheld, size is checked to be within capacity above, and the number of copied
444        // elements corresponds to that size
445        unsafe {
446            self.elements
447                .copy_from_nonoverlapping(src.elements, (src_size / Element::SIZE) as usize);
448            self.size.write(src_size);
449        }
450
451        true
452    }
453
454    /// Assume that the first `size` are initialized and can be read.
455    ///
456    /// Returns `Some(initialized_elements)` on success or `None` if `size` is larger than its
457    /// capacity or not a multiple of `Element::SIZE`.
458    ///
459    /// # Safety
460    /// Caller must ensure `size` is actually initialized
461    #[inline(always)]
462    #[must_use = "Operation may fail"]
463    pub unsafe fn assume_init(&mut self, size: u32) -> Option<&mut [Element]> {
464        if size > self.capacity || !size.is_multiple_of(Element::SIZE) {
465            return None;
466        }
467
468        // SAFETY: guaranteed to be initialized by constructors
469        unsafe {
470            self.size.write(size);
471        }
472        Some(self.get_initialized_mut())
473    }
474}
475
476// These methods don't convert sizes to the number of elements, so they work for any element type
477impl<'a, Element, const RECOMMENDED_ALLOCATION: u32>
478    VariableElements<'a, Element, RECOMMENDED_ALLOCATION>
479where
480    Element: TrivialType,
481{
482    // Size in bytes
483    #[inline(always)]
484    pub const fn size(&self) -> u32 {
485        // SAFETY: guaranteed to be initialized by constructors
486        unsafe { self.size.read() }
487    }
488
489    /// Capacity in bytes
490    #[inline(always)]
491    pub fn capacity(&self) -> u32 {
492        self.capacity
493    }
494
495    /// Cast a shared reference to this instance into a reference to an instance of a different
496    /// recommended allocation
497    #[inline(always)]
498    pub fn cast_ref<const DIFFERENT_RECOMMENDED_ALLOCATION: u32>(
499        &self,
500    ) -> &VariableElements<'a, Element, DIFFERENT_RECOMMENDED_ALLOCATION> {
501        // SAFETY: `VariableElements` has a fixed layout due to `#[repr(C)]`, which doesn't depend
502        // on recommended allocation
503        unsafe {
504            NonNull::from_ref(self)
505                .cast::<VariableElements<'a, Element, DIFFERENT_RECOMMENDED_ALLOCATION>>()
506                .as_ref()
507        }
508    }
509
510    /// Cast an exclusive reference to this instance into a reference to an instance of a different
511    /// recommended allocation
512    #[inline(always)]
513    pub fn cast_mut<const DIFFERENT_RECOMMENDED_ALLOCATION: u32>(
514        &mut self,
515    ) -> &mut VariableElements<'a, Element, DIFFERENT_RECOMMENDED_ALLOCATION> {
516        // SAFETY: `VariableElements` has a fixed layout due to `#[repr(C)]`, which doesn't depend
517        // on recommended allocation
518        unsafe {
519            NonNull::from_mut(self)
520                .cast::<VariableElements<'a, Element, DIFFERENT_RECOMMENDED_ALLOCATION>>()
521                .as_mut()
522        }
523    }
524}