ab_io_type/
trivial_type.rs

1use crate::metadata::{IoTypeMetadataKind, MAX_METADATA_CAPACITY, concat_metadata_sources};
2use crate::{DerefWrapper, IoType};
3pub use ab_trivial_type_derive::TrivialType;
4use core::ops::{Deref, DerefMut};
5use core::ptr;
6use core::ptr::NonNull;
7
8/// Simple wrapper data type that is designed in such a way that its serialization/deserialization
9/// is the same as the type itself.
10///
11/// # Safety
12/// This trait is used for types with memory layout that can be treated as bytes. It must not be
13/// relied on with untrusted data (it can be constructed from bytes, and internal invariants might
14/// not be invalid). Serializing and deserializing of types that implement this trait is simply
15/// casting of underlying memory. As a result, all the types implementing this trait must not use
16/// implicit padding, unions or anything similar that might make it unsound to access any bits of
17/// the type.
18///
19/// Helper functions are provided to make casting to/from bytes a bit safer than it would otherwise,
20/// but extra care is still needed.
21///
22/// **Do not implement this trait explicitly!** Use `#[derive(TrivialType)]` instead, which will
23/// ensure safety requirements are upheld.
24pub unsafe trait TrivialType
25where
26    Self: Copy + 'static,
27{
28    const SIZE: u32 = size_of::<Self>() as u32;
29    // TODO: Compact metadata without field and struct names
30    /// Data structure metadata in binary form, describing shape and types of the contents, see
31    /// [`IoTypeMetadataKind`] for encoding details.
32    const METADATA: &[u8];
33
34    /// Create a reference to a type, which is represented by provided memory.
35    ///
36    /// Memory must be correctly aligned, or else `None` will be returned, but padding beyond the
37    /// size of the type is allowed.
38    ///
39    /// # Safety
40    /// Input bytes must be previously produced by taking underlying bytes of the same type. Using
41    /// anything else will result in UB.
42    #[inline(always)]
43    unsafe fn from_bytes(bytes: &[u8]) -> Option<&Self> {
44        // SAFETY: For trivial types all bit patterns are valid
45        let (before, slice, _) = unsafe { bytes.align_to::<Self>() };
46
47        before.is_empty().then(|| slice.first()).flatten()
48    }
49
50    /// Create a mutable reference to a type, which is represented by provided memory.
51    ///
52    /// Memory must be correctly aligned, or else `None` will be returned, but padding beyond the
53    /// size of the type is allowed.
54    ///
55    /// # Safety
56    /// Input bytes must be previously produced by taking underlying bytes of the same type. Using
57    /// anything else will result in UB.
58    #[inline(always)]
59    unsafe fn from_bytes_mut(bytes: &mut [u8]) -> Option<&mut Self> {
60        // SAFETY: For trivial types all bit patterns are valid
61        let (before, slice, _) = unsafe { bytes.align_to_mut::<Self>() };
62
63        before.is_empty().then(|| slice.first_mut()).flatten()
64    }
65
66    /// Access the underlying byte representation of a data structure
67    #[inline(always)]
68    fn as_bytes(&self) -> &[u8; size_of::<Self>()] {
69        // SAFETY: All bits are valid for reading as bytes, see `TrivialType` description
70        unsafe { ptr::from_ref(self).cast::<[u8; _]>().as_ref_unchecked() }
71    }
72
73    /// Access the underlying mutable byte representation of a data structure.
74    ///
75    /// # Safety
76    /// While calling this function is technically safe, modifying returned memory buffer may result
77    /// in broken invariants of underlying data structure and should be done with extra care.
78    #[inline(always)]
79    unsafe fn as_bytes_mut(&mut self) -> &mut [u8; size_of::<Self>()] {
80        // SAFETY: All bits are valid for reading as bytes, see `TrivialType` description
81        unsafe { ptr::from_mut(self).cast::<[u8; _]>().as_mut_unchecked() }
82    }
83}
84
85// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
86unsafe impl TrivialType for () {
87    const METADATA: &[u8] = &[IoTypeMetadataKind::Unit as u8];
88}
89// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
90unsafe impl TrivialType for u8 {
91    const METADATA: &[u8] = &[IoTypeMetadataKind::U8 as u8];
92}
93// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
94unsafe impl TrivialType for u16 {
95    const METADATA: &[u8] = &[IoTypeMetadataKind::U16 as u8];
96}
97// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
98unsafe impl TrivialType for u32 {
99    const METADATA: &[u8] = &[IoTypeMetadataKind::U32 as u8];
100}
101// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
102unsafe impl TrivialType for u64 {
103    const METADATA: &[u8] = &[IoTypeMetadataKind::U64 as u8];
104}
105// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
106unsafe impl TrivialType for u128 {
107    const METADATA: &[u8] = &[IoTypeMetadataKind::U128 as u8];
108}
109// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
110unsafe impl TrivialType for i8 {
111    const METADATA: &[u8] = &[IoTypeMetadataKind::I8 as u8];
112}
113// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
114unsafe impl TrivialType for i16 {
115    const METADATA: &[u8] = &[IoTypeMetadataKind::I16 as u8];
116}
117// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
118unsafe impl TrivialType for i32 {
119    const METADATA: &[u8] = &[IoTypeMetadataKind::I32 as u8];
120}
121// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
122unsafe impl TrivialType for i64 {
123    const METADATA: &[u8] = &[IoTypeMetadataKind::I64 as u8];
124}
125// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
126unsafe impl TrivialType for i128 {
127    const METADATA: &[u8] = &[IoTypeMetadataKind::I128 as u8];
128}
129
130const fn array_metadata(size: u32, inner_metadata: &[u8]) -> ([u8; MAX_METADATA_CAPACITY], usize) {
131    if inner_metadata.len() == 1 && inner_metadata[0] == IoTypeMetadataKind::U8 as u8 {
132        if size == 8 {
133            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x8 as u8]]);
134        } else if size == 16 {
135            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x16 as u8]]);
136        } else if size == 32 {
137            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x32 as u8]]);
138        } else if size == 64 {
139            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x64 as u8]]);
140        } else if size == 128 {
141            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x128 as u8]]);
142        } else if size == 256 {
143            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x256 as u8]]);
144        } else if size == 512 {
145            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x512 as u8]]);
146        } else if size == 1024 {
147            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x1024 as u8]]);
148        } else if size == 2028 {
149            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x2028 as u8]]);
150        } else if size == 4096 {
151            return concat_metadata_sources(&[&[IoTypeMetadataKind::ArrayU8x4096 as u8]]);
152        }
153    }
154
155    let (io_type, size_bytes) = if size < 2u32.pow(8) {
156        (IoTypeMetadataKind::Array8b, 1)
157    } else if size < 2u32.pow(16) {
158        (IoTypeMetadataKind::Array16b, 2)
159    } else {
160        (IoTypeMetadataKind::Array32b, 4)
161    };
162
163    concat_metadata_sources(&[
164        &[io_type as u8],
165        size.to_le_bytes().split_at(size_bytes).0,
166        inner_metadata,
167    ])
168}
169
170// TODO: Change `usize` to `u32` once stabilized `generic_const_exprs` feature allows us to do
171//  `SIZE as usize`
172// SAFETY: Any bit pattern is valid, so it is safe to implement `TrivialType` for this type
173unsafe impl<const SIZE: usize, T> TrivialType for [T; SIZE]
174where
175    T: TrivialType,
176{
177    const METADATA: &[u8] = {
178        // Strange syntax to allow Rust to extend the lifetime of metadata scratch automatically
179        array_metadata(SIZE as u32, T::METADATA)
180            .0
181            .split_at(array_metadata(SIZE as u32, T::METADATA).1)
182            .0
183    };
184}
185
186// SAFETY: All `TrivialType` instances are valid for `IoType`
187unsafe impl<T> IoType for T
188where
189    T: TrivialType,
190{
191    const METADATA: &[u8] = T::METADATA;
192
193    type PointerType = T;
194
195    #[inline(always)]
196    fn size(&self) -> u32 {
197        size_of::<T>() as u32
198    }
199
200    #[inline(always)]
201    unsafe fn size_ptr(&self) -> impl Deref<Target = NonNull<u32>> {
202        DerefWrapper(NonNull::from_ref(&T::SIZE))
203    }
204
205    #[inline(always)]
206    unsafe fn size_mut_ptr(&mut self) -> impl DerefMut<Target = *mut u32> {
207        DerefWrapper(ptr::null_mut())
208    }
209
210    #[inline(always)]
211    fn capacity(&self) -> u32 {
212        self.size()
213    }
214
215    #[inline(always)]
216    unsafe fn capacity_ptr(&self) -> impl Deref<Target = NonNull<u32>> {
217        DerefWrapper(NonNull::from_ref(&Self::SIZE))
218    }
219
220    #[inline(always)]
221    #[track_caller]
222    unsafe fn set_size(&mut self, size: u32) {
223        debug_assert_eq!(size, T::SIZE, "`set_size` called with invalid input");
224    }
225
226    #[inline(always)]
227    #[track_caller]
228    unsafe fn from_ptr<'a>(
229        ptr: &'a NonNull<Self::PointerType>,
230        size: &'a u32,
231        capacity: u32,
232    ) -> impl Deref<Target = Self> + 'a {
233        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
234        debug_assert_eq!(*size, T::SIZE, "Invalid size");
235        debug_assert!(
236            *size <= capacity,
237            "Size {size} must not exceed capacity {capacity}"
238        );
239
240        // SAFETY: guaranteed by this function signature
241        unsafe { ptr.as_ref() }
242    }
243
244    #[inline(always)]
245    #[track_caller]
246    unsafe fn from_mut_ptr<'a>(
247        ptr: &'a mut NonNull<Self::PointerType>,
248        _size: &'a mut *mut u32,
249        capacity: u32,
250    ) -> impl DerefMut<Target = Self> + 'a {
251        debug_assert!(ptr.is_aligned(), "Misaligned pointer");
252        debug_assert!(
253            Self::SIZE <= capacity,
254            "Size {} must not exceed capacity {capacity}",
255            Self::SIZE
256        );
257
258        // SAFETY: guaranteed by this function signature
259        unsafe { ptr.as_mut() }
260    }
261
262    #[inline(always)]
263    unsafe fn as_ptr(&self) -> impl Deref<Target = NonNull<Self::PointerType>> {
264        DerefWrapper(NonNull::from_ref(self))
265    }
266
267    #[inline(always)]
268    unsafe fn as_mut_ptr(&mut self) -> impl DerefMut<Target = NonNull<Self::PointerType>> {
269        DerefWrapper(NonNull::from_mut(self))
270    }
271}