Skip to main content

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    generic_const_args,
71    generic_const_items,
72    macroless_generic_const_args,
73    min_generic_const_args,
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::ops::{Deref, DerefMut};
93use core::ptr::NonNull;
94
95// Only little-endian platforms are supported. On big-endian platforms the byte order differs,
96// so `TrivialType` values cannot be transferred simply by sending their raw struct bytes
97// between host and guest environments
98#[cfg(not(target_endian = "little"))]
99compile_error!("Only little-endian platforms are supported");
100
101const {
102    assert!(
103        size_of::<usize>() >= size_of::<u32>(),
104        "At least 32-bit platform required"
105    );
106
107    // Only support targets with expected alignment and refuse to compile on other targets
108    assert!(align_of::<()>() == 1, "Unsupported alignment of `()`");
109    assert!(align_of::<u8>() == 1, "Unsupported alignment of `u8`");
110    assert!(align_of::<u16>() == 2, "Unsupported alignment of `u16`");
111    assert!(align_of::<u32>() == 4, "Unsupported alignment of `u32`");
112    assert!(align_of::<u64>() == 8, "Unsupported alignment of `u64`");
113    assert!(align_of::<i8>() == 1, "Unsupported alignment of `i8`");
114    assert!(align_of::<i16>() == 2, "Unsupported alignment of `i16`");
115    assert!(align_of::<i32>() == 4, "Unsupported alignment of `i32`");
116    assert!(align_of::<i64>() == 8, "Unsupported alignment of `i64`");
117    assert!(
118        align_of::<u128>() <= usize::from(Alignment::MAX.get()),
119        "Unsupported alignment of `u128`"
120    );
121    assert!(
122        align_of::<i128>() <= usize::from(Alignment::MAX.get()),
123        "Unsupported alignment of `i128`"
124    );
125}
126
127struct DerefWrapper<T>(T);
128
129impl<T> Deref for DerefWrapper<T> {
130    type Target = T;
131
132    #[inline(always)]
133    fn deref(&self) -> &Self::Target {
134        &self.0
135    }
136}
137
138impl<T> DerefMut for DerefWrapper<T> {
139    #[inline(always)]
140    fn deref_mut(&mut self) -> &mut Self::Target {
141        &mut self.0
142    }
143}
144
145// TODO: A way to point output types to input types in order to avoid unnecessary memory copy
146//  (setting a pointer)
147/// Trait that is used for types that are crossing the host/guest boundary in contracts.
148///
149/// Crucially, it is implemented for any type that implements [`TrivialType`] and for
150/// [`VariableBytes`](variable_bytes::VariableBytes).
151///
152/// # Safety
153/// This trait is used for types with memory transmutation capabilities. Memory with untrusted
154/// contents is parsed with [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`].
155/// Serializing and deserializing of types that implement this trait is simply casting of underlying
156/// memory. As a result, all the types implementing this trait must not use implicit padding,
157/// unions, or anything similar that might make it unsound to access any bits of the type.
158/// [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`] return `Some` only if
159/// [`IoType::from_ptr()`] and [`IoType::from_mut_ptr()`] with the same arguments would be sound.
160///
161/// Helper functions are provided to make casting to/from bytes a bit safer than it would otherwise,
162/// but extra care is still needed.
163///
164/// **Do not implement this trait explicitly!** Use `#[derive(TrivialType)]` instead, which will
165/// ensure safety requirements are upheld, or use `VariableBytes` or other provided wrapper types if
166/// more flexibility is needed.
167///
168/// In case of variable state size is needed, create a wrapper struct around `VariableBytes` and
169/// implement traits on it by forwarding everything to the inner implementation, including
170/// [`IoType::try_from_ptr()`] and [`IoType::try_from_mut_ptr()`].
171pub unsafe trait IoType {
172    /// Data structure metadata in binary form, describing shape and types of the contents, see
173    /// [`IoTypeMetadataKind`] for encoding details
174    ///
175    /// [`IoTypeMetadataKind`]: metadata::IoTypeMetadataKind
176    const METADATA: &[u8];
177
178    /// Pointer with a trivial type that this `IoType` represents
179    type PointerType: TrivialType;
180
181    /// Number of bytes that are currently used to store data
182    fn size(&self) -> u32;
183
184    /// Number of bytes are allocated right now
185    fn capacity(&self) -> u32;
186
187    /// Set the number of used bytes.
188    ///
189    /// # Safety
190    /// `size` must be valid for the type (see [`Self::try_from_ptr()`]) and not exceed the
191    /// capacity, and that many bytes must be initialized. They don't need to be a valid value of
192    /// the type, but then the contents must not be accessed through this value until they are
193    /// parsed, for example with [`Self::try_from_mut_ptr()`].
194    unsafe fn set_size(&mut self, size: u32);
195
196    /// Create a reference to a type, which is represented by provided memory.
197    ///
198    /// Memory must be correctly aligned and sufficient in size, but padding beyond the size of the
199    /// type is allowed. Memory behind a pointer must not be written to in the meantime either.
200    ///
201    /// Only `size` bytes are guaranteed to be allocated for types that can store a variable amount
202    /// of data due to the read-only nature of read-only access here.
203    ///
204    /// # Safety
205    /// The memory holds a valid value of the type with a valid size, for example one that
206    /// [`Self::try_from_ptr()`] accepts.
207    // `impl Deref` is used to tie lifetime of returned value to inputs but still treat it as a
208    // shared reference for most practical purposes. While lifetime here is somewhat superficial due
209    // to the `Copy` nature of the value, it must be respected. Size must point to properly
210    // initialized memory.
211    #[track_caller]
212    unsafe fn from_ptr<'a>(
213        ptr: &'a NonNull<Self::PointerType>,
214        size: &'a u32,
215        capacity: u32,
216    ) -> impl Deref<Target = Self> + 'a;
217
218    /// Create a mutable reference to a type, which is represented by provided memory.
219    ///
220    /// Memory must be correctly aligned and sufficient in size, but padding beyond the size of the
221    /// type is allowed. Memory behind a pointer must not be read or written to in the meantime
222    /// either.
223    ///
224    /// `size` indicates how many bytes are used within a larger allocation for types that can
225    /// store a variable amount of data.
226    ///
227    /// # Safety
228    /// The memory holds a valid value of the type with a valid size, for example one that
229    /// [`Self::try_from_mut_ptr()`] accepts.
230    // `impl DerefMut` is used to tie lifetime of returned value to inputs, but still treat it as an
231    // exclusive reference for most practical purposes. While lifetime here is somewhat superficial
232    // due to the `Copy` nature of the value, it must be respected. Size must point to properly
233    // initialized and aligned memory for non-[`TrivialType`].
234    #[track_caller]
235    unsafe fn from_mut_ptr<'a>(
236        ptr: &'a mut NonNull<Self::PointerType>,
237        size: &'a mut u32,
238        capacity: u32,
239    ) -> impl DerefMut<Target = Self> + 'a;
240
241    /// Create a reference to a type, which is represented by provided memory, if the memory holds a
242    /// valid value.
243    ///
244    /// In contrast to [`Self::from_ptr()`], the contents are checked, `None` is returned if the
245    /// first `size` bytes are not a valid value. Alignment, `size` and `capacity` are trusted like
246    /// in [`Self::from_ptr()`] and only checked with debug assertions.
247    ///
248    /// # Safety
249    /// `ptr` is aligned for `Self::PointerType`, `size` doesn't exceed `capacity`, and both are
250    /// valid for the type: `size` is the size of a [`TrivialType`], `0` or the size of the data for
251    /// [`MaybeData`] and a multiple of the element size for [`VariableElements`], and the capacity
252    /// of [`MaybeData`] is `0` or at least the size of the data. `ptr` is valid for reads of `size`
253    /// bytes, which are initialized and not written to during the call and while the returned value
254    /// exists.
255    ///
256    /// [`MaybeData`]: maybe_data::MaybeData
257    /// [`VariableElements`]: variable_elements::VariableElements
258    #[track_caller]
259    unsafe fn try_from_ptr<'a>(
260        ptr: &'a NonNull<Self::PointerType>,
261        size: &'a u32,
262        capacity: u32,
263    ) -> Option<impl Deref<Target = Self> + 'a>;
264
265    /// Like [`Self::try_from_ptr()`], but returns an exclusive reference.
266    ///
267    /// # Safety
268    /// `ptr`, `size` and `capacity` are valid as required by [`Self::try_from_ptr()`], except that
269    /// the capacity of [`MaybeData`] is at least the size of the data, since data can be written
270    /// into it even if it is empty. `ptr` is valid for writes of `capacity` bytes and for reads of
271    /// the first `size`, which are initialized, while the bytes after them may be uninitialized.
272    /// The memory is not accessed otherwise during the call and while the returned value exists.
273    ///
274    /// [`MaybeData`]: maybe_data::MaybeData
275    #[track_caller]
276    unsafe fn try_from_mut_ptr<'a>(
277        ptr: &'a mut NonNull<Self::PointerType>,
278        size: &'a mut u32,
279        capacity: u32,
280    ) -> Option<impl DerefMut<Target = Self> + 'a>;
281
282    /// Get a raw pointer to the underlying data.
283    ///
284    /// The pointer can be used for reading [`Self::size()`] bytes as long as the value is not
285    /// modified, it must not be used for writing.
286    fn as_ptr(&self) -> NonNull<Self::PointerType>;
287
288    /// Get an exclusive raw pointer to the underlying data.
289    ///
290    /// The pointer can be used for reading [`Self::size()`] bytes and writing up to
291    /// [`Self::capacity()`] bytes as long as the value is not accessed otherwise in the meantime.
292    /// [`Self::set_size()`] needs to be called afterward if the number of initialized bytes
293    /// changes.
294    fn as_mut_ptr(&mut self) -> NonNull<Self::PointerType>;
295}
296
297/// Marker trait, companion to [`IoType`] that indicates the ability to store optional contents.
298///
299/// This means that zero bytes size is a valid invariant. This trait is never implemented for types
300/// implementing [`TrivialType`] because they always have fixed size, and it is not zero.
301///
302/// # Safety
303/// Size `0` must be a valid state that doesn't require any initialized memory:
304/// [`IoType::from_ptr()`], [`IoType::from_mut_ptr()`], [`IoType::try_from_ptr()`] and
305/// [`IoType::try_from_mut_ptr()`] must be sound to call with `size` of `0` and uninitialized memory
306/// behind the pointer, and must not expose that memory as initialized.
307pub unsafe trait IoTypeOptional: IoType {}
308
309/// Exclusive access to an [`IoType`] value.
310///
311/// It is implemented for:
312/// * every [`IoType`]
313/// * views that constructors of containers return over exclusive memory, like
314///   [`MaybeData::from_mut()`] and [`VariableElements::from_uninit()`]
315///
316/// This allows generic code to accept both.
317///
318/// # Safety
319/// Generic code relies on [`Self::io_type_mut()`] giving access to a single value owned by `Self`:
320/// every call returns a reference to the same value without changing it or anything else, and the
321/// value is not accessible other than through `Self`. Generic code may also move the value out of
322/// `Self` through that reference and forget `Self` right after, so the value must stay valid
323/// without `Self`.
324///
325/// [`MaybeData::from_mut()`]: maybe_data::MaybeData::from_mut
326/// [`VariableElements::from_uninit()`]: variable_elements::VariableElements::from_uninit
327#[diagnostic::on_unimplemented(
328    message = "`{Self}` doesn't give exclusive access to an `IoType` value"
329)]
330pub unsafe trait IoTypeMut {
331    /// The type of the value
332    type Target: IoType;
333
334    /// Exclusive access to the value
335    fn io_type_mut(&mut self) -> &mut Self::Target;
336}
337
338// SAFETY: The value is `self` itself
339unsafe impl<T> IoTypeMut for T
340where
341    T: IoType,
342{
343    type Target = T;
344
345    #[inline(always)]
346    fn io_type_mut(&mut self) -> &mut Self::Target {
347        self
348    }
349}
350
351// SAFETY: The value is the only field of the wrapper, which doesn't implement `Drop`
352unsafe impl<T> IoTypeMut for DerefWrapper<T>
353where
354    T: IoType,
355{
356    type Target = T;
357
358    #[inline(always)]
359    fn io_type_mut(&mut self) -> &mut Self::Target {
360        &mut self.0
361    }
362}