use crate::smith_waterman::algo::{ascii_gap, unicode_gap};
use super::{Backend, BytesVec, MaskVec, ScoreVec};
#[derive(Debug, Clone, Copy)]
pub struct ScalarBytes<const LANES: usize>([u8; LANES]);
#[derive(Debug, Clone, Copy)]
pub struct ScalarScoreU16<const LANES: usize>([u16; LANES]);
#[derive(Debug, Clone, Copy)]
pub struct ScalarScoreU8<const LANES: usize>([u8; LANES]);
impl<const LANES: usize> ScalarScoreU16<LANES> {
#[inline(always)]
fn widen(mask: ScalarBytes<LANES>) -> Self {
let mut out = [0u16; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if mask.0[idx] != 0 { u16::MAX } else { 0 };
}
Self(out)
}
}
impl<const LANES: usize> ScalarScoreU8<LANES> {
#[inline(always)]
fn widen(mask: ScalarBytes<LANES>) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if mask.0[idx] != 0 { u8::MAX } else { 0 };
}
Self(out)
}
}
impl<const LANES: usize> BytesVec for ScalarBytes<LANES> {
type Mask = ScalarBytes<LANES>;
#[inline(always)]
unsafe fn splat(value: u8) -> Self {
Self([value; LANES])
}
#[inline(always)]
unsafe fn eq(self, other: Self) -> Self::Mask {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if self.0[idx] == other.0[idx] { 0xFF } else { 0 };
}
Self(out)
}
#[inline(always)]
unsafe fn gt(self, other: Self) -> Self::Mask {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if self.0[idx] > other.0[idx] { 0xFF } else { 0 };
}
Self(out)
}
#[inline(always)]
unsafe fn lt(self, other: Self) -> Self::Mask {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if self.0[idx] < other.0[idx] { 0xFF } else { 0 };
}
Self(out)
}
#[inline(always)]
unsafe fn load_partial(data: *const u8, start: usize, len: usize) -> Self {
let mut out = [0u8; LANES];
let take = len.saturating_sub(start).min(LANES);
for (idx, lane) in out.iter_mut().take(take).enumerate() {
*lane = unsafe { *data.add(start + idx) };
}
Self(out)
}
#[cfg(test)]
fn from_lanes(values: &[u8]) -> Self {
assert_eq!(values.len(), LANES);
let mut out = [0u8; LANES];
out.copy_from_slice(values);
Self(out)
}
#[cfg(test)]
fn to_lanes(self) -> Vec<u8> {
self.0.to_vec()
}
}
impl<const LANES: usize> MaskVec for ScalarBytes<LANES> {
#[inline(always)]
unsafe fn zero() -> Self {
Self([0; LANES])
}
#[inline(always)]
unsafe fn and(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx] & other.0[idx];
}
Self(out)
}
#[inline(always)]
unsafe fn or(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx] | other.0[idx];
}
Self(out)
}
#[inline(always)]
unsafe fn not(self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = !self.0[idx];
}
Self(out)
}
#[inline(always)]
unsafe fn is_zero(self) -> bool {
self.0.iter().all(|&v| v == 0)
}
#[inline(always)]
unsafe fn shift_right_padded_1(self, prev: Self) -> Self {
let mut out = [0u8; LANES];
out[0] = prev.0[LANES - 1];
out[1..LANES].copy_from_slice(&self.0[..(LANES - 1)]);
Self(out)
}
#[cfg(test)]
fn from_lanes(values: &[bool]) -> Self {
assert_eq!(values.len(), LANES);
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = if values[idx] { 0xFF } else { 0 };
}
Self(out)
}
#[cfg(test)]
fn to_lanes(self) -> Vec<bool> {
self.0.iter().map(|&v| v != 0).collect()
}
}
impl<const LANES: usize> ScoreVec for ScalarScoreU16<LANES> {
#[inline(always)]
unsafe fn zero() -> Self {
Self([0; LANES])
}
#[inline(always)]
unsafe fn splat(value: u16) -> Self {
Self([value; LANES])
}
#[inline(always)]
unsafe fn first_lane(value: u16) -> Self {
let mut out = [0u16; LANES];
out[0] = value;
Self(out)
}
#[inline(always)]
unsafe fn max(self, other: Self) -> Self {
let mut out = [0u16; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].max(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn horizontal_max(self) -> u16 {
*self.0.iter().max().unwrap()
}
#[inline(always)]
unsafe fn add(self, other: Self) -> Self {
let mut out = [0u16; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].wrapping_add(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn subs(self, other: Self) -> Self {
let mut out = [0u16; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].saturating_sub(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn and(self, other: Self) -> Self {
let mut out = [0u16; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx] & other.0[idx];
}
Self(out)
}
#[inline(always)]
unsafe fn shift_right_padded<const L: i32>(self, prev: Self) -> Self {
const { assert!(L >= 0 && (L as usize) <= LANES) };
let n = L as usize;
let mut out = [0u16; LANES];
for (idx, item) in out.iter_mut().enumerate().take(n) {
*item = prev.0[LANES - n + idx];
}
out[n..LANES].copy_from_slice(&self.0[..(LANES - n)]);
Self(out)
}
#[inline(always)]
unsafe fn find_lane(self, search: u16) -> usize {
for (idx, &lane) in self.0.iter().enumerate() {
if lane == search {
return idx;
}
}
LANES
}
#[cfg(test)]
fn from_lanes(values: &[u16]) -> Self {
assert_eq!(values.len(), LANES);
let mut out = [0u16; LANES];
out.copy_from_slice(values);
Self(out)
}
#[cfg(test)]
fn to_lanes(self) -> Vec<u16> {
self.0.to_vec()
}
}
impl<const LANES: usize> ScoreVec for ScalarScoreU8<LANES> {
#[inline(always)]
unsafe fn zero() -> Self {
Self([0; LANES])
}
#[inline(always)]
unsafe fn splat(value: u16) -> Self {
Self([value as u8; LANES])
}
#[inline(always)]
unsafe fn first_lane(value: u16) -> Self {
let mut out = [0u8; LANES];
out[0] = value as u8;
Self(out)
}
#[inline(always)]
unsafe fn max(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].max(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn horizontal_max(self) -> u16 {
*self.0.iter().max().unwrap() as u16
}
#[inline(always)]
unsafe fn add(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].wrapping_add(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn subs(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx].saturating_sub(other.0[idx]);
}
Self(out)
}
#[inline(always)]
unsafe fn and(self, other: Self) -> Self {
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = self.0[idx] & other.0[idx];
}
Self(out)
}
#[inline(always)]
unsafe fn shift_right_padded<const L: i32>(self, prev: Self) -> Self {
const { assert!(L >= 0 && (L as usize) <= LANES) };
let n = L as usize;
let mut out = [0u8; LANES];
for (idx, item) in out.iter_mut().enumerate().take(n) {
*item = prev.0[LANES - n + idx];
}
out[n..LANES].copy_from_slice(&self.0[..(LANES - n)]);
Self(out)
}
#[inline(always)]
unsafe fn find_lane(self, search: u16) -> usize {
let search = search as u8;
for (idx, &lane) in self.0.iter().enumerate() {
if lane == search {
return idx;
}
}
LANES
}
#[cfg(test)]
fn from_lanes(values: &[u16]) -> Self {
assert_eq!(values.len(), LANES);
let mut out = [0u8; LANES];
for (idx, lane) in out.iter_mut().enumerate() {
*lane = values[idx] as u8;
}
Self(out)
}
#[cfg(test)]
fn to_lanes(self) -> Vec<u16> {
self.0.iter().map(|&v| v as u16).collect()
}
}
macro_rules! scalar_backend {
(
$backend:ident,
$lanes:literal,
$lane_bytes:literal,
$score:ty,
$propagate:ident,
$propagate_unicode:ident
) => {
#[derive(Debug, Clone, Copy)]
pub struct $backend;
impl Backend for $backend {
const LANES: usize = $lanes;
const LANE_BYTES: usize = $lane_bytes;
type Bytes = ScalarBytes<$lanes>;
type Mask = ScalarBytes<$lanes>;
type Score = $score;
fn is_available() -> bool {
true
}
#[inline(always)]
unsafe fn widen_mask(m: Self::Mask) -> Self::Score {
<$score>::widen(m)
}
#[inline(always)]
unsafe fn propagate_horizontal_gaps(
row: Self::Score,
adjacent_row: Self::Score,
match_mask: Self::Score,
adjacent_match_mask: Self::Score,
gap_open_penalty: Self::Score,
gap_extend_penalty: Self::Score,
) -> Self::Score {
unsafe {
ascii_gap::$propagate::<Self>(
row,
adjacent_row,
match_mask,
adjacent_match_mask,
gap_open_penalty,
gap_extend_penalty,
)
}
}
#[inline(always)]
unsafe fn propagate_horizontal_unicode_gaps(
row: Self::Score,
adjacent_row: Self::Score,
pending_gap_open_mask: Self::Score,
adjacent_pending_gap_open_mask: Self::Score,
continuation_gap_extend_penalty: Self::Score,
adjacent_continuation_gap_extend_penalty: Self::Score,
scalar_end_mask: Self::Score,
adjacent_scalar_end_mask: Self::Score,
gap_open_penalty: Self::Score,
gap_extend_penalty: Self::Score,
) -> (Self::Score, Self::Score) {
unsafe {
unicode_gap::$propagate_unicode::<Self>(
row,
adjacent_row,
pending_gap_open_mask,
adjacent_pending_gap_open_mask,
continuation_gap_extend_penalty,
adjacent_continuation_gap_extend_penalty,
scalar_end_mask,
adjacent_scalar_end_mask,
gap_open_penalty,
gap_extend_penalty,
)
}
}
}
};
}
scalar_backend!(
BackendScalar8,
8,
2,
ScalarScoreU16<8>,
propagate_8_lane,
propagate_unicode_8_lane
);
scalar_backend!(
BackendScalar16U8,
16,
1,
ScalarScoreU8<16>,
propagate_16_lane,
propagate_unicode_16_lane
);
#[cfg(target_arch = "x86_64")]
#[cfg(test)]
scalar_backend!(
TestScalar16,
16,
2,
ScalarScoreU16<16>,
propagate_16_lane,
propagate_unicode_16_lane
);
#[cfg(target_arch = "x86_64")]
#[cfg(test)]
scalar_backend!(
TestScalar32,
32,
2,
ScalarScoreU16<32>,
propagate_32_lane,
propagate_unicode_32_lane
);
#[cfg(target_arch = "x86_64")]
#[cfg(test)]
scalar_backend!(
TestScalar32U8,
32,
1,
ScalarScoreU8<32>,
propagate_32_lane,
propagate_unicode_32_lane
);
#[cfg(target_arch = "x86_64")]
#[cfg(test)]
scalar_backend!(
TestScalar64U8,
64,
1,
ScalarScoreU8<64>,
propagate_64_lane,
propagate_unicode_64_lane
);