#![allow(unsafe_op_in_unsafe_fn)]
use super::atom_tables::ATOM_TABLES;
use super::{Atom, CONT, MB, char_len};
use core::arch::wasm32::*;
const CJK_TAG: u8 = Atom::Letter as u8;
const HAN_TAG: u8 = Atom::HanLetter as u8;
#[inline]
fn decode(t: &[u8], i: usize) -> u32 {
let b = t[i] as u32;
match 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)
}
}
}
#[inline(always)]
unsafe fn eq(bytes: v128, byte: u8) -> v128 {
u8x16_eq(bytes, u8x16_splat(byte))
}
#[inline(always)]
unsafe fn ge(bytes: v128, lo: u8) -> v128 {
u8x16_ge(bytes, u8x16_splat(lo))
}
#[inline(always)]
unsafe fn le(bytes: v128, hi: u8) -> v128 {
u8x16_le(bytes, u8x16_splat(hi))
}
#[inline(always)]
unsafe fn in_range(bytes: v128, lo: u8, hi: u8) -> v128 {
v128_and(ge(bytes, lo), le(bytes, hi))
}
#[inline(always)]
unsafe fn hmax(v: v128) -> u8 {
let v = u8x16_max(
v,
u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, 12, 13, 14, 15>(v, v),
);
let v = u8x16_max(
v,
u8x16_shuffle::<4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7>(v, v),
);
let v = u8x16_max(
v,
u8x16_shuffle::<2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3>(v, v),
);
let v = u8x16_max(
v,
u8x16_shuffle::<1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1>(v, v),
);
u8x16_extract_lane::<0>(v)
}
#[inline(always)]
unsafe fn hmin(v: v128) -> u8 {
let v = u8x16_min(
v,
u8x16_shuffle::<8, 9, 10, 11, 12, 13, 14, 15, 8, 9, 10, 11, 12, 13, 14, 15>(v, v),
);
let v = u8x16_min(
v,
u8x16_shuffle::<4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7, 4, 5, 6, 7>(v, v),
);
let v = u8x16_min(
v,
u8x16_shuffle::<2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3, 2, 3>(v, v),
);
let v = u8x16_min(
v,
u8x16_shuffle::<1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1>(v, v),
);
u8x16_extract_lane::<0>(v)
}
#[inline(always)]
unsafe fn lookup128(lo: &[u8; 64], hi: &[u8; 64], index: v128) -> v128 {
let mut acc = u8x16_splat(0);
for sub in 0..8usize {
let base = if sub < 4 {
lo.as_ptr().add(sub * 16)
} else {
hi.as_ptr().add((sub - 4) * 16)
};
let table = v128_load(base as *const v128);
acc = v128_or(
acc,
u8x16_swizzle(table, i8x16_sub(index, u8x16_splat((sub * 16) as u8))),
);
}
acc
}
#[inline(always)]
unsafe fn lookup256(table: *const u8, index: v128) -> v128 {
let mut acc = u8x16_splat(0);
for sub in 0..16usize {
let chunk = v128_load(table.add(sub * 16) as *const v128);
acc = v128_or(
acc,
u8x16_swizzle(chunk, i8x16_sub(index, u8x16_splat((sub * 16) as u8))),
);
}
acc
}
pub unsafe fn classify_wasm(text: &[u8], tags: &mut [u8]) {
let n = text.len();
let mut mb_seen = false;
let mut i = 0usize;
let ones = u8x16_splat(0xFF);
let zeros = u8x16_splat(0);
while i + 32 <= n {
let b0 = v128_load(text.as_ptr().add(i) as *const v128);
if u8x16_bitmask(b0) == 0 {
let out = lookup128(&ATOM_TABLES.ascii_lo, &ATOM_TABLES.ascii_hi, b0);
v128_store(tags.as_mut_ptr().add(i) as *mut v128, out);
i += 16;
continue;
}
let next = v128_load(text.as_ptr().add(i + 16) as *const v128);
let b1 = u8x16_shuffle::<1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16>(b0, next); let b2 = u8x16_shuffle::<2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17>(b0, next); let mut out = lookup128(&ATOM_TABLES.ascii_lo, &ATOM_TABLES.ascii_hi, b0);
let mut resolved = zeros;
let is_lead2 = u8x16_eq(v128_and(b0, u8x16_splat(0xE0)), u8x16_splat(0xC0));
if v128_any_true(is_lead2) {
let group_index = v128_or(
i8x16_shl(v128_and(b0, u8x16_splat(3)), 6),
v128_and(b1, u8x16_splat(0x3F)),
);
let min_lead = hmin(v128_bitselect(b0, ones, is_lead2)); let max_lead = hmax(v128_bitselect(b0, zeros, is_lead2));
let lead_group = u8x16_shr(b0, 2); let mut tags2 = u8x16_splat(MB);
let mut group = min_lead >> 2;
while group <= (max_lead >> 2) {
let this_group = v128_and(is_lead2, u8x16_eq(lead_group, u8x16_splat(group)));
if v128_any_true(this_group) {
let group_table =
ATOM_TABLES.group_tables[(group & 7) as usize].as_ptr() as *const u8;
tags2 = v128_bitselect(lookup256(group_table, group_index), tags2, this_group);
}
group += 1;
}
out = v128_bitselect(tags2, out, is_lead2);
resolved = is_lead2;
}
let in_cjk_leads = in_range(b0, 0xE3, 0xED);
if v128_any_true(in_cjk_leads) {
let han = v128_andnot(
in_range(b0, 0xE4, 0xE9),
v128_and(eq(b0, 0xE4), eq(b1, 0xB7)),
);
let hangul = v128_or(
v128_or(
in_range(b0, 0xEB, 0xEC),
v128_and(eq(b0, 0xEA), ge(b1, 0xB0)),
),
v128_and(eq(b0, 0xED), le(b1, 0x9D)),
);
let hole_3040 = v128_and(eq(b1, 0x81), eq(b2, 0x80));
let hole_309x = v128_and(
eq(b1, 0x82),
v128_or(in_range(b2, 0x97, 0x9C), eq(b2, 0xA0)),
);
let hole_30fb = v128_and(eq(b1, 0x83), eq(b2, 0xBB));
let kana = v128_andnot(
v128_and(eq(b0, 0xE3), in_range(b1, 0x81, 0x83)),
v128_or(v128_or(hole_3040, hole_309x), hole_30fb),
);
let is_cjk_letter = v128_or(v128_or(han, hangul), kana);
out = v128_bitselect(u8x16_splat(CJK_TAG), out, is_cjk_letter);
out = v128_bitselect(u8x16_splat(HAN_TAG), out, han);
resolved = v128_or(resolved, is_cjk_letter);
}
let is_lead3 = v128_andnot(in_range(b0, 0xE0, 0xEF), resolved);
if v128_any_true(is_lead3) {
let block_index = v128_or(
i8x16_shl(v128_and(b1, u8x16_splat(1)), 6),
v128_and(b2, u8x16_splat(0x3F)),
);
let pair = u8x16_shr(b1, 1); let mut tags3 = u8x16_splat(MB);
let mut unresolved = is_lead3;
while v128_any_true(unresolved) {
let lead = hmin(v128_bitselect(b0, ones, unresolved)); let lead_lanes = v128_and(unresolved, eq(b0, lead));
let min_pair = hmin(v128_bitselect(pair, ones, lead_lanes)); let block_lanes = v128_and(lead_lanes, u8x16_eq(pair, u8x16_splat(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 {
u8x16_splat(uniform_tag) } else {
let (lo, hi) = &ATOM_TABLES.fast3_mixed[ATOM_TABLES.fast3_slot[block] as usize];
lookup128(lo, hi, block_index)
};
tags3 = v128_bitselect(block_tags, tags3, block_lanes);
unresolved = v128_andnot(unresolved, block_lanes); }
out = v128_bitselect(tags3, out, is_lead3);
resolved = v128_or(resolved, v128_andnot(is_lead3, eq(tags3, MB)));
}
let stray_lead = v128_andnot(ge(b0, 0xC0), resolved);
out = v128_bitselect(u8x16_splat(MB), out, stray_lead);
let is_cont = u8x16_eq(v128_and(b0, u8x16_splat(0xC0)), u8x16_splat(0x80));
out = v128_bitselect(u8x16_splat(CONT), out, is_cont);
if v128_any_true(eq(out, MB)) {
mb_seen = true;
}
v128_store(tags.as_mut_ptr().add(i) as *mut v128, out);
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);
let mut j = 1;
while j < w && i + j < n {
tags[i + j] = CONT;
j += 1;
}
i += w;
}
if mb_seen {
let mut pos = 0;
while pos < n {
if tags[pos] == MB {
let cp = decode(text, pos);
tags[pos] = if cp < 0x10000 {
ATOM_TABLES.bmp_tag(cp as u16)
} else {
ATOM_TABLES.classify_char(text, pos)
};
}
pos += 1;
}
}
}