use crate::Voxel;
use crate::mode::M0Interpretation;
use std::vec::Vec;
#[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)
}
}
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()
}
pub(crate) fn convert_with<Src: Voxel, Dst>(
bytes: &[u8],
endian: FileEndian,
) -> Result<Vec<Dst>, Error>
where
Dst: ConvertFrom<Src>,
{
let src = decode_slice::<Src>(bytes, endian)?;
Ok(Dst::convert_from(&src))
}
#[cfg(feature = "f16")]
pub(crate) fn convert_block<T>(
bytes: &[u8],
mode: Mode,
endian: FileEndian,
nx: usize,
ny: usize,
) -> Result<Vec<T>, Error>
where
T: Voxel + ConvertFrom<i8> + ConvertFrom<i16> + ConvertFrom<u16> + ConvertFrom<f32>,
{
match mode {
Mode::Int8 => convert_with::<i8, T>(bytes, endian),
Mode::Int16 => convert_with::<i16, T>(bytes, endian),
Mode::Uint16 => convert_with::<u16, T>(bytes, endian),
Mode::Float32 => convert_with::<f32, T>(bytes, endian),
Mode::Float16 => {
let src = decode_slice::<crate::f16>(bytes, endian)?;
let f32_data = convert_f16_slice_to_f32(&src);
Ok(T::convert_from(&f32_data))
}
Mode::Float32Complex => {
let src = decode_slice::<Float32Complex>(bytes, endian)?;
let mag: Vec<f32> = src
.iter()
.map(|c| c.to_real(ComplexToRealStrategy::Magnitude))
.collect();
Ok(T::convert_from(&mag))
}
Mode::Int16Complex => {
let src = decode_slice::<Int16Complex>(bytes, endian)?;
let mag: Vec<f32> = src
.iter()
.map(|c| c.to_real(ComplexToRealStrategy::Magnitude))
.collect();
Ok(T::convert_from(&mag))
}
Mode::Packed4Bit => {
let unpacked = unpack_u4_bytes_to_u8(bytes, nx, ny);
let f32_data = convert_u8_slice_to_f32(&unpacked);
Ok(T::convert_from(&f32_data))
}
}
}
#[cfg(not(feature = "f16"))]
pub(crate) fn convert_block<T>(
bytes: &[u8],
mode: Mode,
endian: FileEndian,
nx: usize,
ny: usize,
) -> Result<Vec<T>, Error>
where
T: Voxel + ConvertFrom<i8> + ConvertFrom<i16> + ConvertFrom<u16> + ConvertFrom<f32>,
{
match mode {
Mode::Int8 => convert_with::<i8, T>(bytes, endian),
Mode::Int16 => convert_with::<i16, T>(bytes, endian),
Mode::Uint16 => convert_with::<u16, T>(bytes, endian),
Mode::Float32 => convert_with::<f32, T>(bytes, endian),
Mode::Float16 => Err(Error::UnsupportedMode),
Mode::Float32Complex => {
let src = decode_slice::<Float32Complex>(bytes, endian)?;
let mag: Vec<f32> = src
.iter()
.map(|c| c.to_real(ComplexToRealStrategy::Magnitude))
.collect();
Ok(T::convert_from(&mag))
}
Mode::Int16Complex => {
let src = decode_slice::<Int16Complex>(bytes, endian)?;
let mag: Vec<f32> = src
.iter()
.map(|c| c.to_real(ComplexToRealStrategy::Magnitude))
.collect();
Ok(T::convert_from(&mag))
}
Mode::Packed4Bit => {
let unpacked = unpack_u4_bytes_to_u8(bytes, nx, ny);
let f32_data = convert_u8_slice_to_f32(&unpacked);
Ok(T::convert_from(&f32_data))
}
}
}
#[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);
}
}