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