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