use crate::Voxel;
use crate::mode::M0Interpretation;
use std::vec::Vec;
unsafe fn reinterpret_vec<S, T>(v: Vec<S>) -> Vec<T> {
debug_assert_eq!(core::mem::size_of::<S>(), core::mem::size_of::<T>());
debug_assert_eq!(core::mem::align_of::<S>(), core::mem::align_of::<T>());
let ptr = v.as_ptr() as *mut T;
let len = v.len();
let cap = v.capacity();
core::mem::forget(v);
unsafe { Vec::from_raw_parts(ptr, len, cap) }
}
#[cfg(feature = "simd")]
use super::simd;
use super::codec::decode_slice;
use super::endian::FileEndian;
use crate::Error;
use crate::mode::{ComplexToRealStrategy, Float32Complex, Int16Complex, Mode};
pub trait ConvertFrom<Src: Voxel>: Voxel {
fn convert_from(src: &[Src]) -> Vec<Self>;
}
impl<T: Voxel> ConvertFrom<T> for T {
fn convert_from(src: &[T]) -> Vec<T> {
src.to_vec()
}
}
impl ConvertFrom<i16> for f32 {
fn convert_from(src: &[i16]) -> Vec<f32> {
convert_i16_slice_to_f32(src)
}
}
impl ConvertFrom<i8> for f32 {
fn convert_from(src: &[i8]) -> Vec<f32> {
convert_i8_slice_to_f32(src)
}
}
impl ConvertFrom<u16> for f32 {
fn convert_from(src: &[u16]) -> Vec<f32> {
convert_u16_slice_to_f32(src)
}
}
#[cfg(feature = "f16")]
impl ConvertFrom<crate::f16> for f32 {
fn convert_from(src: &[crate::f16]) -> Vec<f32> {
convert_f16_slice_to_f32(src)
}
}
#[cfg(feature = "f16")]
impl ConvertFrom<f32> for crate::f16 {
fn convert_from(src: &[f32]) -> Vec<crate::f16> {
convert_f32_slice_to_f16(src)
}
}
impl ConvertFrom<f32> for i16 {
fn convert_from(src: &[f32]) -> Vec<i16> {
convert_f32_slice_to_i16(src)
}
}
impl ConvertFrom<f32> for u16 {
fn convert_from(src: &[f32]) -> Vec<u16> {
convert_f32_slice_to_u16(src)
}
}
impl ConvertFrom<f32> for i8 {
fn convert_from(src: &[f32]) -> Vec<i8> {
convert_f32_slice_to_i8(src)
}
}
pub(crate) fn unpack_u4_bytes_to_u8(src: &[u8], nx: usize, ny: usize) -> Vec<u8> {
let row_bytes = nx.div_ceil(2);
let mut dst = Vec::with_capacity(nx * ny);
for y in 0..ny {
let row_start = y * row_bytes;
for x in 0..nx {
let byte = src[row_start + x / 2];
let nibble = if x % 2 == 0 {
byte & 0x0F
} else {
(byte >> 4) & 0x0F
};
dst.push(nibble);
}
}
dst
}
pub(crate) fn pack_u8_to_u4_bytes(src: &[u8], nx: usize, ny: usize) -> Vec<u8> {
let row_bytes = nx.div_ceil(2);
let mut dst = vec![0u8; row_bytes * ny];
for y in 0..ny {
let row_start = y * row_bytes;
for x in 0..nx {
let val = src[y * nx + x] & 0x0F;
let byte_idx = row_start + x / 2;
if x % 2 == 0 {
dst[byte_idx] = val;
} else {
dst[byte_idx] |= val << 4;
}
}
}
dst
}
pub fn reinterpret_m0(data: &[u8], interp: M0Interpretation) -> Vec<f32> {
match interp {
M0Interpretation::Signed => {
let src: &[i8] =
unsafe { core::slice::from_raw_parts(data.as_ptr() as *const i8, data.len()) };
convert_i8_slice_to_f32(src)
}
M0Interpretation::Unsigned => convert_u8_slice_to_f32(data),
}
}
#[cfg(feature = "simd")]
pub(crate) fn convert_i8_slice_to_f32(src: &[i8]) -> Vec<f32> {
simd::convert_i8_to_f32_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_i8_slice_to_f32(src: &[i8]) -> Vec<f32> {
src.iter().map(|&x| x as f32).collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_i16_slice_to_f32(src: &[i16]) -> Vec<f32> {
simd::convert_i16_to_f32_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_i16_slice_to_f32(src: &[i16]) -> Vec<f32> {
src.iter().map(|&x| x as f32).collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_u16_slice_to_f32(src: &[u16]) -> Vec<f32> {
simd::convert_u16_to_f32_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_u16_slice_to_f32(src: &[u16]) -> Vec<f32> {
src.iter().map(|&x| x as f32).collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_u8_slice_to_f32(src: &[u8]) -> Vec<f32> {
simd::convert_u8_to_f32_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_u8_slice_to_f32(src: &[u8]) -> Vec<f32> {
src.iter().map(|&x| x as f32).collect()
}
#[cfg(all(feature = "simd", feature = "f16"))]
pub(crate) fn convert_f16_slice_to_f32(src: &[crate::f16]) -> Vec<f32> {
simd::convert_f16_to_f32_simd(src)
}
#[cfg(all(feature = "f16", not(feature = "simd")))]
pub(crate) fn convert_f16_slice_to_f32(src: &[crate::f16]) -> Vec<f32> {
src.iter().map(|&v| f32::from(v)).collect()
}
#[cfg(all(feature = "simd", feature = "f16"))]
pub(crate) fn convert_f32_slice_to_f16(src: &[f32]) -> Vec<crate::f16> {
simd::convert_f32_to_f16_simd(src)
}
#[cfg(all(feature = "f16", not(feature = "simd")))]
pub(crate) fn convert_f32_slice_to_f16(src: &[f32]) -> Vec<crate::f16> {
src.iter().map(|&v| crate::f16::from_f32(v)).collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_f32_slice_to_i16(src: &[f32]) -> Vec<i16> {
simd::convert_f32_to_i16_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_f32_slice_to_i16(src: &[f32]) -> Vec<i16> {
src.iter()
.map(|&v| {
if v >= i16::MAX as f32 {
i16::MAX
} else if v <= i16::MIN as f32 {
i16::MIN
} else {
v as i16
}
})
.collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_f32_slice_to_u16(src: &[f32]) -> Vec<u16> {
simd::convert_f32_to_u16_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_f32_slice_to_u16(src: &[f32]) -> Vec<u16> {
src.iter()
.map(|&v| {
if v >= u16::MAX as f32 {
u16::MAX
} else if v <= 0.0 {
0
} else {
v as u16
}
})
.collect()
}
#[cfg(feature = "simd")]
pub(crate) fn convert_f32_slice_to_i8(src: &[f32]) -> Vec<i8> {
simd::convert_f32_to_i8_simd(src)
}
#[cfg(not(feature = "simd"))]
pub(crate) fn convert_f32_slice_to_i8(src: &[f32]) -> Vec<i8> {
src.iter()
.map(|&v| {
if v >= i8::MAX as f32 {
i8::MAX
} else if v <= i8::MIN as f32 {
i8::MIN
} else {
v as i8
}
})
.collect()
}
pub(crate) fn convert_i8_slice_to_i16(src: &[i8]) -> Vec<i16> {
src.iter().map(|&v| v as i16).collect()
}
pub(crate) fn convert_i16_slice_to_i8(src: &[i16]) -> Vec<i8> {
src.iter()
.map(|&v| {
if v > i8::MAX as i16 {
i8::MAX
} else if v < i8::MIN as i16 {
i8::MIN
} else {
v as i8
}
})
.collect()
}
pub(crate) fn convert_i8_slice_to_u16(src: &[i8]) -> Vec<u16> {
src.iter().map(|&v| v.max(0) as u16).collect()
}
pub(crate) fn convert_u16_slice_to_i8(src: &[u16]) -> Vec<i8> {
src.iter()
.map(|&v| if v > i8::MAX as u16 { i8::MAX } else { v as i8 })
.collect()
}
pub(crate) fn convert_i16_slice_to_u16(src: &[i16]) -> Vec<u16> {
src.iter().map(|&v| v.max(0) as u16).collect()
}
pub(crate) fn convert_u16_slice_to_i16(src: &[u16]) -> Vec<i16> {
src.iter()
.map(|&v| {
if v > i16::MAX as u16 {
i16::MAX
} else {
v as i16
}
})
.collect()
}
#[allow(clippy::too_many_arguments)]
pub(crate) fn convert_block<T>(
bytes: &[u8],
mode: Mode,
endian: FileEndian,
nx: usize,
ny: usize,
block_shape: [usize; 3],
complex_strategy: ComplexToRealStrategy,
m0_interp: M0Interpretation,
) -> Result<Vec<T>, Error>
where
T: Voxel + ConvertFrom<f32>,
{
{
if mode == Mode::Int16 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<i8>() {
let src = decode_slice::<i16>(bytes, endian)?;
let r = convert_i16_slice_to_i8(&src);
return Ok(unsafe { reinterpret_vec::<i8, T>(r) });
}
if mode == Mode::Int8 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<i16>() {
let src = decode_slice::<i8>(bytes, endian)?;
let r = convert_i8_slice_to_i16(&src);
return Ok(unsafe { reinterpret_vec::<i16, T>(r) });
}
if mode == Mode::Uint16 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<i8>() {
let src = decode_slice::<u16>(bytes, endian)?;
let r = convert_u16_slice_to_i8(&src);
return Ok(unsafe { reinterpret_vec::<i8, T>(r) });
}
if mode == Mode::Int8 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<u16>() {
let src = decode_slice::<i8>(bytes, endian)?;
let r = convert_i8_slice_to_u16(&src);
return Ok(unsafe { reinterpret_vec::<u16, T>(r) });
}
if mode == Mode::Uint16 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<i16>() {
let src = decode_slice::<u16>(bytes, endian)?;
let r = convert_u16_slice_to_i16(&src);
return Ok(unsafe { reinterpret_vec::<i16, T>(r) });
}
if mode == Mode::Int16 && core::any::TypeId::of::<T>() == core::any::TypeId::of::<u16>() {
let src = decode_slice::<i16>(bytes, endian)?;
let r = convert_i16_slice_to_u16(&src);
return Ok(unsafe { reinterpret_vec::<u16, T>(r) });
}
}
let f32_data = match mode {
Mode::Packed4Bit => {
let sx = block_shape[0];
let total_rows = block_shape[1] * block_shape[2];
let unpacked = unpack_u4_bytes_to_u8(bytes, sx, total_rows);
convert_u8_slice_to_f32(&unpacked)
}
Mode::Float16 => convert_block_float16(bytes, endian)?,
other => convert_block_inner(bytes, other, endian, nx, ny, complex_strategy, m0_interp)?,
};
if core::any::TypeId::of::<T>() == core::any::TypeId::of::<f32>() {
let ptr = f32_data.as_ptr() as *mut T;
let len = f32_data.len();
let cap = f32_data.capacity();
core::mem::forget(f32_data);
Ok(unsafe { Vec::from_raw_parts(ptr, len, cap) })
} else {
Ok(T::convert_from(&f32_data))
}
}
fn convert_block_inner(
bytes: &[u8],
mode: Mode,
endian: FileEndian,
_nx: usize,
_ny: usize,
complex_strategy: ComplexToRealStrategy,
m0_interp: M0Interpretation,
) -> Result<Vec<f32>, Error> {
match mode {
Mode::Int8 => match m0_interp {
M0Interpretation::Signed => {
let src = decode_slice::<i8>(bytes, endian)?;
Ok(convert_i8_slice_to_f32(&src))
}
M0Interpretation::Unsigned => Ok(reinterpret_m0(bytes, M0Interpretation::Unsigned)),
},
Mode::Int16 => {
let src = decode_slice::<i16>(bytes, endian)?;
Ok(convert_i16_slice_to_f32(&src))
}
Mode::Uint16 => {
let src = decode_slice::<u16>(bytes, endian)?;
Ok(convert_u16_slice_to_f32(&src))
}
Mode::Float32 => decode_slice::<f32>(bytes, endian),
Mode::Float32Complex => {
let src = decode_slice::<Float32Complex>(bytes, endian)?;
let mag: Vec<f32> = src.iter().map(|c| c.to_real(complex_strategy)).collect();
Ok(mag)
}
Mode::Int16Complex => {
let src = decode_slice::<Int16Complex>(bytes, endian)?;
let mag: Vec<f32> = src.iter().map(|c| c.to_real(complex_strategy)).collect();
Ok(mag)
}
Mode::Packed4Bit => unreachable!(
"Packed4Bit is dispatched via convert_block before convert_block_inner"
),
Mode::Float16 => unreachable!("Float16 is dispatched via convert_block_float16"),
}
}
#[cfg(feature = "f16")]
fn convert_block_float16(bytes: &[u8], endian: FileEndian) -> Result<Vec<f32>, Error> {
let src = decode_slice::<crate::f16>(bytes, endian)?;
Ok(convert_f16_slice_to_f32(&src))
}
#[cfg(not(feature = "f16"))]
fn convert_block_float16(_bytes: &[u8], _endian: FileEndian) -> Result<Vec<f32>, Error> {
Err(Error::UnsupportedMode)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ComplexToRealStrategy;
#[test]
fn test_convert_i8_slice_to_f32() {
let input: Vec<i8> = vec![-128, -64, 0, 64, 127];
let output = convert_i8_slice_to_f32(&input);
assert_eq!(output.len(), input.len());
for (src, dst) in input.iter().zip(output.iter()) {
assert_eq!(*dst, *src as f32);
}
}
#[test]
fn test_convert_i16_slice_to_f32() {
let input: Vec<i16> = vec![-32768, -1000, 0, 1000, 32767];
let output = convert_i16_slice_to_f32(&input);
assert_eq!(output.len(), input.len());
for (src, dst) in input.iter().zip(output.iter()) {
assert_eq!(*dst, *src as f32);
}
}
#[test]
fn test_convert_u16_slice_to_f32() {
let input: Vec<u16> = vec![0, 1000, 32767, 65535];
let output = convert_u16_slice_to_f32(&input);
assert_eq!(output.len(), input.len());
for (src, dst) in input.iter().zip(output.iter()) {
assert_eq!(*dst, *src as f32);
}
}
#[test]
fn test_convert_empty_slice() {
let input: Vec<i8> = vec![];
let output = convert_i8_slice_to_f32(&input);
assert!(output.is_empty());
}
#[test]
fn test_convert_single_element() {
let input: Vec<i16> = vec![42];
let output = convert_i16_slice_to_f32(&input);
assert_eq!(output.len(), 1);
assert_eq!(output[0], 42.0f32);
}
#[test]
fn test_convert_large_slice() {
let input: Vec<i16> = (0..10000).map(|i| (i % 65536) as i16).collect();
let output = convert_i16_slice_to_f32(&input);
assert_eq!(output.len(), input.len());
for (src, dst) in input.iter().zip(output.iter()) {
assert_eq!(*dst, *src as f32);
}
}
#[test]
#[cfg(feature = "simd")]
fn test_simd_scalar_equivalence_i8() {
let input: Vec<i8> = (-128..=127).collect();
let simd_result = crate::engine::convert::convert_i8_slice_to_f32(&input);
let scalar_result: Vec<f32> = input.iter().map(|&x| x as f32).collect();
assert_eq!(simd_result, scalar_result);
}
#[test]
#[cfg(feature = "simd")]
fn test_simd_scalar_equivalence_i16() {
let input: Vec<i16> = (-32768..=-31768).collect(); let simd_result = crate::engine::convert::convert_i16_slice_to_f32(&input);
let scalar_result: Vec<f32> = input.iter().map(|&x| x as f32).collect();
assert_eq!(simd_result, scalar_result);
}
#[test]
#[cfg(feature = "simd")]
fn test_simd_scalar_equivalence_u16() {
let input: Vec<u16> = (0..10000).collect();
let simd_result = crate::engine::convert::convert_u16_slice_to_f32(&input);
let scalar_result: Vec<f32> = input.iter().map(|&x| x as f32).collect();
assert_eq!(simd_result, scalar_result);
}
#[test]
fn test_unpack_u4_bytes_to_u8_even() {
let bytes = vec![0x21, 0x43];
let result = unpack_u4_bytes_to_u8(&bytes, 4, 1);
assert_eq!(result, vec![1, 2, 3, 4]);
}
#[test]
fn test_unpack_u4_bytes_to_u8_odd() {
let bytes = vec![0x21, 0x30]; let result = unpack_u4_bytes_to_u8(&bytes, 3, 1);
assert_eq!(result, vec![1, 2, 0]);
}
#[test]
fn test_pack_u8_to_u4_bytes_even() {
let values = vec![1, 2, 3, 4];
let packed = pack_u8_to_u4_bytes(&values, 4, 1);
assert_eq!(packed, vec![0x21, 0x43]);
}
#[test]
fn test_pack_u8_to_u4_bytes_odd() {
let values = vec![1, 2, 3];
let packed = pack_u8_to_u4_bytes(&values, 3, 1);
assert_eq!(packed, vec![0x21, 0x03]);
}
#[test]
fn test_pack_unpack_roundtrip() {
let values: Vec<u8> = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let packed = pack_u8_to_u4_bytes(&values, 8, 2);
let unpacked = unpack_u4_bytes_to_u8(&packed, 8, 2);
assert_eq!(unpacked, values);
}
#[test]
fn test_pack_unpack_roundtrip_odd() {
let values: Vec<u8> = vec![1, 2, 3, 4, 5]; let packed = pack_u8_to_u4_bytes(&values, 5, 1);
let unpacked = unpack_u4_bytes_to_u8(&packed, 5, 1);
assert_eq!(unpacked, values);
}
#[test]
fn test_reinterpret_m0_signed() {
let data = vec![0x00, 0x80, 0xFF]; let result = reinterpret_m0(&data, M0Interpretation::Signed);
assert_eq!(result, vec![0.0, -128.0, -1.0]);
}
#[test]
fn test_reinterpret_m0_unsigned() {
let data = vec![0x00, 0x80, 0xFF]; let result = reinterpret_m0(&data, M0Interpretation::Unsigned);
assert_eq!(result, vec![0.0, 128.0, 255.0]);
}
#[test]
fn test_complex_to_real_strategies() {
let c = crate::mode::Float32Complex {
real: 3.0,
imag: 4.0,
};
assert_eq!(c.to_real(ComplexToRealStrategy::RealPart), 3.0);
assert_eq!(c.to_real(ComplexToRealStrategy::ImaginaryPart), 4.0);
assert_eq!(c.to_real(ComplexToRealStrategy::Magnitude), 5.0);
let phase = c.to_real(ComplexToRealStrategy::Phase);
assert!((phase - 0.9272952).abs() < 1e-6);
}
}
pub fn convert_u8_slice_to_u16(src: &[u8]) -> Vec<u16> {
src.iter().map(|&v| v as u16).collect()
}
pub fn convert_u16_slice_to_u8(src: &[u16]) -> Result<Vec<u8>, crate::Error> {
let mut out = Vec::with_capacity(src.len());
for &v in src {
if v > 255 {
return Err(crate::Error::TypeMismatch {
expected: 1,
actual: 2,
});
}
out.push(v as u8);
}
Ok(out)
}
#[cfg(test)]
mod u8_tests {
use super::*;
#[test]
fn test_convert_u8_to_u16() {
let src: Vec<u8> = vec![0, 1, 127, 128, 255];
let dst = convert_u8_slice_to_u16(&src);
assert_eq!(dst, vec![0u16, 1, 127, 128, 255]);
}
#[test]
fn test_convert_u16_to_u8_ok() {
let src: Vec<u16> = vec![0, 1, 127, 128, 255];
let dst = convert_u16_slice_to_u8(&src).unwrap();
assert_eq!(dst, vec![0u8, 1, 127, 128, 255]);
}
#[test]
fn test_convert_u16_to_u8_overflow() {
let src: Vec<u16> = vec![0, 256];
assert!(convert_u16_slice_to_u8(&src).is_err());
}
#[test]
fn test_u8_roundtrip() {
let original: Vec<u8> = (0..=255).collect();
let widened = convert_u8_slice_to_u16(&original);
let narrowed = convert_u16_slice_to_u8(&widened).unwrap();
assert_eq!(original, narrowed);
}
}