use std::arch::aarch64::uint8x16_t;
use std::arch::aarch64::vdupq_n_u8;
use std::arch::aarch64::vld1q_u8;
use std::arch::aarch64::vmaxq_u8;
use std::arch::aarch64::vmaxvq_u8;
use std::arch::aarch64::vqtbl1q_u8;
use std::arch::aarch64::vst1q_u8;
use vortex_buffer::Buffer;
use vortex_buffer::BufferAllocatorRef;
use vortex_buffer::BufferMut;
use super::super::FixedWidthTakeValue;
use super::super::take_values_fallback;
use crate::dtype::PType;
use crate::dtype::UnsignedPType;
unsafe impl FixedWidthTakeValue for u8 {
fn take<I: UnsignedPType>(
values: &[Self],
indices: &[I],
allocator: &BufferAllocatorRef,
) -> Buffer<Self> {
take(values, indices, allocator)
}
}
unsafe impl FixedWidthTakeValue for i8 {
fn take<I: UnsignedPType>(
values: &[Self],
indices: &[I],
allocator: &BufferAllocatorRef,
) -> Buffer<Self> {
take(values, indices, allocator)
}
}
unsafe impl<const N: usize> FixedWidthTakeValue for [u8; N] {
fn take<I: UnsignedPType>(
values: &[Self],
indices: &[I],
allocator: &BufferAllocatorRef,
) -> Buffer<Self> {
take(values, indices, allocator)
}
}
fn take<T: FixedWidthTakeValue, I: UnsignedPType>(
values: &[T],
indices: &[I],
allocator: &BufferAllocatorRef,
) -> Buffer<T> {
if I::PTYPE != PType::U8
|| values.is_empty()
|| values.len() > 16
|| size_of::<T>() != 1
|| indices.len() < 64
{
return take_values_fallback(values, indices, allocator);
}
let indices: &[u8] =
unsafe { std::slice::from_raw_parts(indices.as_ptr().cast(), indices.len()) };
let mut table = [values[0]; 16];
table[..values.len()].copy_from_slice(values);
let mut output = BufferMut::<T>::with_capacity_in(indices.len(), allocator.clone());
let spare = output.spare_capacity_mut();
let output_ptr = spare.as_mut_ptr().cast::<u8>();
let (offset, max_code) = unsafe { take_vectors(&table, indices, output_ptr) };
for offset in offset..indices.len() {
let code = usize::from(indices[offset]);
assert!(
code < values.len(),
"take index {code} out of bounds for length {}",
values.len()
);
spare[offset].write(values[code]);
}
assert!(
usize::from(max_code) < values.len(),
"take index {max_code} out of bounds for length {}",
values.len()
);
unsafe { output.set_len(indices.len()) };
output.freeze()
}
unsafe fn take_vectors<T: FixedWidthTakeValue>(
table: &[T; 16],
indices: &[u8],
output: *mut u8,
) -> (usize, u8) {
let table = unsafe { vld1q_u8(table.as_ptr().cast::<u8>()) };
let mut max_codes: uint8x16_t = unsafe { vdupq_n_u8(0) };
let mut offset = 0;
while offset + 16 <= indices.len() {
let codes = unsafe { vld1q_u8(indices.as_ptr().add(offset)) };
max_codes = unsafe { vmaxq_u8(max_codes, codes) };
unsafe { vst1q_u8(output.add(offset), vqtbl1q_u8(table, codes)) };
offset += 16;
}
(offset, unsafe { vmaxvq_u8(max_codes) })
}