ab_io_type/lib.rs
1//! Infrastructure for zero-cost zero-copy serialization/deserialization.
2//!
3//! <div class="warning">
4//! This crate only supports little-endian platforms by design.
5//! </div>
6//!
7//! This crate primarily offers the following:
8//! * [`TrivialType`] trait
9//! * [`IoType`] trait
10//! * metadata describing types implementing the above traits (see [`IoTypeMetadataKind`])
11//!
12//! [`IoTypeMetadataKind`]: metadata::IoTypeMetadataKind
13//!
14//! ## `TrivialType`
15//! This trait is implemented for a bunch of built-in types and can be derived for custom types that
16//! contain them (structs and enums). It represents trivial types, which do not contain
17//! uninitialized bytes and are fully represented by their byte representation.
18//!
19//! What this means is that serialization to bytes can be done by simply casting a pointer to a data
20//! structure to an array of bytes. Similarly, deserialization of correctly aligned memory is simply
21//! casting of a pointer back to the data structure. The trait provides a few helper methods for
22//! dealing with serialization/deserialization.
23//!
24//! ## `IoType`
25//! This trait is implemented for all types that implement [`TrivialType`] and for a few additional
26//! custom types that have special properties and are useful for FFI purposes.
27//!
28//! `IoType` data structures can contain optional values or lists of values of a dynamic size. They
29//! are not as composable as `TrivialType` and are usually used as wrappers of the highest level.
30//! This is in contrast to `TrivialType` that is always fixed size and can't have optional data.
31//!
32//! ## Metadata
33//!
34//! The data structures implementing [`TrivialType`] and [`IoType`] traits have the `METADATA`
35//! associated constant (see [`IoTypeMetadataKind`]). This field contains a compact binary
36//! representation of the recursive layout of the type, which can then be decoded by the machine for
37//! FFI purposes or converted into human-readable format for presentation to the user in a somewhat
38//! readable way.
39//!
40//! The metadata contains both the memory layout and the names of data structures and fields.
41//! Metadata can also be compressed into an equivalent layout without field names to shrink the size
42//! of the metadata further when human readability is not necessary. Compressed metadata can also
43//! be hashed to get a "fingerprint" of the data structure, which may be used to distinguish a
44//! compatible FFI interface from an incompatible one based on the data layout, not just the number
45//! of bytes.
46//!
47//! ## Overall
48//!
49//! These traits are designed for zero-cost zero-copy serialization/deserialization. Any correctly
50//! aligned memory (both normal and memory-mapped files with `mmap`) can be interpreted as
51//! ready-to-use data structures without even reading them first.
52//!
53//! Does not require a standard library (`no_std`) or an allocator.
54
55#![expect(incomplete_features, reason = "generic_const_*")]
56#![feature(
57 cast_maybe_uninit,
58 const_block_items,
59 const_convert,
60 const_index,
61 const_option_ops,
62 const_result_trait_fn,
63 const_split_off_first_last,
64 const_trait_impl,
65 const_try,
66 generic_const_args,
67 generic_const_items,
68 macroless_generic_const_args,
69 min_generic_const_args,
70 ptr_as_uninit
71)]
72#![no_std]
73
74pub mod bool;
75pub mod fixed_capacity_bytes;
76pub mod fixed_capacity_string;
77pub mod maybe_data;
78pub mod metadata;
79pub mod trivial_type;
80pub mod unaligned;
81pub mod variable_bytes;
82pub mod variable_elements;
83
84use crate::trivial_type::TrivialType;
85use core::ops::{Deref, DerefMut};
86use core::ptr::NonNull;
87
88/// The maximum alignment supported by [`IoType`] types (16 bytes, corresponds to alignment of
89/// `u128`)
90pub const MAX_ALIGNMENT: u8 = 16;
91
92// Only little-endian platforms are supported. On big-endian platforms the byte order differs,
93// so `TrivialType` values cannot be transferred simply by sending their raw struct bytes
94// between host and guest environments
95#[cfg(not(target_endian = "little"))]
96compile_error!("Only little-endian platforms are supported");
97
98const {
99 assert!(
100 size_of::<usize>() >= size_of::<u32>(),
101 "At least 32-bit platform required"
102 );
103
104 // Max alignment is expected to match that of `u128`
105 assert!(
106 align_of::<u128>() == MAX_ALIGNMENT as usize,
107 "Max alignment mismatch"
108 );
109
110 // Only support targets with expected alignment and refuse to compile on other targets
111 assert!(align_of::<()>() == 1, "Unsupported alignment of `()`");
112 assert!(align_of::<u8>() == 1, "Unsupported alignment of `u8`");
113 assert!(align_of::<u16>() == 2, "Unsupported alignment of `u16`");
114 assert!(align_of::<u32>() == 4, "Unsupported alignment of `u32`");
115 assert!(align_of::<u64>() == 8, "Unsupported alignment of `u64`");
116 assert!(align_of::<u128>() == 16, "Unsupported alignment of `u128`");
117 assert!(align_of::<i8>() == 1, "Unsupported alignment of `i8`");
118 assert!(align_of::<i16>() == 2, "Unsupported alignment of `i16`");
119 assert!(align_of::<i32>() == 4, "Unsupported alignment of `i32`");
120 assert!(align_of::<i64>() == 8, "Unsupported alignment of `i64`");
121 assert!(align_of::<i128>() == 16, "Unsupported alignment of `i128`");
122}
123
124struct DerefWrapper<T>(T);
125
126impl<T> Deref for DerefWrapper<T> {
127 type Target = T;
128
129 #[inline(always)]
130 fn deref(&self) -> &Self::Target {
131 &self.0
132 }
133}
134
135impl<T> DerefMut for DerefWrapper<T> {
136 #[inline(always)]
137 fn deref_mut(&mut self) -> &mut Self::Target {
138 &mut self.0
139 }
140}
141
142// TODO: A way to point output types to input types in order to avoid unnecessary memory copy
143// (setting a pointer)
144/// Trait that is used for types that are crossing the host/guest boundary in contracts.
145///
146/// Crucially, it is implemented for any type that implements [`TrivialType`] and for
147/// [`VariableBytes`](variable_bytes::VariableBytes).
148///
149/// # Safety
150/// This trait is used for types with memory transmutation capabilities, it must not be relied on
151/// with untrusted data. Serializing and deserializing of types that implement this trait is simply
152/// casting of underlying memory. As a result, all the types implementing this trait must not use
153/// implicit padding, unions, or anything similar that might make it unsound to access any bits of
154/// the type.
155///
156/// Helper functions are provided to make casting to/from bytes a bit safer than it would otherwise,
157/// but extra care is still needed.
158///
159/// **Do not implement this trait explicitly!** Use `#[derive(TrivialType)]` instead, which will
160/// ensure safety requirements are upheld, or use `VariableBytes` or other provided wrapper types if
161/// more flexibility is needed.
162///
163/// In case of variable state size is needed, create a wrapper struct around `VariableBytes` and
164/// implement traits on it by forwarding everything to the inner implementation.
165pub unsafe trait IoType {
166 /// Data structure metadata in binary form, describing shape and types of the contents, see
167 /// [`IoTypeMetadataKind`] for encoding details
168 ///
169 /// [`IoTypeMetadataKind`]: metadata::IoTypeMetadataKind
170 const METADATA: &[u8];
171
172 /// Pointer with a trivial type that this `IoType` represents
173 type PointerType: TrivialType;
174
175 /// Number of bytes that are currently used to store data
176 fn size(&self) -> u32;
177
178 /// Number of bytes are allocated right now
179 fn capacity(&self) -> u32;
180
181 /// Set the number of used bytes
182 ///
183 /// # Safety
184 /// `size` must be set to number of properly initialized bytes
185 unsafe fn set_size(&mut self, size: u32);
186
187 /// Create a reference to a type, which is represented by provided memory.
188 ///
189 /// Memory must be correctly aligned and sufficient in size, but padding beyond the size of the
190 /// type is allowed. Memory behind a pointer must not be written to in the meantime either.
191 ///
192 /// Only `size` bytes are guaranteed to be allocated for types that can store a variable amount
193 /// of data due to the read-only nature of read-only access here.
194 ///
195 /// # Safety
196 /// Input bytes must be previously produced by taking underlying bytes of the same type.
197 // `impl Deref` is used to tie lifetime of returned value to inputs but still treat it as a
198 // shared reference for most practical purposes. While lifetime here is somewhat superficial due
199 // to the `Copy` nature of the value, it must be respected. Size must point to properly
200 // initialized memory.
201 #[track_caller]
202 unsafe fn from_ptr<'a>(
203 ptr: &'a NonNull<Self::PointerType>,
204 size: &'a u32,
205 capacity: u32,
206 ) -> impl Deref<Target = Self> + 'a;
207
208 /// Create a mutable reference to a type, which is represented by provided memory.
209 ///
210 /// Memory must be correctly aligned and sufficient in size, or else `None` will be returned,
211 /// but padding beyond the size of the type is allowed. Memory behind a pointer must not be
212 /// read or written to in the meantime either.
213 ///
214 /// `size` indicates how many bytes are used within a larger allocation for types that can
215 /// store a variable amount of data.
216 ///
217 /// # Safety
218 /// Input bytes must be previously produced by taking underlying bytes of the same type.
219 // `impl DerefMut` is used to tie lifetime of returned value to inputs, but still treat it as an
220 // exclusive reference for most practical purposes. While lifetime here is somewhat superficial
221 // due to the `Copy` nature of the value, it must be respected. Size must point to properly
222 // initialized and aligned memory for non-[`TrivialType`].
223 #[track_caller]
224 unsafe fn from_mut_ptr<'a>(
225 ptr: &'a mut NonNull<Self::PointerType>,
226 size: &'a mut u32,
227 capacity: u32,
228 ) -> impl DerefMut<Target = Self> + 'a;
229
230 /// Get a raw pointer to the underlying data with no checks.
231 ///
232 /// # Safety
233 /// While calling this function is technically safe, it and allows to ignore many of its
234 /// invariants, so requires extra care. In particular, no modifications must be done to the
235 /// value while this returned pointer might be used and no changes must be done through the
236 /// returned pointer. Also, lifetimes are only superficial here and can be easily (and
237 /// incorrectly) ignored by using `Copy`.
238 unsafe fn as_ptr(&self) -> impl Deref<Target = NonNull<Self::PointerType>>;
239
240 /// Get an exclusive raw pointer to the underlying data with no checks.
241 ///
242 /// # Safety
243 /// While calling this function is technically safe, it and allows to ignore many of its
244 /// invariants, so requires extra care. In particular, the value's contents must not be read or
245 /// written to while returned point might be used. Also, lifetimes are only superficial here and
246 /// can be easily (and incorrectly) ignored by using `Copy`.
247 unsafe fn as_mut_ptr(&mut self) -> impl DerefMut<Target = NonNull<Self::PointerType>>;
248}
249
250/// Marker trait, companion to [`IoType`] that indicates the ability to store optional contents.
251///
252/// This means that zero bytes size is a valid invariant. This type is never implemented for types
253/// implementing [`TrivialType`] because they always have fixed size, and it is not zero.
254pub trait IoTypeOptional: IoType {}