Skip to main content

ab_io_type/
trivial_type.rs

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