use super::endian::FileEndian;
use crate::mode::{Float32Complex, Int16Complex};
#[doc(hidden)]
pub trait EndianCodec: Sized {
const BYTE_SIZE: usize;
fn from_bytes(bytes: &[u8], offset: usize, endian: FileEndian) -> Self;
fn to_bytes(&self, bytes: &mut [u8], offset: usize, endian: FileEndian);
#[inline]
fn decode(bytes: &[u8], offset: usize, endian: FileEndian) -> Self {
Self::from_bytes(bytes, offset, endian)
}
#[inline]
fn encode(&self, bytes: &mut [u8], offset: usize, endian: FileEndian) {
self.to_bytes(bytes, offset, endian)
}
}
macro_rules! impl_endian_codec {
($ty:ty, $size:literal) => {
impl EndianCodec for $ty {
const BYTE_SIZE: usize = $size;
#[inline]
fn from_bytes(bytes: &[u8], offset: usize, endian: FileEndian) -> Self {
let mut arr = [0u8; $size];
arr.copy_from_slice(&bytes[offset..offset + $size]);
match endian {
FileEndian::LittleEndian => Self::from_le_bytes(arr),
FileEndian::BigEndian => Self::from_be_bytes(arr),
}
}
#[inline]
fn to_bytes(&self, bytes: &mut [u8], offset: usize, endian: FileEndian) {
let arr = match endian {
FileEndian::LittleEndian => self.to_le_bytes(),
FileEndian::BigEndian => self.to_be_bytes(),
};
bytes[offset..offset + $size].copy_from_slice(&arr);
}
}
};
}
impl_endian_codec!(i16, 2);
impl_endian_codec!(u16, 2);
impl_endian_codec!(i32, 4);
impl_endian_codec!(f32, 4);
impl_endian_codec!(u32, 4);
impl_endian_codec!(i64, 8);
impl_endian_codec!(f64, 8);
impl EndianCodec for i8 {
const BYTE_SIZE: usize = 1;
#[inline]
fn from_bytes(bytes: &[u8], offset: usize, _endian: FileEndian) -> Self {
bytes[offset] as Self
}
#[inline]
fn to_bytes(&self, bytes: &mut [u8], offset: usize, _endian: FileEndian) {
bytes[offset] = *self as u8;
}
}
impl EndianCodec for Int16Complex {
const BYTE_SIZE: usize = 4;
#[inline]
fn from_bytes(bytes: &[u8], offset: usize, endian: FileEndian) -> Self {
Self {
real: i16::from_bytes(bytes, offset, endian),
imag: i16::from_bytes(bytes, offset + 2, endian),
}
}
#[inline]
fn to_bytes(&self, bytes: &mut [u8], offset: usize, endian: FileEndian) {
self.real.to_bytes(bytes, offset, endian);
self.imag.to_bytes(bytes, offset + 2, endian);
}
}
impl EndianCodec for Float32Complex {
const BYTE_SIZE: usize = 8;
#[inline]
fn from_bytes(bytes: &[u8], offset: usize, endian: FileEndian) -> Self {
Self {
real: f32::from_bytes(bytes, offset, endian),
imag: f32::from_bytes(bytes, offset + 4, endian),
}
}
#[inline]
fn to_bytes(&self, bytes: &mut [u8], offset: usize, endian: FileEndian) {
self.real.to_bytes(bytes, offset, endian);
self.imag.to_bytes(bytes, offset + 4, endian);
}
}
#[cfg(feature = "f16")]
impl EndianCodec for crate::f16 {
const BYTE_SIZE: usize = 2;
#[inline]
fn from_bytes(bytes: &[u8], offset: usize, endian: FileEndian) -> Self {
let arr: [u8; 2] = [bytes[offset], bytes[offset + 1]];
let bits = match endian {
FileEndian::LittleEndian => u16::from_le_bytes(arr),
FileEndian::BigEndian => u16::from_be_bytes(arr),
};
Self::from_bits(bits)
}
#[inline]
fn to_bytes(&self, bytes: &mut [u8], offset: usize, endian: FileEndian) {
let bits = self.to_bits();
let arr = match endian {
FileEndian::LittleEndian => bits.to_le_bytes(),
FileEndian::BigEndian => bits.to_be_bytes(),
};
bytes[offset..offset + 2].copy_from_slice(&arr);
}
}
pub fn decode_into<T: EndianCodec + Copy>(
bytes: &[u8],
values: &mut [T],
endian: FileEndian,
) -> Result<(), crate::Error> {
let expected = values
.len()
.checked_mul(T::BYTE_SIZE)
.ok_or(crate::Error::TypeMismatch {
expected: 0,
actual: bytes.len(),
})?;
if bytes.len() != expected {
return Err(crate::Error::TypeMismatch {
expected,
actual: bytes.len(),
});
}
if endian == FileEndian::native() {
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
values.as_mut_ptr() as *mut u8,
bytes.len(),
);
}
return Ok(());
}
let dst_bytes =
unsafe { core::slice::from_raw_parts_mut(values.as_mut_ptr() as *mut u8, bytes.len()) };
swap_bytes_by_size::<T>(bytes, dst_bytes);
Ok(())
}
fn swap_bytes_by_size<T: EndianCodec>(src: &[u8], dst: &mut [u8]) {
match T::BYTE_SIZE {
1 => dst.copy_from_slice(src),
2 => {
#[cfg(feature = "simd")]
crate::engine::simd::swap_2byte_simd(src, dst);
#[cfg(not(feature = "simd"))]
for (d, s) in dst.chunks_exact_mut(2).zip(src.chunks_exact(2)) {
d[0] = s[1];
d[1] = s[0];
}
}
4 => {
#[cfg(feature = "simd")]
crate::engine::simd::swap_4byte_simd(src, dst);
#[cfg(not(feature = "simd"))]
for (d, s) in dst.chunks_exact_mut(4).zip(src.chunks_exact(4)) {
d[0] = s[3];
d[1] = s[2];
d[2] = s[1];
d[3] = s[0];
}
}
8 => {
#[cfg(feature = "simd")]
crate::engine::simd::swap_8byte_simd(src, dst);
#[cfg(not(feature = "simd"))]
for (d, s) in dst.chunks_exact_mut(8).zip(src.chunks_exact(8)) {
d[0] = s[7];
d[1] = s[6];
d[2] = s[5];
d[3] = s[4];
d[4] = s[3];
d[5] = s[2];
d[6] = s[1];
d[7] = s[0];
}
}
_ => unreachable!(),
}
}
pub fn decode_slice<T: EndianCodec + Send + Copy>(
bytes: &[u8],
endian: FileEndian,
) -> Result<Vec<T>, crate::Error> {
if bytes.len() % T::BYTE_SIZE != 0 {
return Err(crate::Error::TypeMismatch {
expected: T::BYTE_SIZE,
actual: bytes.len(),
});
}
let n = bytes.len() / T::BYTE_SIZE;
let mut result = Vec::with_capacity(n);
if endian == FileEndian::native() {
unsafe {
core::ptr::copy_nonoverlapping(
bytes.as_ptr(),
result.as_mut_ptr() as *mut u8,
bytes.len(),
);
result.set_len(n);
}
return Ok(result);
}
let dst_bytes =
unsafe { std::slice::from_raw_parts_mut(result.as_mut_ptr() as *mut u8, bytes.len()) };
#[cfg(feature = "parallel")]
if n >= PAR_MIN_VOXELS {
use rayon::prelude::*;
let chunk_voxels = PAR_MIN_VOXELS;
let chunk_bytes = chunk_voxels * T::BYTE_SIZE;
dst_bytes
.par_chunks_mut(chunk_bytes)
.zip(bytes.par_chunks(chunk_bytes))
.for_each(|(dst, src)| swap_bytes_by_size::<T>(src, dst));
} else {
swap_bytes_by_size::<T>(bytes, dst_bytes);
}
#[cfg(not(feature = "parallel"))]
swap_bytes_by_size::<T>(bytes, dst_bytes);
unsafe {
result.set_len(n);
}
Ok(result)
}
pub fn encode_slice<T: EndianCodec + Sync>(
values: &[T],
bytes: &mut [u8],
endian: FileEndian,
) -> Result<(), crate::Error> {
if values.len().checked_mul(T::BYTE_SIZE) != Some(bytes.len()) {
return Err(crate::Error::TypeMismatch {
expected: values.len() * T::BYTE_SIZE,
actual: bytes.len(),
});
}
if endian == FileEndian::native() {
unsafe {
core::ptr::copy_nonoverlapping(
values.as_ptr() as *const u8,
bytes.as_mut_ptr(),
bytes.len(),
);
}
return Ok(());
}
let src = unsafe { core::slice::from_raw_parts(values.as_ptr() as *const u8, bytes.len()) };
swap_bytes_by_size::<T>(src, bytes);
Ok(())
}
#[cfg(feature = "parallel")]
pub(crate) const PAR_MIN_VOXELS: usize = 262_144;
pub fn swap_bytes_in_place<T: crate::Voxel>(data: &mut [T], from: FileEndian) {
if from == FileEndian::native() || data.is_empty() {
return;
}
let byte_len = data.len() * T::BYTE_SIZE;
let bytes = unsafe { core::slice::from_raw_parts_mut(data.as_mut_ptr() as *mut u8, byte_len) };
for chunk in bytes.chunks_exact_mut(T::BYTE_SIZE) {
chunk.reverse();
}
}
#[cfg(feature = "parallel")]
pub fn encode_block_parallel<T: EndianCodec + Sync>(
values: &[T],
endian: FileEndian,
) -> Result<Vec<u8>, crate::Error> {
use rayon::prelude::*;
let byte_len = values.len() * T::BYTE_SIZE;
let mut buffer = vec![0u8; byte_len];
let chunk_voxels = 262_144;
buffer
.par_chunks_mut(chunk_voxels * T::BYTE_SIZE)
.zip(values.par_chunks(chunk_voxels))
.try_for_each(|(dst, chunk)| encode_slice(chunk, dst, endian))?;
Ok(buffer)
}