use crate::{AUX_NONE, AUX_SLASH, Blk, lead_run};
use core::arch::x86_64::*;
#[inline(always)]
unsafe fn le(x: __m128i, k: u8) -> __m128i {
unsafe { _mm_cmpeq_epi8(_mm_min_epu8(x, _mm_set1_epi8(k as i8)), x) }
}
#[inline(always)]
unsafe fn ge(x: __m128i, k: u8) -> __m128i {
unsafe { _mm_cmpeq_epi8(_mm_max_epu8(x, _mm_set1_epi8(k as i8)), x) }
}
#[inline(always)]
unsafe fn in_range(x: __m128i, lo: u8, n: u8) -> __m128i {
unsafe { le(_mm_sub_epi8(x, _mm_set1_epi8(lo as i8)), n) }
}
#[inline(always)]
unsafe fn sel(mask: __m128i, a: __m128i, b: __m128i) -> __m128i {
unsafe { _mm_or_si128(_mm_and_si128(mask, a), _mm_andnot_si128(mask, b)) }
}
#[target_feature(enable = "ssse3")]
pub(crate) unsafe fn eq64(text: &[u8], base: usize, b: u8) -> u64 {
unsafe {
let n = _mm_set1_epi8(b as i8);
let mut m = 0u64;
for k in 0..4 {
let v = _mm_loadu_si128(text.as_ptr().add(base + k * 16).cast());
m |= (_mm_movemask_epi8(_mm_cmpeq_epi8(v, n)) as u16 as u64) << (16 * k);
}
m
}
}
#[target_feature(enable = "ssse3")]
pub(crate) unsafe fn any64(text: &[u8], base: usize, b: u8) -> bool {
unsafe {
let n = _mm_set1_epi8(b as i8);
let mut acc = _mm_setzero_si128();
for k in 0..4 {
let v = _mm_loadu_si128(text.as_ptr().add(base + k * 16).cast());
acc = _mm_or_si128(acc, _mm_cmpeq_epi8(v, n));
}
_mm_movemask_epi8(acc) != 0
}
}
#[target_feature(enable = "ssse3")]
pub(crate) unsafe fn build64<const AUX: u8, const P3: bool>(
text: &[u8],
tags: &[u8],
base: usize,
lut: &[u8; 64],
cur_code: u8,
cur_aux: bool,
) -> (Blk, u8) {
unsafe {
let low_nibble = _mm_set1_epi8(0x0F);
let seven = _mm_set1_epi8(7);
let t0 = _mm_loadu_si128(lut.as_ptr().cast());
let t1 = _mm_loadu_si128(lut.as_ptr().add(16).cast());
let t2 = _mm_loadu_si128(lut.as_ptr().add(32).cast());
let t3 = _mm_loadu_si128(lut.as_ptr().add(48).cast());
let mut cd = [_mm_setzero_si128(); 4];
let mut isc = [_mm_setzero_si128(); 4];
let mut prev = _mm_set1_epi8(cur_code as i8);
for k in 0..4 {
let t = _mm_loadu_si128(tags.as_ptr().add(base + k * 16).cast());
let lo = _mm_and_si128(t, low_nibble);
let hi = _mm_and_si128(_mm_srli_epi16(t, 4), low_nibble);
let raw = sel(
_mm_cmpeq_epi8(hi, _mm_set1_epi8(3)),
_mm_shuffle_epi8(t3, lo),
sel(
_mm_cmpeq_epi8(hi, _mm_set1_epi8(2)),
_mm_shuffle_epi8(t2, lo),
sel(
_mm_cmpeq_epi8(hi, _mm_set1_epi8(1)),
_mm_shuffle_epi8(t1, lo),
_mm_shuffle_epi8(t0, lo),
),
),
);
isc[k] = _mm_cmpeq_epi8(raw, seven);
let c = sel(isc[k], _mm_alignr_epi8(raw, prev, 15), raw);
let c = sel(_mm_cmpeq_epi8(c, seven), _mm_alignr_epi8(c, prev, 14), c);
prev = c;
cd[k] = c;
}
let last_code = (_mm_extract_epi16(prev, 7) >> 8) as u8;
let gather = |v: [__m128i; 4]| -> u64 {
let mut m = 0u64;
for (k, chunk) in v.iter().enumerate() {
m |= (_mm_movemask_epi8(*chunk) as u16 as u64) << (16 * k);
}
m
};
macro_rules! plane {
($shift:literal) => {
gather([
_mm_slli_epi16::<$shift>(cd[0]),
_mm_slli_epi16::<$shift>(cd[1]),
_mm_slli_epi16::<$shift>(cd[2]),
_mm_slli_epi16::<$shift>(cd[3]),
])
};
}
let mut b = Blk {
cont: gather(isc),
p0: plane!(7),
p1: plane!(6),
p2: plane!(5),
p3: if P3 { plane!(4) } else { 0 },
aux: 0,
};
if AUX == AUX_NONE {
return (b, last_code);
}
if AUX == AUX_SLASH {
let sl = _mm_set1_epi8(b'/' as i8);
b.aux = gather([
_mm_cmpeq_epi8(_mm_loadu_si128(text.as_ptr().add(base).cast()), sl),
_mm_cmpeq_epi8(_mm_loadu_si128(text.as_ptr().add(base + 16).cast()), sl),
_mm_cmpeq_epi8(_mm_loadu_si128(text.as_ptr().add(base + 32).cast()), sl),
_mm_cmpeq_epi8(_mm_loadu_si128(text.as_ptr().add(base + 48).cast()), sl),
]);
return (b, last_code);
}
let ntext = text.len();
let tv = [
_mm_loadu_si128(text.as_ptr().add(base).cast()),
_mm_loadu_si128(text.as_ptr().add(base + 16).cast()),
_mm_loadu_si128(text.as_ptr().add(base + 32).cast()),
_mm_loadu_si128(text.as_ptr().add(base + 48).cast()),
];
let any = tv
.iter()
.any(|v| _mm_movemask_epi8(in_range(*v, 0xE3, 6)) != 0);
if !any {
if cur_aux {
b.aux |= lead_run(b.cont, !0);
}
return (b, last_code);
}
let tail = {
let mut buf = [0u8; 16];
let off = base + 64;
let avail = ntext.saturating_sub(off).min(16);
buf[..avail].copy_from_slice(&text[off..off + avail]);
_mm_loadu_si128(buf.as_ptr().cast())
};
let cjkv = |v: __m128i, nx: __m128i| -> __m128i {
let b1 = _mm_alignr_epi8(nx, v, 1);
let b2 = _mm_alignr_epi8(nx, v, 2);
let e9 = _mm_and_si128(
_mm_cmpeq_epi8(v, _mm_set1_epi8(0xE9u8 as i8)),
_mm_or_si128(
le(b1, 0xBD),
_mm_and_si128(
_mm_cmpeq_epi8(b1, _mm_set1_epi8(0xBEu8 as i8)),
le(b2, 0xA5),
),
),
);
_mm_or_si128(
_mm_or_si128(
_mm_and_si128(
_mm_cmpeq_epi8(v, _mm_set1_epi8(0xE3u8 as i8)),
in_range(b1, 0x81, 2),
),
_mm_and_si128(_mm_cmpeq_epi8(v, _mm_set1_epi8(0xE4u8 as i8)), ge(b1, 0xB8)),
),
_mm_or_si128(in_range(v, 0xE5, 3), e9),
)
};
let mut leads = gather([
cjkv(tv[0], tv[1]),
cjkv(tv[1], tv[2]),
cjkv(tv[2], tv[3]),
cjkv(tv[3], tail),
]);
let lim = ntext.saturating_sub(base + 2);
if lim < 64 {
leads &= (1u64 << lim) - 1;
}
b.aux = leads | (leads << 1) | (leads << 2);
if cur_aux {
b.aux |= lead_run(b.cont, !0);
}
(b, last_code)
}
}