use bevy_math::{Mat4, Vec2, Vec3, Vec4}; pub use bevy_derive::Bytes; /// Converts the implementing type to bytes by writing them to a given buffer pub trait Bytes { /// Converts the implementing type to bytes by writing them to a given buffer fn write_bytes(&self, buffer: &mut [u8]); /// The number of bytes that will be written when calling `write_bytes` fn byte_len(&self) -> usize; } /// A trait that indicates that it is safe to cast the type to a byte array reference. pub unsafe trait Byteable where Self: Sized, { } impl Bytes for T where T: Byteable, { fn write_bytes(&self, buffer: &mut [u8]) { let bytes = self.as_bytes(); buffer[0..self.byte_len()].copy_from_slice(bytes) } fn byte_len(&self) -> usize { std::mem::size_of::() } } /// Reads the implementing type as a byte array reference pub trait AsBytes { /// Reads the implementing type as a byte array reference fn as_bytes(&self) -> &[u8]; } /// Converts a byte array to `Self` pub trait FromBytes { /// Converts a byte array to `Self` fn from_bytes(bytes: &[u8]) -> Self; } impl FromBytes for T where T: Byteable + Clone, { fn from_bytes(bytes: &[u8]) -> Self { unsafe { let byte_ptr = bytes.as_ptr(); let ptr = byte_ptr as *const Self; (*ptr).clone() } } } impl AsBytes for T where T: Byteable, { fn as_bytes(&self) -> &[u8] { let len = std::mem::size_of_val(self); unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) } } } impl<'a, T> AsBytes for [T] where T: Byteable, { fn as_bytes(&self) -> &[u8] { let len = std::mem::size_of_val(self); unsafe { core::slice::from_raw_parts(self as *const Self as *const u8, len) } } } unsafe impl Byteable for [T] where Self: Sized, T: Byteable, { } unsafe impl Byteable for [T; 2] where T: Byteable {} unsafe impl Byteable for [T; 3] where T: Byteable {} unsafe impl Byteable for [T; 4] where T: Byteable {} unsafe impl Byteable for [T; 16] where T: Byteable {} unsafe impl Byteable for u8 {} unsafe impl Byteable for u16 {} unsafe impl Byteable for u32 {} unsafe impl Byteable for u64 {} unsafe impl Byteable for usize {} unsafe impl Byteable for i8 {} unsafe impl Byteable for i16 {} unsafe impl Byteable for i32 {} unsafe impl Byteable for i64 {} unsafe impl Byteable for isize {} unsafe impl Byteable for f32 {} unsafe impl Byteable for f64 {} unsafe impl Byteable for Vec2 {} // NOTE: Vec3 actually takes up the size of 4 floats / 16 bytes due to SIMD. This is actually convenient because GLSL // uniform buffer objects pad Vec3s to be 16 bytes. unsafe impl Byteable for Vec3 {} unsafe impl Byteable for Vec4 {} impl Bytes for Mat4 { fn write_bytes(&self, buffer: &mut [u8]) { let array = self.to_cols_array(); array.write_bytes(buffer); } fn byte_len(&self) -> usize { std::mem::size_of::() } } impl FromBytes for Mat4 { fn from_bytes(bytes: &[u8]) -> Self { let array = <[f32; 16]>::from_bytes(bytes); Mat4::from_cols_array(&array) } } impl Bytes for Option where T: Bytes, { fn write_bytes(&self, buffer: &mut [u8]) { if let Some(val) = self { val.write_bytes(buffer) } } fn byte_len(&self) -> usize { self.as_ref().map_or(0, |val| val.byte_len()) } } impl FromBytes for Option where T: FromBytes, { fn from_bytes(bytes: &[u8]) -> Self { if bytes.is_empty() { None } else { Some(T::from_bytes(bytes)) } } } impl Bytes for Vec where T: Sized + Byteable, { fn write_bytes(&self, buffer: &mut [u8]) { let bytes = self.as_slice().as_bytes(); buffer[0..self.byte_len()].copy_from_slice(bytes) } fn byte_len(&self) -> usize { self.as_slice().as_bytes().len() } } impl FromBytes for Vec where T: Sized + Clone + Byteable, { fn from_bytes(bytes: &[u8]) -> Self { unsafe { let byte_ptr = bytes.as_ptr() as *const T; let len = bytes.len() / std::mem::size_of::(); let slice = core::slice::from_raw_parts::(byte_ptr, len); slice.to_vec() } } } #[cfg(test)] mod tests { use super::{Bytes, FromBytes}; use bevy_math::{Mat4, Vec2, Vec3, Vec4}; fn test_round_trip(value: T) { let mut bytes = vec![0; value.byte_len()]; value.write_bytes(&mut bytes); let result = T::from_bytes(&bytes); assert_eq!(value, result); } #[test] fn test_u32_bytes_round_trip() { test_round_trip(123u32); } #[test] fn test_f64_bytes_round_trip() { test_round_trip(123f64); } #[test] fn test_vec_bytes_round_trip() { test_round_trip(vec![1u32, 2u32, 3u32]); } #[test] fn test_option_bytes_round_trip() { test_round_trip(Some(123u32)); test_round_trip(Option::::None); } #[test] fn test_vec2_round_trip() { test_round_trip(Vec2::new(1.0, 2.0)); } #[test] fn test_vec3_round_trip() { test_round_trip(Vec3::new(1.0, 2.0, 3.0)); } #[test] fn test_vec4_round_trip() { test_round_trip(Vec4::new(1.0, 2.0, 3.0, 4.0)); } #[test] fn test_mat4_round_trip() { test_round_trip(Mat4::identity()); } }