use core::fmt;
use std::simd::prelude::*;
use std::simd::LaneCount;
use std::simd::SimdElement;
use std::simd::SupportedLaneCount;
use crate::util::invert_index;
use crate::util::tiled;
#[inline]
pub fn decode<const N: usize>(ascii: Simd<u8, N>) -> (Simd<u8, N>, bool)
where
LaneCount<N>: SupportedLaneCount,
{
let hashes = (ascii >> Simd::splat(4))
+ Simd::simd_eq(ascii, Simd::splat(b'/'))
.to_int()
.cast::<u8>();
let sextets =
ascii + tiled(&[!0, 16, 19, 4, 191, 191, 185, 185]).swizzle_dyn(hashes);
const LO_LUT: Simd<u8, 16> = Simd::from_array([
0b10101, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001,
0b10001, 0b10001, 0b10011, 0b11010, 0b11011, 0b11011, 0b11011, 0b11010,
]);
const HI_LUT: Simd<u8, 16> = Simd::from_array([
0b10000, 0b10000, 0b00001, 0b00010, 0b00100, 0b01000, 0b00100, 0b01000,
0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000,
]);
let lo = swizzle::<16, N>(LO_LUT, ascii & Simd::splat(0x0f));
let hi = swizzle::<16, N>(HI_LUT, ascii >> Simd::splat(4));
let valid = (lo & hi).reduce_or() == 0;
let shifted = sextets.cast::<u16>() << tiled(&[2, 4, 6, 8]);
let lo = shifted.cast::<u8>();
let hi = (shifted >> Simd::splat(8)).cast::<u8>();
let decoded_chunks = lo | hi.rotate_lanes_left::<1>();
let output = swizzle!(N; decoded_chunks, array!(N; |i| i + i / 3));
(output, valid)
}
#[inline]
pub fn encode<const N: usize>(data: Simd<u8, N>) -> Simd<u8, N>
where
LaneCount<N>: SupportedLaneCount,
{
let data = swizzle!(N; data, invert_index(array!(N; |i| i + i / 3)));
let mask = tiled(&[0b11111100, 0b11110000, 0b11000000, 0b00000000]);
let lo = data & mask;
let hi = (data & !mask).rotate_lanes_right::<1>();
let shifted = lo.cast::<u16>() | (hi.cast::<u16>() << Simd::splat(8));
let sextets = (shifted >> tiled(&[2, 4, 6, 8])).cast::<u8>();
let hashes = (sextets.saturating_sub(Simd::splat(0x0a))
+ mask_splat(sextets.simd_ge(Simd::splat(0x34)), 0x0f)
+ mask_splat(sextets.simd_ge(Simd::splat(0x3e)), 0x1c))
>> Simd::splat(4);
let offsets = tiled(&[191, 185, 185, 4, 4, 19, 16, !0]).swizzle_dyn(hashes);
sextets - offsets
}
fn mask_splat<T, const N: usize>(mask: Mask<T::Mask, N>, val: T) -> Simd<T, N>
where
T: SimdElement + Default,
LaneCount<N>: SupportedLaneCount,
{
mask.select(Simd::splat(val), Simd::splat(Default::default()))
}
fn resize<T, const N: usize, const M: usize>(v: Simd<T, N>) -> Simd<T, M>
where
T: SimdElement + Default,
LaneCount<N>: SupportedLaneCount,
LaneCount<M>: SupportedLaneCount,
{
let len = usize::min(N, M);
let mut out = Simd::default();
out.as_mut_array()[..len].copy_from_slice(&v.as_array()[..len]);
out
}
fn swizzle<const N: usize, const M: usize>(
table: Simd<u8, N>,
indices: Simd<u8, M>,
) -> Simd<u8, M>
where
LaneCount<N>: SupportedLaneCount,
LaneCount<M>: SupportedLaneCount,
{
if N < M {
Simd::swizzle_dyn(resize(table), indices)
} else {
resize(Simd::swizzle_dyn(table, resize(indices)))
}
}
#[allow(dead_code)]
struct SimdDbg<V>(pub V);
impl<T, const N: usize> fmt::Binary for SimdDbg<Simd<T, N>>
where
T: SimdElement + fmt::Binary,
LaneCount<N>: SupportedLaneCount,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct Patch<T>(T);
impl<T: fmt::Binary> fmt::Debug for Patch<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Binary::fmt(&self.0, f)
}
}
let mut f = f.debug_list();
for b in self.0.to_array() {
f.entry(&Patch(b));
}
f.finish()
}
}
impl<T, const N: usize> fmt::LowerHex for SimdDbg<Simd<T, N>>
where
T: SimdElement + fmt::LowerHex,
LaneCount<N>: SupportedLaneCount,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
struct Patch<T>(T);
impl<T: fmt::LowerHex> fmt::Debug for Patch<T> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::LowerHex::fmt(&self.0, f)
}
}
let mut f = f.debug_list();
for b in self.0.to_array() {
f.entry(&Patch(b));
}
f.finish()
}
}