bytesagent/
lib.rs

1#![allow(clippy::size_of_in_element_count)] // Clippy miss-detects this in the macros
2#![warn(clippy::pedantic)]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5//! A small crate to cast to and from arbitrary bytes
6
7#[cfg(feature = "glam")]
8use glam::{
9    DMat2, DMat3, DMat4, DQuat, DVec2, DVec3, DVec4, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat,
10    UVec2, UVec3, UVec4, Vec2, Vec3, Vec4,
11};
12#[cfg(feature = "half")]
13use half::{bf16, f16};
14
15#[derive(Debug)]
16pub enum Error {
17    Size,
18}
19
20#[cfg(feature = "std")]
21impl std::error::Error for Error {}
22impl core::fmt::Display for Error {
23    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
24        match self {
25            Error::Size => write!(f, ""),
26        }
27    }
28}
29
30pub type Result<T, E = Error> = core::result::Result<T, E>;
31
32/// # Safety
33/// Implementors must ensure that the type is nothing more than a sequence of bytes
34pub unsafe trait Pod {
35    fn as_bytes(&self) -> &[u8];
36    fn as_bytes_mut(&mut self) -> &mut [u8];
37    /// # Errors
38    /// TODO
39    fn from_bytes(bytes: &[u8]) -> Result<&Self>
40    where
41        Self: Sized,
42    {
43        if bytes.len() == core::mem::size_of::<Self>() {
44            Ok(unsafe { Self::from_bytes_unchecked(bytes) })
45        } else {
46            Err(Error::Size)
47        }
48    }
49    /// # Safety
50    /// TODO
51    unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self
52    where
53        Self: Sized,
54    {
55        &*bytes.as_ptr().cast::<Self>()
56    }
57    /// # Errors
58    /// TODO
59    fn from_bytes_mut(bytes: &mut [u8]) -> Result<&mut Self>
60    where
61        Self: Sized,
62    {
63        if bytes.len() == core::mem::size_of::<Self>() {
64            Ok(unsafe { Self::from_bytes_mut_unchecked(bytes) })
65        } else {
66            Err(Error::Size)
67        }
68    }
69
70    /// # Safety
71    /// TODO
72    unsafe fn from_bytes_mut_unchecked(bytes: &mut [u8]) -> &mut Self
73    where
74        Self: Sized,
75    {
76        &mut *bytes.as_mut_ptr().cast::<Self>()
77    }
78}
79
80macro_rules! impl_pod {
81    ($($ty:ty)+) => {
82        $(
83        // Plain types can be simply asked to the correct memory representation.
84        unsafe impl Pod for $ty {
85            fn as_bytes(&self) -> &[u8] {
86                unsafe { &*(self as *const $ty).cast::<[u8; 4]>() }
87            }
88            fn as_bytes_mut(&mut self) -> &mut [u8] {
89                unsafe { &mut*(self as *mut $ty).cast::<[u8; 4]>() }
90            }
91        }
92        // Unfortunately until const generics get stabilized we cannot do the above with arrays.
93        // This however is a non issue: the final asm (at least in release mode) is the same.
94        unsafe impl<const N: usize> Pod for [$ty; N] {
95            fn as_bytes(&self) -> &[u8] {
96                unsafe {
97                    core::slice::from_raw_parts(
98                        self.as_ptr().cast(),
99                        self.len() * core::mem::size_of::<$ty>(),
100                    )
101                }
102            }
103            fn as_bytes_mut(&mut self) -> &mut [u8] {
104                unsafe {
105                    core::slice::from_raw_parts_mut(
106                        self.as_mut_ptr().cast(),
107                        self.len() * core::mem::size_of::<$ty>(),
108                    )
109                }
110            }
111        }
112        )*
113    }
114}
115
116// Implement for all the primitive types which are "plain old data types" so are just repprasented as a series of bytes in memory.
117impl_pod!(u8 u16 u32 u64 u128 usize i8 i16 i32 i64 i128 isize  f32 f64);
118#[cfg(feature = "half")]
119impl_pod!(f16 bf16);
120// This types from glam are also represented in memory by a series of bytes so this is also fine.
121#[cfg(feature = "glam")]
122impl_pod!(DMat2 DMat3 DMat4 DQuat DVec2 DVec3 DVec4 IVec2 IVec3 IVec4 Mat2 Mat3 Mat4 Quat UVec2 UVec3 UVec4 Vec2 Vec3 Vec4);