228 lines
4.9 KiB
Rust
228 lines
4.9 KiB
Rust
use bevy_math::{Mat4, Vec2, Vec3, Vec4};
|
|
|
|
pub use bevy_derive::Bytes;
|
|
|
|
pub trait Bytes {
|
|
fn write_bytes(&self, buffer: &mut [u8]);
|
|
fn byte_len(&self) -> usize;
|
|
}
|
|
|
|
pub unsafe trait Byteable
|
|
where
|
|
Self: Sized,
|
|
{
|
|
}
|
|
|
|
impl<T> 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::<Self>()
|
|
}
|
|
}
|
|
|
|
pub trait AsBytes {
|
|
fn as_bytes(&self) -> &[u8];
|
|
}
|
|
|
|
pub trait FromBytes {
|
|
fn from_bytes(bytes: &[u8]) -> Self;
|
|
}
|
|
|
|
impl<T> 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<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) }
|
|
}
|
|
}
|
|
|
|
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<T> Byteable for [T]
|
|
where
|
|
Self: Sized,
|
|
T: Byteable,
|
|
{
|
|
}
|
|
unsafe impl<T> Byteable for [T; 2] where T: Byteable {}
|
|
unsafe impl<T> Byteable for [T; 3] where T: Byteable {}
|
|
unsafe impl<T> Byteable for [T; 4] where T: Byteable {}
|
|
unsafe impl<T> 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::<Self>()
|
|
}
|
|
}
|
|
|
|
impl FromBytes for Mat4 {
|
|
fn from_bytes(bytes: &[u8]) -> Self {
|
|
let array = <[f32; 16]>::from_bytes(bytes);
|
|
Mat4::from_cols_array(&array)
|
|
}
|
|
}
|
|
|
|
impl<T> Bytes for Option<T>
|
|
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<T> FromBytes for Option<T>
|
|
where
|
|
T: FromBytes,
|
|
{
|
|
fn from_bytes(bytes: &[u8]) -> Self {
|
|
if bytes.len() == 0 {
|
|
None
|
|
} else {
|
|
Some(T::from_bytes(bytes))
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<T> Bytes for Vec<T>
|
|
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<T> FromBytes for Vec<T>
|
|
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::<T>();
|
|
let slice = core::slice::from_raw_parts::<T>(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<T: Bytes + FromBytes + std::fmt::Debug + PartialEq>(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::<u32>::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());
|
|
}
|
|
}
|