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 parses bytes, checking that they are
21//! a valid value of the type, which for types that accept every bit pattern compiles to a length
22//! check, and then casts the pointer. The trait provides a few helper methods for dealing with
23//! serialization/deserialization.
24//!
25//! ## `IoType`
26//! This trait is implemented for all types that implement [`TrivialType`] and for a few additional
27//! custom types that have special properties and are useful for FFI purposes.
28//!
29//! `IoType` data structures can contain optional values or lists of values of a dynamic size. They
30//! are not as composable as `TrivialType` and are usually used as wrappers of the highest level.
31//! This is in contrast to `TrivialType` that is always fixed size and can't have optional data.
32//!
33//! ## Metadata
34//!
35//! The data structures implementing [`TrivialType`] and [`IoType`] traits have the `METADATA`
36//! associated constant (see [`IoTypeMetadataKind`]). This field contains a compact binary
37//! representation of the recursive layout of the type, which can then be decoded by the machine for
38//! FFI purposes or converted into human-readable format for presentation to the user in a somewhat
39//! readable way.
40//!
41//! The metadata contains both the memory layout and the names of data structures and fields.
42//! Metadata can also be compressed into an equivalent layout without names and with newtypes
43//! replaced by the types they wrap to shrink the size of the metadata further when human
44//! readability is not necessary. Compressed metadata can also be hashed to get a "fingerprint" of
45//! the data structure, which may be used to distinguish a compatible FFI interface from an
46//! incompatible one based on the data layout, not just the number of bytes.
47//!
48//! ## Overall
49//!
50//! These traits are designed for zero-cost zero-copy serialization/deserialization. Correctly
51//! aligned memory (both normal and memory-mapped files with `mmap`) is used in place as
52//! ready-to-use data structures after parsing, which for types that accept every bit pattern is a
53//! length check that doesn't read the memory.
54//!
55//! Does not require a standard library (`no_std`) or an allocator.
56
57#![expect(incomplete_features, reason = "generic_const_*")]
58#![feature(
59 cast_maybe_uninit,
60 const_block_items,
61 const_cmp,
62 const_convert,
63 const_index,
64 const_result_trait_fn,
65 const_split_off_first_last,
66 const_trait_impl,
67 const_try,
68 derive_const,
69 final_associated_functions,
70 gca_const_items,
71 gca_macroless_args,
72 gca_min_const_items,
73 generic_const_items,
74 mut_restriction,
75 ptr_as_uninit
76)]
77#![no_std]
78
79pub mod alignment;
80pub mod fixed_capacity_bytes;
81pub mod fixed_capacity_elements;
82pub mod fixed_capacity_string;
83pub mod maybe_data;
84pub mod metadata;
85pub mod trivial_type;
86pub mod unaligned;
87pub mod variable_bytes;
88pub mod variable_elements;
89
90use crate::alignment::Alignment;
91use crate::trivial_type::TrivialType;
92use core::borrow::BorrowMut;
93use core::ops::{Deref, DerefMut};
94use core::ptr::NonNull;
95
96// Only little-endian platforms are supported. On big-endian platforms the byte order differs,
97// so `TrivialType` values cannot be transferred simply by sending their raw struct bytes
98// between host and guest environments
99#[cfg(not(target_endian = "little"))]
100compile_error!("Only little-endian platforms are supported");
101
102const {
103 assert!(
104 size_of::<usize>() >= size_of::<u32>(),
105 "At least 32-bit platform required"
106 );
107
108 // Only support targets with expected alignment and refuse to compile on other targets
109 assert!(align_of::<()>() == 1, "Unsupported alignment of `()`");
110 assert!(align_of::<u8>() == 1, "Unsupported alignment of `u8`");
111 assert!(align_of::<u16>() == 2, "Unsupported alignment of `u16`");
112 assert!(align_of::<u32>() == 4, "Unsupported alignment of `u32`");
113 assert!(align_of::<u64>() == 8, "Unsupported alignment of `u64`");
114 assert!(align_of::<i8>() == 1, "Unsupported alignment of `i8`");
115 assert!(align_of::<i16>() == 2, "Unsupported alignment of `i16`");
116 assert!(align_of::<i32>() == 4, "Unsupported alignment of `i32`");
117 assert!(align_of::<i64>() == 8, "Unsupported alignment of `i64`");
118 assert!(
119 align_of::<u128>() <= usize::from(Alignment::MAX.get()),
120 "Unsupported alignment of `u128`"
121 );
122 assert!(
123 align_of::<i128>() <= usize::from(Alignment::MAX.get()),
124 "Unsupported alignment of `i128`"
125 );
126}
127
128struct DerefWrapper<T>(T);
129
130impl<T> Deref for DerefWrapper<T> {
131 type Target = T;
132
133 #[inline(always)]
134 fn deref(&self) -> &Self::Target {
135 &self.0
136 }
137}
138
139impl<T> DerefMut for DerefWrapper<T> {
140 #[inline(always)]
141 fn deref_mut(&mut self) -> &mut Self::Target {
142 &mut self.0
143 }
144}
145
146// TODO: A way to point output types to input types in order to avoid unnecessary memory copy
147// (setting a pointer)
148/// Trait that is used for types that are crossing the host/guest boundary in contracts.
149///
150/// Crucially, it is implemented for any type that implements [`TrivialType`] and for
151/// [`VariableBytes`](variable_bytes::VariableBytes).
152///
153/// # Safety
154/// This trait is used for types with memory transmutation capabilities. Memory with untrusted
155/// contents is parsed with [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`].
156/// Serializing and deserializing of types that implement this trait is simply casting of underlying
157/// memory. As a result, all the types implementing this trait must not use implicit padding,
158/// unions, or anything similar that might make it unsound to access any bits of the type.
159/// [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`] return `Some` only if
160/// [`IoType::from_ptr()`] and [`IoType::from_mut_ptr()`] with the same arguments would be sound.
161///
162/// [`IoType::WithLifetime`] is the same type as `Self`, except that the memory it views, if any, is
163/// borrowed for the lifetime it is given. [`IoType::reborrow_mut()`] returns `self` for types that
164/// don't view memory, and a new value that views the same memory for types that do.
165///
166/// Helper functions are provided to make casting to/from bytes a bit safer than it would otherwise,
167/// but extra care is still needed.
168///
169/// **Do not implement this trait explicitly!** Use `#[derive(TrivialType)]` instead, which will
170/// ensure safety requirements are upheld, or use `VariableBytes` or other provided wrapper types if
171/// more flexibility is needed.
172///
173/// In case of variable state size is needed, create a wrapper struct around `VariableBytes` and
174/// implement traits on it by forwarding everything to the inner implementation, including
175/// [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`].
176pub unsafe trait IoType {
177 /// Data structure metadata in binary form, describing shape and types of the contents, see
178 /// [`IoTypeMetadataKind`] for encoding details
179 ///
180 /// [`IoTypeMetadataKind`]: metadata::IoTypeMetadataKind
181 const METADATA: &[u8];
182
183 /// Pointer with a trivial type that this `IoType` represents
184 type PointerType: TrivialType;
185
186 /// The same type, viewing memory borrowed for `'b`.
187 ///
188 /// Containers like [`VariableElements`] view memory they don't own and have a lifetime
189 /// parameter for it, which is `'b` here. Types that don't view memory, like those implementing
190 /// [`TrivialType`], are `Self`.
191 ///
192 /// Methods that create values from pointers and [`Self::reborrow_mut()`] return this type with
193 /// the lifetime of the borrow. So a container moved out of what they return, for example with
194 /// [`mem::swap()`], can't be used after the borrow ends, whatever lifetime `Self` has.
195 ///
196 /// [`VariableElements`]: variable_elements::VariableElements
197 /// [`mem::swap()`]: core::mem::swap
198 type WithLifetime<'b>: IoType<PointerType = Self::PointerType>;
199
200 /// Exclusive access to a value for `'b`, which [`Self::reborrow_mut()`] returns.
201 ///
202 /// This is `&'b mut Self` for types that don't view memory, like those implementing
203 /// [`TrivialType`], and [`Self::WithLifetime`] by value for containers. Generic code can get
204 /// `&mut Self::WithLifetime<'b>` from both with [`BorrowMut::borrow_mut()`].
205 type Reborrowed<'b>: BorrowMut<Self::WithLifetime<'b>>
206 where
207 Self: 'b;
208
209 /// Number of bytes that are currently used to store data
210 fn size(&self) -> u32;
211
212 /// Number of bytes are allocated right now
213 fn capacity(&self) -> u32;
214
215 /// Set the number of used bytes.
216 ///
217 /// # Safety
218 /// `size` must be valid for the type (see [`Self::try_from_ptr()`]) and not exceed the
219 /// capacity, and that many bytes must be initialized. They don't need to be a valid value of
220 /// the type, but then the contents must not be accessed through this value until they are
221 /// parsed, for example with [`Self::try_from_mut_ptr()`].
222 unsafe fn set_size(&mut self, size: u32);
223
224 /// Create a reference to a type, which is represented by provided memory.
225 ///
226 /// Memory must be correctly aligned and sufficient in size, but padding beyond the size of the
227 /// type is allowed. Memory behind a pointer must not be written to in the meantime either.
228 ///
229 /// Only `size` bytes are guaranteed to be allocated for types that can store a variable amount
230 /// of data due to the read-only nature of read-only access here.
231 ///
232 /// # Safety
233 /// The memory holds a valid value of the type with a valid size, for example one that
234 /// [`Self::try_from_ptr()`] accepts.
235 // `impl Deref` keeps `ptr` and `size` borrowed for `'a`, and otherwise works like a shared
236 // reference. The value it points to has the lifetime of that borrow too (see
237 // `Self::WithLifetime`). Size must point to properly initialized memory.
238 #[track_caller]
239 unsafe fn from_ptr<'a>(
240 ptr: &'a NonNull<Self::PointerType>,
241 size: &'a u32,
242 capacity: u32,
243 ) -> impl Deref<Target = Self::WithLifetime<'a>> + 'a;
244
245 /// Create a mutable reference to a type, which is represented by provided memory.
246 ///
247 /// Memory must be correctly aligned and sufficient in size, but padding beyond the size of the
248 /// type is allowed. Memory behind a pointer must not be read or written to in the meantime
249 /// either.
250 ///
251 /// `size` indicates how many bytes are used within a larger allocation for types that can
252 /// store a variable amount of data.
253 ///
254 /// # Safety
255 /// The memory holds a valid value of the type with a valid size, for example one that
256 /// [`Self::try_from_mut_ptr()`] accepts.
257 ///
258 /// The returned value can be used for `'a`. A container like [`VariableElements`] can also be
259 /// moved out of it, for example with [`mem::swap()`], and then used until `'a` ends. So the
260 /// memory must stay valid and must not be accessed otherwise until `'a` ends, not only while
261 /// the returned value exists.
262 ///
263 /// [`VariableElements`]: variable_elements::VariableElements
264 /// [`mem::swap()`]: core::mem::swap
265 // `impl DerefMut` keeps `ptr` and `size` borrowed for `'a`, and otherwise works like an
266 // exclusive reference. The value it points to has the lifetime of that borrow too (see
267 // `Self::WithLifetime`), so a container moved out of it can't be used after `'a` ends either.
268 // Size must point to properly initialized and aligned memory for non-[`TrivialType`].
269 #[track_caller]
270 unsafe fn from_mut_ptr<'a>(
271 ptr: &'a mut NonNull<Self::PointerType>,
272 size: &'a mut u32,
273 capacity: u32,
274 ) -> impl DerefMut<Target = Self::WithLifetime<'a>> + 'a;
275
276 /// Create a reference to a type, which is represented by provided memory, if the memory holds a
277 /// valid value.
278 ///
279 /// In contrast to [`Self::from_ptr()`], the contents are checked, `None` is returned if the
280 /// first `size` bytes are not a valid value. Alignment, `size` and `capacity` are trusted like
281 /// in [`Self::from_ptr()`] and only checked with debug assertions.
282 ///
283 /// # Safety
284 /// `ptr` is aligned for `Self::PointerType`, `size` doesn't exceed `capacity`, and both are
285 /// valid for the type: `size` is the size of a [`TrivialType`], `0` or the size of the data for
286 /// [`MaybeData`] and a multiple of the element size for [`VariableElements`], and the capacity
287 /// of [`MaybeData`] is `0` or at least the size of the data. `ptr` is valid for reads of `size`
288 /// bytes, which are initialized and not written to during the call and while the returned value
289 /// exists.
290 ///
291 /// [`MaybeData`]: maybe_data::MaybeData
292 /// [`VariableElements`]: variable_elements::VariableElements
293 #[track_caller]
294 unsafe fn try_from_ptr<'a>(
295 ptr: &'a NonNull<Self::PointerType>,
296 size: &'a u32,
297 capacity: u32,
298 ) -> Option<impl Deref<Target = Self::WithLifetime<'a>> + 'a>;
299
300 /// Like [`Self::try_from_ptr()`], but returns an exclusive reference.
301 ///
302 /// # Safety
303 /// `ptr`, `size` and `capacity` are valid as required by [`Self::try_from_ptr()`], except that
304 /// the capacity of [`MaybeData`] is at least the size of the data, since data can be written
305 /// into it even if it is empty. `ptr` is valid for writes of `capacity` bytes and for reads of
306 /// the first `size`, which are initialized, while the bytes after them may be uninitialized.
307 /// The memory is not accessed otherwise during the call and while the returned value exists.
308 /// Like with [`Self::from_mut_ptr()`], this lasts until `'a` ends, since a container can be
309 /// moved out of the returned value.
310 ///
311 /// [`MaybeData`]: maybe_data::MaybeData
312 #[track_caller]
313 unsafe fn try_from_mut_ptr<'a>(
314 ptr: &'a mut NonNull<Self::PointerType>,
315 size: &'a mut u32,
316 capacity: u32,
317 ) -> Option<impl DerefMut<Target = Self::WithLifetime<'a>> + 'a>;
318
319 /// Get a raw pointer to the underlying data.
320 ///
321 /// The pointer can be used for reading [`Self::size()`] bytes as long as the value is not
322 /// modified, it must not be used for writing.
323 fn as_ptr(&self) -> NonNull<Self::PointerType>;
324
325 /// Get an exclusive raw pointer to the underlying data.
326 ///
327 /// The pointer can be used for reading [`Self::size()`] bytes and writing up to
328 /// [`Self::capacity()`] bytes as long as the value is not accessed otherwise in the meantime.
329 /// [`Self::set_size()`] needs to be called afterward if the number of initialized bytes
330 /// changes.
331 fn as_mut_ptr(&mut self) -> NonNull<Self::PointerType>;
332
333 /// Exclusive access to this value for the duration of the borrow.
334 ///
335 /// For a container, this is a new view of the same memory with the lifetime of the borrow.
336 /// Replacing it doesn't replace the container behind `self`, and it can't be used after the
337 /// borrow ends. Types that don't view memory return `self`.
338 fn reborrow_mut(&mut self) -> Self::Reborrowed<'_>;
339}
340
341/// Marker trait, companion to [`IoType`] that indicates the ability to store optional contents.
342///
343/// This means that zero bytes size is a valid invariant. This trait is never implemented for types
344/// implementing [`TrivialType`] because they always have fixed size, and it is not zero.
345///
346/// # Safety
347/// Size `0` must be a valid state that doesn't require any initialized memory:
348/// [`IoType::from_ptr()`], [`IoType::from_mut_ptr()`], [`IoType::try_from_ptr()`] and
349/// [`IoType::try_from_mut_ptr()`] must be sound to call with `size` of `0` and uninitialized memory
350/// behind the pointer, and must not expose that memory as initialized.
351pub unsafe trait IoTypeOptional: IoType {}