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