Skip to main content

ab_contracts_common/
output.rs

1//! Memory given to method calls as `#[output]`
2
3use crate::ContractError;
4use ab_io_type::IoType;
5use ab_io_type::maybe_data::MaybeData;
6use ab_io_type::trivial_type::{AnyBitPattern, TrivialType};
7use ab_io_type::variable_elements::VariableElements;
8use core::fmt;
9use core::marker::PhantomData;
10use core::mem::MaybeUninit;
11use core::ptr::NonNull;
12
13/// Memory that can be given to a method call as an `#[output]` of type `Target`.
14///
15/// A call can leave contents behind that are not a valid value of `Target`, for example when it
16/// fails halfway, so the contents must be parsed before they are accessed, unless every bit
17/// pattern is a valid value (see [`AnyBitPattern`]). [`Lent`] does this for memory whose contents
18/// need parsing.
19///
20/// A call gets the pointer, size and capacity from [`Self::as_raw_output()`]. Until `self` is used
21/// again, the call may write initialized bytes anywhere within the capacity, even if it fails. If
22/// it succeeds, the size it reports is set with [`Self::set_output_size()`].
23///
24/// # Safety
25/// [`Self::as_raw_output()`] returns a pointer aligned for `Target::PointerType`, and a size that
26/// is valid for `Target` and doesn't exceed the capacity it returns. The pointer is valid for
27/// writes of the capacity, but for reads only of the size, with that many bytes initialized: the
28/// bytes after them may be uninitialized and must not be read before they are written. While
29/// `self` stays exclusively borrowed after that, the memory is not accessed other than through the
30/// pointer. Whatever initialized bytes a call leaves within the size (the initial one or the last
31/// one set with [`Self::set_output_size()`]) are never accessed as `Target` without being parsed
32/// first, unless every bit pattern is a valid value of `Target::PointerType`.
33#[diagnostic::on_unimplemented(
34    message = "`{Self}` can't be given to a call as an `#[output]` of type `{Target}`",
35    label = "expected `&mut {Target}` whose contents accept every bit pattern, or \
36    `&mut Lent<{Target}>`",
37    note = "a failed call can leave invalid contents behind, use `Lent::new()` for a local value, \
38    `Lent::<MaybeData<'_, _>>::from_uninit()` or \
39    `Lent::<VariableElements<'_, _>>::from_uninit()` for a local container, or \
40    `Env::lend_output()` for an `#[output]` the method received"
41)]
42pub unsafe trait OutputArg<Target>
43where
44    Target: IoType,
45{
46    /// Pointer to the contents, size and capacity to give to a call
47    fn as_raw_output(&mut self) -> (NonNull<Target::PointerType>, u32, u32);
48
49    /// Set the size that a successful call reported.
50    ///
51    /// # Safety
52    /// `size` must be valid for `Target` (see [`IoType::try_from_ptr()`]) and not exceed the
53    /// capacity, and that many bytes must be initialized.
54    unsafe fn set_output_size(&mut self, size: u32);
55}
56
57// Without this attribute rustc reports the unsatisfied `AnyBitPattern` bound of the contents of
58// containers instead of the message of `OutputArg`
59#[diagnostic::do_not_recommend]
60// SAFETY: Pointer, size and capacity are those of a valid value, the pointer can be used for reads
61// of the size and writes of the capacity while the value isn't accessed otherwise
62// (`IoType::as_mut_ptr()`), which it isn't while `self` is exclusively borrowed. Initialized
63// contents are a valid value, since every bit pattern is.
64unsafe impl<T> OutputArg<T> for T
65where
66    T: IoType<PointerType: AnyBitPattern>,
67{
68    #[inline(always)]
69    fn as_raw_output(&mut self) -> (NonNull<T::PointerType>, u32, u32) {
70        (
71            IoType::as_mut_ptr(self),
72            IoType::size(self),
73            IoType::capacity(self),
74        )
75    }
76
77    #[inline(always)]
78    unsafe fn set_output_size(&mut self, size: u32) {
79        // SAFETY: The size is valid and doesn't exceed the capacity, with that many bytes
80        // initialized (function contract), which are a valid value, since every bit pattern is
81        unsafe { IoType::set_size(self, size) }
82    }
83}
84
85enum LentValue<'a, T> {
86    /// A plain value owned by `Lent`, which may not be a valid value after a call
87    Owned(MaybeUninit<T>),
88    /// A container view owned by `Lent` over memory borrowed for `'a`. The view itself is valid,
89    /// while its contents may not be after a call.
90    View(T),
91    /// A plain value or a container view borrowed for `'a`
92    Borrowed(NonNull<T>, PhantomData<&'a mut T>),
93}
94
95/// Memory lent to calls as an `#[output]`, whose contents are parsed before they are accessed.
96///
97/// A call can leave contents behind that are not a valid value, for example when it fails halfway,
98/// so the contents are only accessible through [`Self::get_mut()`], which parses them once after
99/// each call. Plain values can also be replaced with [`Self::set()`]. Memory whose contents accept
100/// every bit pattern can be given to calls directly, see [`OutputArg`].
101///
102/// Created with [`Self::new()`] for a local value, with `Lent::<MaybeData<'_, _>>::from_uninit()`
103/// or `Lent::<VariableElements<'_, _>>::from_uninit()` for a local container, and with
104/// [`Env::lend_output()`] for an `#[output]` the method received.
105///
106/// [`Env::lend_output()`]: crate::env::Env::lend_output
107pub struct Lent<'a, T>
108where
109    T: IoType,
110{
111    value: LentValue<'a, T>,
112    /// Pointer to the contents, size and capacity, read from the value again while it is
113    /// `checked`
114    ptr: NonNull<T::PointerType>,
115    size: u32,
116    capacity: u32,
117    /// Neither a call nor a size set changed the contents since they were last known to be valid
118    checked: bool,
119}
120
121impl<T> fmt::Debug for Lent<'_, T>
122where
123    T: IoType,
124{
125    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
126        // Contents may not be a valid value
127        f.debug_struct("Lent")
128            .field("size", &self.size)
129            .field("capacity", &self.capacity)
130            .field("checked", &self.checked)
131            .finish_non_exhaustive()
132    }
133}
134
135// SAFETY: Pointer, size and capacity are those of the value when it was last known to be valid
136// (created, parsed or set), so the pointer can be used for reads of the size and writes of the
137// capacity (`IoType::as_mut_ptr()`). Since then, only calls changed the contents, by writing
138// initialized bytes within the capacity, and the size, through `Self::set_output_size()`. The value
139// is owned by `self` or borrowed by it for `'a`, so it isn't accessed while `self` is exclusively
140// borrowed. The contents are only accessed as `T` after `Self::get_mut()` parsed them or
141// `Self::set()` wrote a valid value.
142unsafe impl<T> OutputArg<T> for Lent<'_, T>
143where
144    T: IoType,
145{
146    #[inline(always)]
147    fn as_raw_output(&mut self) -> (NonNull<T::PointerType>, u32, u32) {
148        let raw_parts = self.raw_parts();
149        self.checked = false;
150        raw_parts
151    }
152
153    #[inline(always)]
154    unsafe fn set_output_size(&mut self, size: u32) {
155        // Make sure the cached size is that of the value
156        self.raw_parts();
157        // The only valid size of a plain value is its size, so a different size means `T` is a
158        // container view, which is a valid value regardless of its contents
159        if size != self.size {
160            // SAFETY: A container view, which is valid, owned by `self` or borrowed by it, and not
161            // referenced elsewhere while `self` is exclusively borrowed
162            let value = unsafe { self.value_ptr().as_mut() };
163            // SAFETY: The size is valid and doesn't exceed the capacity, with that many bytes
164            // initialized (function contract), the contents are only accessed after
165            // `Self::get_mut()` parsed them
166            unsafe {
167                IoType::set_size(value, size);
168            }
169            self.size = size;
170        }
171        // The contents may have changed together with the size
172        self.checked = false;
173    }
174}
175
176impl<T> Lent<'static, T>
177where
178    T: TrivialType,
179{
180    /// Lend a local value
181    #[inline(always)]
182    pub fn new(value: T) -> Self {
183        Self {
184            value: LentValue::Owned(MaybeUninit::new(value)),
185            // Read from the value before it is given to a call
186            ptr: NonNull::dangling(),
187            size: T::SIZE,
188            capacity: T::SIZE,
189            checked: true,
190        }
191    }
192}
193
194impl<'a, Data> Lent<'a, MaybeData<'a, Data>>
195where
196    Data: TrivialType,
197    MaybeData<'a, Data>: IoType,
198{
199    /// Lend a local [`MaybeData`] over uninitialized memory, like [`MaybeData::from_uninit()`].
200    ///
201    /// Returns `None` if `size != 0`.
202    #[inline(always)]
203    pub fn from_uninit(uninit: &'a mut MaybeUninit<Data>, size: &'a mut u32) -> Option<Self> {
204        Some(Self::from_view(MaybeData::from_uninit(uninit, size)?))
205    }
206}
207
208impl<'a, Element, const RECOMMENDED_ALLOCATION: u32>
209    Lent<'a, VariableElements<'a, Element, RECOMMENDED_ALLOCATION>>
210where
211    Element: TrivialType,
212    VariableElements<'a, Element, RECOMMENDED_ALLOCATION>: IoType,
213{
214    /// Lend a local [`VariableElements`] over uninitialized memory, like
215    /// [`VariableElements::from_uninit()`].
216    ///
217    /// Returns `None` if `size != 0` or if the buffer is larger than `u32::MAX` bytes.
218    #[inline(always)]
219    pub fn from_uninit(uninit: &'a mut [MaybeUninit<Element>], size: &'a mut u32) -> Option<Self> {
220        Some(Self::from_view(VariableElements::from_uninit(
221            uninit, size,
222        )?))
223    }
224}
225
226impl<'a, T> Lent<'a, T>
227where
228    T: IoType,
229{
230    /// Lend a container view over memory borrowed for `'a`
231    #[inline(always)]
232    fn from_view(mut view: T) -> Self {
233        let ptr = IoType::as_mut_ptr(&mut view);
234        let size = IoType::size(&view);
235        let capacity = IoType::capacity(&view);
236        Self {
237            value: LentValue::View(view),
238            ptr,
239            size,
240            capacity,
241            checked: true,
242        }
243    }
244
245    /// Lend `value`, which is only borrowed for `'a`, rather than until the method returns like
246    /// with [`Env::lend_output()`].
247    ///
248    /// # Safety
249    /// After the returned `Lent` was given to a call, `value` may hold contents that are not a
250    /// valid value. Once the `Lent` is no longer used, the contents of `value` must not be accessed
251    /// without being parsed first, unless the last use of the `Lent` after its last call was a
252    /// successful [`Self::get_mut()`] or [`Self::set()`], or the contents of `T` accept every bit
253    /// pattern.
254    ///
255    /// [`Env::lend_output()`]: crate::env::Env::lend_output
256    #[inline(always)]
257    pub unsafe fn from_mut_unchecked(value: &'a mut T) -> Self {
258        let ptr = IoType::as_mut_ptr(value);
259        let size = IoType::size(value);
260        let capacity = IoType::capacity(value);
261        Self {
262            value: LentValue::Borrowed(NonNull::from_mut(value), PhantomData),
263            ptr,
264            size,
265            capacity,
266            checked: true,
267        }
268    }
269
270    /// Exclusive access to the contents, which are parsed once after each call.
271    ///
272    /// Repeated calls without a call in between don't parse the contents again. Returns
273    /// [`ContractError::BadOutput`] if they are not a valid value of `T`. They then stay
274    /// inaccessible until [`Self::set()`], which only exists for plain values, replaces them.
275    /// Lending them again doesn't help either: methods generated by `#[contract]` reject outputs
276    /// with invalid initial contents with [`ContractError::BadInput`].
277    ///
278    /// A plain value is returned as `&mut T`. A container is returned as a new view of the lent
279    /// memory that only lives while `self` is borrowed (see [`IoType::reborrow_mut()`]). Code that
280    /// replaces it, for example with a view of other memory, doesn't change what `self` lends to
281    /// calls.
282    #[inline(always)]
283    pub fn get_mut(&mut self) -> Result<T::Reborrowed<'_>, ContractError> {
284        if !self.checked {
285            let (mut ptr, mut size, capacity) = self.raw_parts();
286            // SAFETY: The pointer is aligned and valid for writes of the capacity and for reads of
287            // the size, which is valid for `T` and doesn't exceed the capacity, with that many
288            // bytes initialized. The value was valid with these parts, and calls since then only
289            // wrote initialized bytes within the capacity and set a valid size
290            // (`OutputArg::set_output_size()` contract). Nothing else accesses the memory while
291            // `self` is exclusively borrowed, and nothing is moved out of the returned value,
292            // which is dropped right away.
293            if unsafe { T::try_from_mut_ptr(&mut ptr, &mut size, capacity) }.is_none() {
294                return Err(ContractError::BadOutput);
295            }
296            self.checked = true;
297        }
298        // SAFETY: A valid value, since it was parsed above or neither a call nor a size set changed
299        // it since it was last known to be valid, owned by `self` or borrowed by it, and not
300        // referenced elsewhere while `self` is borrowed
301        let value = unsafe { self.value_ptr().as_mut() };
302        Ok(value.reborrow_mut())
303    }
304
305    /// Pointer to the value, which may not be valid unless `checked`
306    #[inline(always)]
307    fn value_ptr(&mut self) -> NonNull<T> {
308        match &mut self.value {
309            LentValue::Owned(value) => NonNull::from_mut(value).cast::<T>(),
310            LentValue::View(view) => NonNull::from_mut(view),
311            LentValue::Borrowed(value, _) => *value,
312        }
313    }
314
315    /// Pointer to the contents, size and capacity, read from the value unless a call or a size set
316    /// may have changed it since it was last known to be valid
317    #[inline(always)]
318    fn raw_parts(&mut self) -> (NonNull<T::PointerType>, u32, u32) {
319        if self.checked {
320            // SAFETY: A valid value, since neither a call nor a size set changed it since it was
321            // created, parsed or set, owned by `self` or borrowed by it, and not referenced
322            // elsewhere while `self` is exclusively borrowed
323            let value = unsafe { self.value_ptr().as_mut() };
324            self.ptr = IoType::as_mut_ptr(value);
325            self.size = IoType::size(value);
326            self.capacity = IoType::capacity(value);
327        } else if let LentValue::Owned(value) = &mut self.value {
328            // An owned value moves together with `Lent`, unlike the memory of other values
329            self.ptr = NonNull::from_mut(value).cast::<T::PointerType>();
330        }
331        (self.ptr, self.size, self.capacity)
332    }
333}
334
335impl<T> Lent<'_, T>
336where
337    T: TrivialType,
338{
339    /// Replace the contents with a valid value
340    #[inline(always)]
341    pub fn set(&mut self, value: T) -> &mut T {
342        let mut ptr = self.value_ptr();
343        // SAFETY: Aligned and valid for writes of `T`, owned by `self` or borrowed by it, and not
344        // referenced elsewhere while `self` is exclusively borrowed
345        unsafe {
346            ptr.write(value);
347        }
348        self.checked = true;
349        // SAFETY: A valid value was written above, not referenced elsewhere while `self` is
350        // borrowed
351        unsafe { ptr.as_mut() }
352    }
353}