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