Skip to main content

ab_io_type/
variable_elements.rs

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