Skip to main content

sp_runtime_interface/
pass_by.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Provides host <-> runtime FFI marshalling strategy newtype wrappers
19//! for defining runtime interfaces.
20
21use crate::{
22	util::{pack_ptr_and_len, unpack_ptr_and_len},
23	RIType,
24};
25
26#[cfg(not(substrate_runtime))]
27use crate::host::*;
28
29#[cfg(substrate_runtime)]
30use crate::wasm::*;
31
32#[cfg(not(substrate_runtime))]
33use sp_wasm_interface::{FunctionContext, Pointer, Result};
34
35#[cfg(not(substrate_runtime))]
36use alloc::{format, string::String};
37
38use alloc::vec::Vec;
39use core::{any::type_name, marker::PhantomData};
40
41/// Pass a value into the host by a thin pointer.
42///
43/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
44/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
45/// into target type using `From<[u8; N]>` and passes it into the host function by a copy.
46///
47/// Use [`PassPointerAndRead`] if you want to have the host function accept a reference type
48/// on the host side or if you'd like to avoid the extra copy.
49///
50/// Raw FFI type: `u32` (a pointer)
51pub struct PassPointerAndReadCopy<T, const N: usize>(PhantomData<(T, [u8; N])>);
52
53impl<T, const N: usize> RIType for PassPointerAndReadCopy<T, N> {
54	type FFIType = u32;
55	type Inner = T;
56}
57
58#[cfg(not(substrate_runtime))]
59impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndReadCopy<T, N>
60where
61	T: From<[u8; N]> + Copy,
62{
63	type Owned = T;
64
65	fn from_ffi_value(
66		context: &mut dyn FunctionContext,
67		arg: Self::FFIType,
68	) -> Result<Self::Owned> {
69		let mut out = [0; N];
70		context.read_memory_into(Pointer::new(arg), &mut out)?;
71		Ok(T::from(out))
72	}
73
74	#[inline]
75	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
76		*owned
77	}
78}
79
80#[cfg(substrate_runtime)]
81impl<T, const N: usize> IntoFFIValue for PassPointerAndReadCopy<T, N>
82where
83	T: AsRef<[u8]>,
84{
85	type Destructor = ();
86
87	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
88		// Using an 'assert' instead of a 'T: AsRef<[u8; N]>` bound since a '[u8; N]' *doesn't*
89		// implement it.
90		assert_eq!(value.as_ref().len(), N);
91		(value.as_ref().as_ptr() as u32, ())
92	}
93}
94
95/// Pass a value into the host by a thin pointer.
96///
97/// This casts the value into a `&[u8]` using `AsRef<[u8]>` and passes a pointer to that byte blob
98/// to the host. Then the host reads `N` bytes from that address into an `[u8; N]`, converts it
99/// into target type using `From<[u8; N]>` and passes it into the host function by a reference.
100///
101/// This can only be used with reference types (e.g. `&[u8; 32]`). Use [`PassPointerAndReadCopy`]
102/// if you want to have the host function accept a non-reference type on the host side.
103///
104/// Raw FFI type: `u32` (a pointer)
105pub struct PassPointerAndRead<T, const N: usize>(PhantomData<(T, [u8; N])>);
106
107impl<'a, T, const N: usize> RIType for PassPointerAndRead<&'a T, N> {
108	type FFIType = u32;
109	type Inner = &'a T;
110}
111
112#[cfg(not(substrate_runtime))]
113impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndRead<&'a T, N>
114where
115	T: From<[u8; N]>,
116{
117	type Owned = T;
118
119	fn from_ffi_value(
120		context: &mut dyn FunctionContext,
121		arg: Self::FFIType,
122	) -> Result<Self::Owned> {
123		let mut out = [0; N];
124		context.read_memory_into(Pointer::new(arg), &mut out)?;
125		Ok(T::from(out))
126	}
127
128	#[inline]
129	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
130		&*owned
131	}
132}
133
134#[cfg(substrate_runtime)]
135impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndRead<&'a T, N>
136where
137	T: AsRef<[u8]>,
138{
139	type Destructor = ();
140
141	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
142		assert_eq!(value.as_ref().len(), N);
143		(value.as_ref().as_ptr() as u32, ())
144	}
145}
146
147/// Pass a value into the host by a fat pointer.
148///
149/// This casts the value into a `&[u8]` and passes a pointer to that byte blob and its length
150/// to the host. Then the host reads that blob and converts it into an owned type and passes it
151/// (either as an owned type or as a reference) to the host function.
152///
153/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
154pub struct PassFatPointerAndRead<T>(PhantomData<T>);
155
156impl<T> RIType for PassFatPointerAndRead<T> {
157	type FFIType = u64;
158	type Inner = T;
159}
160
161#[cfg(not(substrate_runtime))]
162impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a [u8]> {
163	type Owned = Vec<u8>;
164
165	fn from_ffi_value(
166		context: &mut dyn FunctionContext,
167		arg: Self::FFIType,
168	) -> Result<Self::Owned> {
169		let (ptr, len) = unpack_ptr_and_len(arg);
170		context.read_memory(Pointer::new(ptr), len)
171	}
172
173	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
174		&*owned
175	}
176}
177
178#[cfg(not(substrate_runtime))]
179impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<&'a str> {
180	type Owned = String;
181
182	fn from_ffi_value(
183		context: &mut dyn FunctionContext,
184		arg: Self::FFIType,
185	) -> Result<Self::Owned> {
186		let (ptr, len) = unpack_ptr_and_len(arg);
187		let vec = context.read_memory(Pointer::new(ptr), len)?;
188		String::from_utf8(vec).map_err(|_| "could not parse '&str' when marshalling hostcall's arguments through the FFI boundary: the string is not valid UTF-8".into())
189	}
190
191	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
192		&*owned
193	}
194}
195
196#[cfg(not(substrate_runtime))]
197impl<'a> FromFFIValue<'a> for PassFatPointerAndRead<Vec<u8>> {
198	type Owned = Vec<u8>;
199
200	fn from_ffi_value(
201		context: &mut dyn FunctionContext,
202		arg: Self::FFIType,
203	) -> Result<Self::Owned> {
204		<PassFatPointerAndRead<&[u8]> as FromFFIValue>::from_ffi_value(context, arg)
205	}
206
207	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
208		core::mem::take(owned)
209	}
210}
211
212#[cfg(substrate_runtime)]
213impl<T> IntoFFIValue for PassFatPointerAndRead<T>
214where
215	T: AsRef<[u8]>,
216{
217	type Destructor = ();
218
219	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
220		let value = value.as_ref();
221		(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
222	}
223}
224
225/// Pass a value into the host by a fat pointer, writing it back after the host call ends.
226///
227/// This casts the value into a `&mut [u8]` and passes a pointer to that byte blob and its length
228/// to the host. Then the host reads that blob and converts it into an owned type and passes it
229/// as a mutable reference to the host function. After the host function finishes the byte blob
230/// is written back into the guest memory.
231///
232/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
233pub struct PassFatPointerAndReadWrite<T>(PhantomData<T>);
234
235impl<T> RIType for PassFatPointerAndReadWrite<T> {
236	type FFIType = u64;
237	type Inner = T;
238}
239
240#[cfg(not(substrate_runtime))]
241impl<'a> FromFFIValue<'a> for PassFatPointerAndReadWrite<&'a mut [u8]> {
242	type Owned = Vec<u8>;
243
244	fn from_ffi_value(
245		context: &mut dyn FunctionContext,
246		arg: Self::FFIType,
247	) -> Result<Self::Owned> {
248		let (ptr, len) = unpack_ptr_and_len(arg);
249		context.read_memory(Pointer::new(ptr), len)
250	}
251
252	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
253		&mut *owned
254	}
255
256	fn write_back_into_runtime(
257		value: Self::Owned,
258		context: &mut dyn FunctionContext,
259		arg: Self::FFIType,
260	) -> Result<()> {
261		let (ptr, len) = unpack_ptr_and_len(arg);
262		assert_eq!(len as usize, value.len());
263		context.write_memory(Pointer::new(ptr), &value)
264	}
265}
266
267#[cfg(substrate_runtime)]
268impl<'a> IntoFFIValue for PassFatPointerAndReadWrite<&'a mut [u8]> {
269	type Destructor = ();
270
271	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
272		(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
273	}
274}
275
276/// Pass a pointer into the host by a fat pointer, writing it back after the host call ends.
277///
278/// This casts the value into a `&mut [u8]` and passes a pointer to that byte blob and its length
279/// to the host. The host *doesn't* read from this and instead creates a zero-initialized buffer
280/// as a mutable reference to the host function. After the host function finishes the byte blob
281/// is written back into the guest memory.
282///
283/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
284pub struct PassFatPointerAndWrite<T>(PhantomData<T>);
285
286impl<T> RIType for PassFatPointerAndWrite<T> {
287	type FFIType = u64;
288	type Inner = T;
289}
290
291#[cfg(not(substrate_runtime))]
292impl<'a> FromFFIValue<'a> for PassFatPointerAndWrite<&'a mut [u8]> {
293	type Owned = Vec<u8>;
294
295	fn from_ffi_value(
296		_context: &mut dyn FunctionContext,
297		arg: Self::FFIType,
298	) -> Result<Self::Owned> {
299		let (_ptr, len) = unpack_ptr_and_len(arg);
300		Ok(alloc::vec![0u8; len as usize])
301	}
302
303	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
304		&mut *owned
305	}
306
307	fn write_back_into_runtime(
308		value: Self::Owned,
309		context: &mut dyn FunctionContext,
310		arg: Self::FFIType,
311	) -> Result<()> {
312		let (ptr, len) = unpack_ptr_and_len(arg);
313		assert_eq!(len as usize, value.len());
314		context.write_memory(Pointer::new(ptr), &value)
315	}
316}
317
318#[cfg(substrate_runtime)]
319impl<'a> IntoFFIValue for PassFatPointerAndWrite<&'a mut [u8]> {
320	type Destructor = ();
321
322	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
323		(pack_ptr_and_len(value.as_ptr() as u32, value.len() as u32), ())
324	}
325}
326
327/// Pass a pointer into the host and write to it after the host call ends.
328///
329/// This casts a given type into `&mut [u8]` using `AsMut<[u8]>` and passes a pointer to
330/// that byte slice into the host. The host *doesn't* read from this and instead creates
331/// a default instance of type `T` and passes it as a `&mut T` into the host function
332/// implementation. After the host function finishes this value is then cast into a `&[u8]` using
333/// `AsRef<[u8]>` and written back into the guest memory.
334///
335/// Raw FFI type: `u32` (a pointer)
336pub struct PassPointerAndWrite<T, const N: usize>(PhantomData<(T, [u8; N])>);
337
338impl<T, const N: usize> RIType for PassPointerAndWrite<T, N> {
339	type FFIType = u32;
340	type Inner = T;
341}
342
343#[cfg(not(substrate_runtime))]
344impl<'a, T, const N: usize> FromFFIValue<'a> for PassPointerAndWrite<&'a mut T, N>
345where
346	T: Default + AsRef<[u8]>,
347{
348	type Owned = T;
349
350	fn from_ffi_value(
351		_context: &mut dyn FunctionContext,
352		_arg: Self::FFIType,
353	) -> Result<Self::Owned> {
354		Ok(T::default())
355	}
356
357	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
358		&mut *owned
359	}
360
361	fn write_back_into_runtime(
362		value: Self::Owned,
363		context: &mut dyn FunctionContext,
364		arg: Self::FFIType,
365	) -> Result<()> {
366		let value = value.as_ref();
367		assert_eq!(value.len(), N);
368		context.write_memory(Pointer::new(arg), value)
369	}
370}
371
372#[cfg(substrate_runtime)]
373impl<'a, T, const N: usize> IntoFFIValue for PassPointerAndWrite<&'a mut T, N>
374where
375	T: AsMut<[u8]>,
376{
377	type Destructor = ();
378
379	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
380		let value = value.as_mut();
381		assert_eq!(value.len(), N);
382		(value.as_ptr() as u32, ())
383	}
384}
385
386/// Pass a `T` into the host using the SCALE codec.
387///
388/// This encodes a `T` into a `Vec<u8>` using the SCALE codec and then
389/// passes a pointer to that byte blob and its length to the host,
390/// which then reads it and decodes back into `T`.
391///
392/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
393pub struct PassFatPointerAndDecode<T>(PhantomData<T>);
394
395impl<T> RIType for PassFatPointerAndDecode<T> {
396	type FFIType = u64;
397	type Inner = T;
398}
399
400#[cfg(not(substrate_runtime))]
401impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecode<T> {
402	type Owned = Option<T>;
403
404	fn from_ffi_value(
405		context: &mut dyn FunctionContext,
406		arg: Self::FFIType,
407	) -> Result<Self::Owned> {
408		let (ptr, len) = unpack_ptr_and_len(arg);
409		let vec = context.read_memory(Pointer::new(ptr), len)?;
410		T::decode(&mut &vec[..]).map_err(|error| format!(
411			"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
412			type_name::<T>())
413		).map(Some)
414	}
415
416	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
417		owned.take().expect("this is called only once and is never 'None'")
418	}
419}
420
421#[cfg(substrate_runtime)]
422impl<T: codec::Encode> IntoFFIValue for PassFatPointerAndDecode<T> {
423	type Destructor = Vec<u8>;
424
425	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
426		let data = value.encode();
427		(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
428	}
429}
430
431/// Pass a `&[T]` into the host using the SCALE codec.
432///
433/// This encodes a `&[T]` into a `Vec<u8>` using the SCALE codec and then
434/// passes a pointer to that byte blob and its length to the host,
435/// which then reads it and decodes back into `Vec<T>` and passes
436/// a reference to that (as `&[T]`) into the host function.
437///
438/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
439pub struct PassFatPointerAndDecodeSlice<T>(PhantomData<T>);
440
441impl<T> RIType for PassFatPointerAndDecodeSlice<T> {
442	type FFIType = u64;
443	type Inner = T;
444}
445
446#[cfg(not(substrate_runtime))]
447impl<'a, T: codec::Decode> FromFFIValue<'a> for PassFatPointerAndDecodeSlice<&'a [T]> {
448	type Owned = Vec<T>;
449
450	fn from_ffi_value(
451		context: &mut dyn FunctionContext,
452		arg: Self::FFIType,
453	) -> Result<Self::Owned> {
454		let (ptr, len) = unpack_ptr_and_len(arg);
455		let vec = context.read_memory(Pointer::new(ptr), len)?;
456		<Vec::<T> as codec::Decode>::decode(&mut &vec[..]).map_err(|error| format!(
457			"could not SCALE-decode '{}' when marshalling hostcall's arguments through the FFI boundary: {error}",
458			type_name::<Vec<T>>()
459		))
460	}
461
462	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
463		&*owned
464	}
465}
466
467#[cfg(substrate_runtime)]
468impl<'a, T: codec::Encode> IntoFFIValue for PassFatPointerAndDecodeSlice<&'a [T]> {
469	type Destructor = Vec<u8>;
470
471	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
472		let data = codec::Encode::encode(value);
473		(pack_ptr_and_len(data.as_ptr() as u32, data.len() as u32), data)
474	}
475}
476
477/// A trait signifying a primitive type.
478trait Primitive: Copy {}
479
480impl Primitive for u8 {}
481impl Primitive for u16 {}
482impl Primitive for u32 {}
483impl Primitive for u64 {}
484
485impl Primitive for i8 {}
486impl Primitive for i16 {}
487impl Primitive for i32 {}
488impl Primitive for i64 {}
489
490/// Pass `T` through the FFI boundary by first converting it to `U` in the runtime, and then
491/// converting it back to `T` on the host's side.
492///
493/// Raw FFI type: same as `U`'s FFI type
494pub struct PassAs<T, U>(PhantomData<(T, U)>);
495
496impl<T, U> RIType for PassAs<T, U>
497where
498	U: RIType,
499{
500	type FFIType = <U as RIType>::FFIType;
501	type Inner = T;
502}
503
504#[cfg(not(substrate_runtime))]
505impl<'a, T, U> FromFFIValue<'a> for PassAs<T, U>
506where
507	U: RIType + FromFFIValue<'a> + Primitive,
508	T: TryFrom<<U as FromFFIValue<'a>>::Owned> + Copy,
509{
510	type Owned = T;
511
512	fn from_ffi_value(
513		context: &mut dyn FunctionContext,
514		arg: Self::FFIType,
515	) -> Result<Self::Owned> {
516		<U as FromFFIValue>::from_ffi_value(context, arg).and_then(|value| value.try_into()
517			.map_err(|_| format!(
518				"failed to convert '{}' (passed as '{}') into '{}' when marshalling hostcall's arguments through the FFI boundary",
519				type_name::<U>(),
520				type_name::<Self::FFIType>(),
521				type_name::<Self::Owned>()
522			)))
523	}
524
525	fn take_from_owned(owned: &'a mut Self::Owned) -> Self::Inner {
526		*owned
527	}
528}
529
530#[cfg(substrate_runtime)]
531impl<T, U> IntoFFIValue for PassAs<T, U>
532where
533	U: RIType + IntoFFIValue + Primitive,
534	U::Inner: From<T>,
535	T: Copy,
536{
537	type Destructor = <U as IntoFFIValue>::Destructor;
538
539	fn into_ffi_value(value: &mut Self::Inner) -> (Self::FFIType, Self::Destructor) {
540		let mut value = U::Inner::from(*value);
541		<U as IntoFFIValue>::into_ffi_value(&mut value)
542	}
543}
544
545/// Return `T` through the FFI boundary by first converting it to `U` on the host's side, and then
546/// converting it back to `T` in the runtime.
547///
548/// Raw FFI type: same as `U`'s FFI type
549pub struct ReturnAs<T, U>(PhantomData<(T, U)>);
550
551impl<T, U> RIType for ReturnAs<T, U>
552where
553	U: RIType,
554{
555	type FFIType = <U as RIType>::FFIType;
556	type Inner = T;
557}
558
559#[cfg(not(substrate_runtime))]
560impl<T, U> IntoFFIValue for ReturnAs<T, U>
561where
562	U: RIType + IntoFFIValue + Primitive,
563	<U as RIType>::Inner: From<Self::Inner>,
564{
565	fn into_ffi_value(
566		value: Self::Inner,
567		context: &mut dyn FunctionContext,
568	) -> Result<Self::FFIType> {
569		let value: <U as RIType>::Inner = value.into();
570		<U as IntoFFIValue>::into_ffi_value(value, context)
571	}
572}
573
574#[cfg(substrate_runtime)]
575impl<T, U> FromFFIValue for ReturnAs<T, U>
576where
577	U: RIType + FromFFIValue + Primitive,
578	Self::Inner: TryFrom<U::Inner>,
579{
580	fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
581		let value = <U as FromFFIValue>::from_ffi_value(arg);
582		match Self::Inner::try_from(value) {
583			Ok(value) => value,
584			Err(_) => {
585				panic!(
586					"failed to convert '{}' (passed as '{}') into a '{}' when marshalling a hostcall's return value through the FFI boundary",
587					type_name::<U::Inner>(),
588					type_name::<Self::FFIType>(),
589					type_name::<Self::Inner>()
590				);
591			},
592		}
593	}
594}
595
596/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
597///
598/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
599/// using the legacy allocator, copies the slice into the runtime memory, and
600/// returns a pointer to it.
601///
602/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
603///
604/// Ideally use a mutable slice to return data to the guest, for example using
605/// the [`PassPointerAndWrite`] strategy.
606///
607/// Raw FFI type: `u32` (a pointer to the byte blob)
608pub struct AllocateAndReturnPointer<T, const N: usize>(PhantomData<(T, [u8; N])>);
609
610impl<T, const N: usize> RIType for AllocateAndReturnPointer<T, N> {
611	type FFIType = u32;
612	type Inner = T;
613}
614
615#[cfg(not(substrate_runtime))]
616impl<T, const N: usize> IntoFFIValue for AllocateAndReturnPointer<T, N>
617where
618	T: AsRef<[u8]>,
619{
620	fn into_ffi_value(
621		value: Self::Inner,
622		context: &mut dyn FunctionContext,
623	) -> Result<Self::FFIType> {
624		let value = value.as_ref();
625		assert_eq!(
626			value.len(),
627			N,
628			"expected the byte blob to be {N} bytes long, is {} bytes when returning '{}' from a host function",
629			value.len(),
630			type_name::<T>()
631		);
632
633		let addr = context.allocate_memory(value.len() as u32)?;
634		context.write_memory(addr, value)?;
635		Ok(addr.into())
636	}
637}
638
639#[cfg(substrate_runtime)]
640impl<T: codec::Decode, const N: usize> FromFFIValue for AllocateAndReturnPointer<T, N>
641where
642	T: From<[u8; N]>,
643{
644	fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
645		// SAFETY: This memory was allocated by the host allocator with the exact
646		// capacity needed, so it's safe to make a `Vec` out of it.
647		let value = unsafe { Vec::from_raw_parts(arg as *mut u8, N, N) };
648
649		// SAFETY: Reading a `[u8; N]` from a `&[u8]` which is at least `N` elements long is safe.
650		let array = unsafe { *(value.as_ptr() as *const [u8; N]) };
651		T::from(array)
652	}
653}
654
655/// (DEPRECATED) Return `T` as a blob of bytes into the runtime.
656///
657/// Uses `T::AsRef<[u8]>` to cast `T` into a `&[u8]`, allocates runtime memory
658/// using the legacy allocator, copies the slice into the runtime memory, and
659/// returns a pointer to it.
660///
661/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
662///
663/// Ideally use a mutable slice to return data to the guest, for example using
664/// the [`PassPointerAndWrite`] strategy.
665///
666/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
667pub struct AllocateAndReturnFatPointer<T>(PhantomData<T>);
668
669impl<T> RIType for AllocateAndReturnFatPointer<T> {
670	type FFIType = u64;
671	type Inner = T;
672}
673
674#[cfg(not(substrate_runtime))]
675impl<T> IntoFFIValue for AllocateAndReturnFatPointer<T>
676where
677	T: AsRef<[u8]>,
678{
679	fn into_ffi_value(
680		value: Self::Inner,
681		context: &mut dyn FunctionContext,
682	) -> Result<Self::FFIType> {
683		let value = value.as_ref();
684		let ptr = context.allocate_memory(value.len() as u32)?;
685		context.write_memory(ptr, &value)?;
686		Ok(pack_ptr_and_len(ptr.into(), value.len() as u32))
687	}
688}
689
690#[cfg(substrate_runtime)]
691impl<T> FromFFIValue for AllocateAndReturnFatPointer<T>
692where
693	T: From<Vec<u8>>,
694{
695	fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
696		let (ptr, len) = unpack_ptr_and_len(arg);
697		let len = len as usize;
698		let vec = if len == 0 {
699			Vec::new()
700		} else {
701			// SAFETY: This memory was allocated by the host allocator with the exact
702			// capacity needed, so it's safe to make a `Vec` out of it.
703			unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) }
704		};
705
706		T::from(vec)
707	}
708}
709
710/// (DEPRECATED) Return `T` into the runtime using the SCALE codec.
711///
712/// Encodes `T` using the SCALE codec, allocates runtime memory using the legacy
713/// allocator, copies the encoded payload into the runtime memory, and returns
714/// a fat pointer to it.
715///
716/// THIS STRATEGY IS DEPRECATED; DO NOT USE FOR NEW HOST FUNCTIONS!
717///
718/// Ideally use a mutable slice to return data to the guest, for example using
719/// the [`PassPointerAndWrite`] strategy.
720///
721/// Raw FFI type: `u64` (a fat pointer; upper 32 bits is the size, lower 32 bits is the pointer)
722pub struct AllocateAndReturnByCodec<T>(PhantomData<T>);
723
724impl<T> RIType for AllocateAndReturnByCodec<T> {
725	type FFIType = u64;
726	type Inner = T;
727}
728
729#[cfg(not(substrate_runtime))]
730impl<T: codec::Encode> IntoFFIValue for AllocateAndReturnByCodec<T> {
731	fn into_ffi_value(value: T, context: &mut dyn FunctionContext) -> Result<Self::FFIType> {
732		let vec = value.encode();
733		let ptr = context.allocate_memory(vec.len() as u32)?;
734		context.write_memory(ptr, &vec)?;
735		Ok(pack_ptr_and_len(ptr.into(), vec.len() as u32))
736	}
737}
738
739#[cfg(substrate_runtime)]
740impl<T: codec::Decode> FromFFIValue for AllocateAndReturnByCodec<T> {
741	fn from_ffi_value(arg: Self::FFIType) -> Self::Inner {
742		let (ptr, len) = unpack_ptr_and_len(arg);
743		let len = len as usize;
744
745		let encoded = if len == 0 {
746			bytes::Bytes::new()
747		} else {
748			// SAFETY: This memory was allocated by the host allocator with the exact
749			// capacity needed, so it's safe to make a `Vec` out of it.
750			bytes::Bytes::from(unsafe { Vec::from_raw_parts(ptr as *mut u8, len, len) })
751		};
752
753		match codec::decode_from_bytes(encoded) {
754			Ok(value) => value,
755			Err(error) => {
756				panic!(
757					"failed to decode '{}' when marshalling a hostcall's return value through the FFI boundary: {error}",
758					type_name::<T>(),
759				);
760			},
761		}
762	}
763}