use super::atom_tables::ATOM_TABLES;
use super::{Atom, CONT, MB};
const CJK_TAG: u8 = Atom::Letter as u8;
const HAN_TAG: u8 = Atom::HanLetter as u8;
#[cfg(target_arch = "aarch64")]
#[inline]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn tbl256(
t: &[[u8; 64]; 4],
idx: core::arch::aarch64::uint8x16_t,
) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vorrq_u8(
vorrq_u8(
vqtbl4q_u8(vld1q_u8_x4(t[0].as_ptr()), idx),
vqtbl4q_u8(vld1q_u8_x4(t[1].as_ptr()), vsubq_u8(idx, vdupq_n_u8(64))),
),
vorrq_u8(
vqtbl4q_u8(vld1q_u8_x4(t[2].as_ptr()), vsubq_u8(idx, vdupq_n_u8(128))),
vqtbl4q_u8(vld1q_u8_x4(t[3].as_ptr()), vsubq_u8(idx, vdupq_n_u8(192))),
),
)
}
#[cfg(target_arch = "aarch64")]
#[inline]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn ascii_tbl(
v: core::arch::aarch64::uint8x16_t,
lo: &[u8; 64],
hi: &[u8; 64],
) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vorrq_u8(
vqtbl4q_u8(vld1q_u8_x4(lo.as_ptr()), v),
vqtbl4q_u8(vld1q_u8_x4(hi.as_ptr()), vsubq_u8(v, vdupq_n_u8(64))),
)
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn eq(bytes: core::arch::aarch64::uint8x16_t, byte: u8) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vceqq_u8(bytes, vdupq_n_u8(byte))
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn ge(bytes: core::arch::aarch64::uint8x16_t, lo: u8) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vcgeq_u8(bytes, vdupq_n_u8(lo))
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn le(bytes: core::arch::aarch64::uint8x16_t, hi: u8) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vcleq_u8(bytes, vdupq_n_u8(hi))
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn in_range(
bytes: core::arch::aarch64::uint8x16_t,
lo: u8,
hi: u8,
) -> core::arch::aarch64::uint8x16_t {
use core::arch::aarch64::*;
vandq_u8(ge(bytes, lo), le(bytes, hi))
}
#[cfg(target_arch = "aarch64")]
#[inline(always)]
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn any(mask: core::arch::aarch64::uint8x16_t) -> bool {
use core::arch::aarch64::*;
vmaxvq_u8(mask) != 0
}
#[cfg(target_arch = "aarch64")] #[inline]
fn decode(t: &[u8], i: usize) -> u32 {
let b = t[i] as u32;
match super::char_len(t[i]) {
1 => b,
2 => ((b & 0x1F) << 6) | (t[i + 1] as u32 & 0x3F),
3 => ((b & 0x0F) << 12) | ((t[i + 1] as u32 & 0x3F) << 6) | (t[i + 2] as u32 & 0x3F),
_ => {
((b & 0x07) << 18)
| ((t[i + 1] as u32 & 0x3F) << 12)
| ((t[i + 2] as u32 & 0x3F) << 6)
| (t[i + 3] as u32 & 0x3F)
}
}
}
#[cfg(target_arch = "aarch64")]
#[allow(unsafe_op_in_unsafe_fn, non_snake_case)]
pub unsafe fn classify_neon(text: &[u8], tags: &mut [u8]) {
use super::char_len;
use core::arch::aarch64::*;
let n = text.len();
let mut i = 0;
while i + 32 <= n {
let b0 = vld1q_u8(text.as_ptr().add(i));
if vmaxvq_u8(b0) < 0x80 {
vst1q_u8(
tags.as_mut_ptr().add(i),
ascii_tbl(b0, &ATOM_TABLES.ascii_lo, &ATOM_TABLES.ascii_hi),
);
i += 16;
continue;
}
let mut out = ascii_tbl(b0, &ATOM_TABLES.ascii_lo, &ATOM_TABLES.ascii_hi);
let next = vld1q_u8(text.as_ptr().add(i + 16));
let b1 = vextq_u8::<1>(b0, next); let b2 = vextq_u8::<2>(b0, next);
let mut resolved = vdupq_n_u8(0);
let is_lead2 = eq(vandq_u8(b0, vdupq_n_u8(0xE0)), 0xC0);
if any(is_lead2) {
let group_index = vorrq_u8(
vshlq_n_u8::<6>(vandq_u8(b0, vdupq_n_u8(3))),
vandq_u8(b1, vdupq_n_u8(0x3F)),
);
let grp = vshrq_n_u8::<2>(b0); let mut tags2 = vdupq_n_u8(MB);
let mut unresolved = is_lead2;
while any(unresolved) {
let group = vminvq_u8(vbslq_u8(unresolved, grp, vdupq_n_u8(0xFF))); let group_lanes = vandq_u8(unresolved, eq(grp, group)); let group_table = &ATOM_TABLES.group_tables[(group & 7) as usize];
tags2 = vbslq_u8(group_lanes, tbl256(group_table, group_index), tags2);
unresolved = vbicq_u8(unresolved, group_lanes); }
out = vbslq_u8(is_lead2, tags2, out);
resolved = is_lead2;
}
let in_cjk_leads = in_range(b0, 0xE3, 0xED);
if any(in_cjk_leads) {
let han = vbicq_u8(
in_range(b0, 0xE4, 0xE9),
vandq_u8(eq(b0, 0xE4), eq(b1, 0xB7)),
);
let hangul = vorrq_u8(
vorrq_u8(
in_range(b0, 0xEB, 0xEC),
vandq_u8(eq(b0, 0xEA), ge(b1, 0xB0)),
),
vandq_u8(eq(b0, 0xED), le(b1, 0x9D)),
);
let hole_3040 = vandq_u8(eq(b1, 0x81), eq(b2, 0x80));
let hole_309x = vandq_u8(
eq(b1, 0x82),
vorrq_u8(in_range(b2, 0x97, 0x9C), eq(b2, 0xA0)),
);
let hole_30fb = vandq_u8(eq(b1, 0x83), eq(b2, 0xBB));
let kana = vbicq_u8(
vandq_u8(eq(b0, 0xE3), in_range(b1, 0x81, 0x83)),
vorrq_u8(vorrq_u8(hole_3040, hole_309x), hole_30fb),
);
let is_cjk_letter = vorrq_u8(vorrq_u8(han, hangul), kana);
out = vbslq_u8(is_cjk_letter, vdupq_n_u8(CJK_TAG), out);
out = vbslq_u8(han, vdupq_n_u8(HAN_TAG), out);
resolved = vorrq_u8(resolved, is_cjk_letter);
}
let is_lead3 = vbicq_u8(in_range(b0, 0xE0, 0xEF), resolved);
if any(is_lead3) {
let block_index = vorrq_u8(
vshlq_n_u8::<6>(vandq_u8(b1, vdupq_n_u8(1))),
vandq_u8(b2, vdupq_n_u8(0x3F)),
);
let pair = vshrq_n_u8::<1>(b1);
let mut tags3 = vdupq_n_u8(MB);
let mut unresolved = is_lead3;
while any(unresolved) {
let lead = vminvq_u8(vbslq_u8(unresolved, b0, vdupq_n_u8(0xFF))); let lead_lanes = vandq_u8(unresolved, eq(b0, lead));
let min_pair = vminvq_u8(vbslq_u8(lead_lanes, pair, vdupq_n_u8(0xFF))); let block_lanes = vandq_u8(lead_lanes, eq(pair, min_pair)); let block = (lead - 0xE0) as usize * 32 + (min_pair & 0x1F) as usize;
let uniform_tag = ATOM_TABLES.fast3_uni[block];
let block_tags = if uniform_tag != 0xFF {
vdupq_n_u8(uniform_tag) } else {
let (lo, hi) = &ATOM_TABLES.fast3_mixed[ATOM_TABLES.fast3_slot[block] as usize];
vorrq_u8(
vqtbl4q_u8(vld1q_u8_x4(lo.as_ptr()), block_index),
vqtbl4q_u8(
vld1q_u8_x4(hi.as_ptr()),
vsubq_u8(block_index, vdupq_n_u8(64)),
),
)
};
tags3 = vbslq_u8(block_lanes, block_tags, tags3);
unresolved = vbicq_u8(unresolved, block_lanes); }
out = vbslq_u8(is_lead3, tags3, out);
resolved = vorrq_u8(resolved, vbicq_u8(is_lead3, eq(tags3, MB)));
}
let stray_lead = vbicq_u8(ge(b0, 0xC0), resolved);
out = vbslq_u8(stray_lead, vdupq_n_u8(MB), out);
let is_cont = eq(vandq_u8(b0, vdupq_n_u8(0xC0)), 0x80);
out = vbslq_u8(is_cont, vdupq_n_u8(CONT), out);
vst1q_u8(tags.as_mut_ptr().add(i), out);
if any(eq(out, MB)) {
for j in 0..16 {
if tags[i + j] == MB {
let cp = decode(text, i + j);
tags[i + j] = if cp < 0x10000 {
ATOM_TABLES.bmp_tag(cp as u16)
} else {
ATOM_TABLES.classify_char(text, i + j)
};
}
}
}
i += 16;
}
while i < n {
let b = text[i];
if b & 0xC0 == 0x80 {
tags[i] = CONT;
i += 1;
continue;
}
tags[i] = ATOM_TABLES.classify_char(text, i);
let w = char_len(b);
for j in 1..w {
if i + j < n {
tags[i + j] = CONT;
}
}
i += w;
}
}