#![allow(clippy::similar_names, reason = "XXX")]
use core::mem::MaybeUninit;
use core::simd::prelude::*;
use core::{ptr, slice};
use crate::backend::generic::{decode_generic_unchecked, encode_generic_unchecked};
use crate::error::InvalidInput;
use crate::util::lut16;
#[cfg_attr(
any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "loongarch32",
target_arch = "loongarch64",
target_arch = "wasm32"
),
allow(dead_code, reason = "XXX")
)]
pub(crate) unsafe fn encode_simd128_unchecked<const UPPER: bool>(
src: &[u8],
dst: &mut [[MaybeUninit<u8>; 2]],
) {
#[allow(clippy::identity_op, reason = "XXX")]
const BATCH_ELEMS_V128_X1: usize = size_of::<u8x16>() * 1;
debug_assert!(src.len() <= dst.len());
if src.len() >= BATCH_ELEMS_V128_X1 {
let mask = u8x16::splat(0b_0000_1111);
let lut = u8x16::from_array(*lut16::<UPPER>());
let encode_v128 = |src: *const u8x16, dst: *mut u8x16| {
let chunk = src.read_unaligned();
let hi = chunk >> 4;
let lo = chunk & mask;
let (a, b) = u8x16::interleave(hi, lo);
let out0 = lut.swizzle_dyn(a);
let out1 = lut.swizzle_dyn(b);
ptr::copy_nonoverlapping(out0.as_array().as_ptr(), dst.add(0).cast(), out0.len());
ptr::copy_nonoverlapping(out1.as_array().as_ptr(), dst.add(1).cast(), out1.len());
};
let batches = src.len() / BATCH_ELEMS_V128_X1;
let remainder = src.len() % BATCH_ELEMS_V128_X1;
for i in 0..batches {
encode_v128(
src.as_ptr().cast::<u8x16>().add(i),
dst.as_mut_ptr().cast::<u8x16>().add(i * 2),
);
}
encode_generic_unchecked::<UPPER>(
slice::from_raw_parts(src.as_ptr().add(batches * BATCH_ELEMS_V128_X1), remainder),
slice::from_raw_parts_mut(
dst.as_mut_ptr().add(batches * BATCH_ELEMS_V128_X1),
remainder,
),
);
} else {
encode_generic_unchecked::<UPPER>(src, dst);
}
}
#[cfg_attr(
any(
target_arch = "x86",
target_arch = "x86_64",
target_arch = "aarch64",
target_arch = "arm64ec",
target_arch = "loongarch32",
target_arch = "loongarch64",
target_arch = "wasm32"
),
allow(dead_code, reason = "XXX")
)]
#[allow(clippy::cast_possible_wrap, reason = "XXX")]
pub(crate) unsafe fn decode_simd128_unchecked(
src: *const [[u8; 2]],
dst: *mut [MaybeUninit<u8>],
) -> Result<(), InvalidInput> {
const BATCH_ELEMS_V128_X2: usize = size_of::<i8x16>() / 2 * 2;
if src.len() >= BATCH_ELEMS_V128_X2 {
let n_c6 = i8x16::splat((0xFF_u8 - b'9').cast_signed());
let n_06 = i8x16::splat(0x06);
let n_f0 = i8x16::splat(0xF0_u8.cast_signed());
let n_df = i8x16::splat(0xDF_u8.cast_signed());
let u_a = i8x16::splat(b'A'.cast_signed());
let n_0a = i8x16::splat(0x0A);
let n_0f = u8x16::splat(15);
let batches = src.len() / BATCH_ELEMS_V128_X2;
let remainder = src.len() % BATCH_ELEMS_V128_X2;
let mut invalid = false;
let decode_chunk = |chunk: u8x16| -> u8x16 {
let d = (chunk.cast::<i8>() + n_c6)
.cast::<u8>()
.saturating_sub(n_06.cast::<u8>())
.cast::<i8>()
- n_f0;
let a = ((chunk.cast::<i8>() & n_df) - u_a)
.cast::<u8>()
.saturating_add(n_0a.cast::<u8>());
d.cast::<u8>().simd_min(a)
};
let mut decode_v128x2 = |src: *const u8x16, dst: *mut u8x16| {
let chunk0 = src.add(0).read_unaligned();
let chunk1 = src.add(1).read_unaligned();
let n0 = decode_chunk(chunk0);
let n1 = decode_chunk(chunk1);
invalid |= (n0 | n1).simd_gt(n_0f).any();
let out = {
let (hi, lo) = Simd::deinterleave(n0, n1);
(hi << 4) | lo
};
ptr::copy_nonoverlapping(out.as_array().as_ptr(), dst.cast::<u8>(), out.len());
};
for i in 0..batches {
decode_v128x2(src.cast::<u8x16>().add(i * 2), dst.cast::<u8x16>().add(i));
}
if invalid {
return Err(InvalidInput);
}
decode_generic_unchecked::<false>(
ptr::slice_from_raw_parts(
src.cast::<[u8; 2]>().add(batches * BATCH_ELEMS_V128_X2),
remainder,
),
ptr::slice_from_raw_parts_mut(
dst.cast::<MaybeUninit<u8>>()
.add(batches * BATCH_ELEMS_V128_X2),
remainder,
),
)
} else {
decode_generic_unchecked::<false>(src, dst)
}
}
#[cfg(test)]
mod smoking {
use super::*;
use crate::backend::tests::{
check_decode_validation_any_backend, check_encode_decode_any_backend,
};
#[test]
fn test_encode_decode_simd128() {
check_encode_decode_any_backend::<true>(
encode_simd128_unchecked::<true>,
decode_generic_unchecked::<false>,
);
check_encode_decode_any_backend::<false>(
encode_simd128_unchecked::<false>,
decode_generic_unchecked::<false>,
);
check_encode_decode_any_backend::<true>(
encode_simd128_unchecked::<true>,
decode_simd128_unchecked,
);
check_encode_decode_any_backend::<false>(
encode_simd128_unchecked::<false>,
decode_simd128_unchecked,
);
}
#[test]
fn test_decode_validation_simd128() {
check_decode_validation_any_backend(decode_simd128_unchecked);
}
}