use crate::unicode_data::{UNICODE_MAP_LOWERCASE, UNICODE_RANGES_FLAGS, UNICODE_SET_WHITESPACE};
use std::collections::HashMap;
use std::sync::OnceLock;
const MAX_CODEPOINTS: usize = 0x110000;
#[allow(dead_code)]
mod flag {
pub const UNDEFINED: u16 = 0x0001;
pub const NUMBER: u16 = 0x0002; pub const LETTER: u16 = 0x0004; pub const SEPARATOR: u16 = 0x0008; pub const ACCENT_MARK: u16 = 0x0010; pub const PUNCTUATION: u16 = 0x0020; pub const SYMBOL: u16 = 0x0040; pub const CONTROL: u16 = 0x0080; pub const WHITESPACE: u16 = 0x0100; }
pub use flag::{
ACCENT_MARK as FLAG_ACCENT_MARK, LETTER as FLAG_LETTER, NUMBER as FLAG_NUMBER,
UNDEFINED as FLAG_UNDEFINED, WHITESPACE as FLAG_WHITESPACE,
};
#[derive(Clone, Copy, Default)]
pub struct CptFlags(pub u16);
impl CptFlags {
#[inline]
pub fn is_number(self) -> bool {
self.0 & FLAG_NUMBER != 0
}
#[inline]
pub fn is_letter(self) -> bool {
self.0 & FLAG_LETTER != 0
}
#[inline]
pub fn is_accent_mark(self) -> bool {
self.0 & FLAG_ACCENT_MARK != 0
}
#[inline]
pub fn is_whitespace(self) -> bool {
self.0 & FLAG_WHITESPACE != 0
}
#[inline]
pub fn as_uint(self) -> u16 {
self.0
}
}
fn cpt_flags_table() -> &'static Vec<u16> {
static TABLE: OnceLock<Vec<u16>> = OnceLock::new();
TABLE.get_or_init(|| {
let mut flags = vec![FLAG_UNDEFINED; MAX_CODEPOINTS];
for i in 1..UNICODE_RANGES_FLAGS.len() {
let (ini, fl) = UNICODE_RANGES_FLAGS[i - 1];
let (end, _) = UNICODE_RANGES_FLAGS[i];
for cpt in ini..end {
flags[cpt as usize] = fl;
}
}
for &cpt in UNICODE_SET_WHITESPACE.iter() {
flags[cpt as usize] |= FLAG_WHITESPACE;
}
flags
})
}
#[inline]
pub fn cpt_flags_from_cpt(cpt: u32) -> CptFlags {
let table = cpt_flags_table();
if (cpt as usize) < MAX_CODEPOINTS {
CptFlags(table[cpt as usize])
} else {
CptFlags(FLAG_UNDEFINED)
}
}
#[inline]
pub fn tolower(cpt: u32) -> u32 {
match UNICODE_MAP_LOWERCASE.binary_search_by(|&(k, _)| k.cmp(&cpt)) {
Ok(idx) => UNICODE_MAP_LOWERCASE[idx].1,
Err(_) => cpt,
}
}
fn byte_unicode_maps() -> &'static (Vec<char>, HashMap<char, u8>) {
static MAPS: OnceLock<(Vec<char>, HashMap<char, u8>)> = OnceLock::new();
MAPS.get_or_init(|| {
let mut byte_to_char: Vec<Option<char>> = vec![None; 256];
let mut set = |ch: u32| {
byte_to_char[ch as usize] = Some(char::from_u32(ch).unwrap());
};
for ch in 0x21..=0x7E {
set(ch);
}
for ch in 0xA1..=0xAC {
set(ch);
}
for ch in 0xAE..=0xFF {
set(ch);
}
let mut n: u32 = 0;
for ch in 0..256u32 {
if byte_to_char[ch as usize].is_none() {
byte_to_char[ch as usize] = Some(char::from_u32(256 + n).unwrap());
n += 1;
}
}
let b2c: Vec<char> = byte_to_char.into_iter().map(|c| c.unwrap()).collect();
let mut c2b: HashMap<char, u8> = HashMap::with_capacity(256);
for (b, &c) in b2c.iter().enumerate() {
c2b.insert(c, b as u8);
}
(b2c, c2b)
})
}
#[inline]
pub fn byte_to_unicode(byte: u8) -> char {
byte_unicode_maps().0[byte as usize]
}
#[inline]
pub fn unicode_to_byte(c: char) -> Option<u8> {
byte_unicode_maps().1.get(&c).copied()
}
pub fn byte_encode(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for &b in s.as_bytes() {
out.push(byte_to_unicode(b));
}
out
}
pub fn split_qwen35(text: &str) -> Vec<String> {
let cpts: Vec<u32> = text.chars().map(|c| c as u32).collect();
let cpt_bytes: Vec<usize> = text.chars().map(|c| c.len_utf8()).collect();
let n = cpts.len();
const OOR: u32 = 0xFFFF_FFFF;
let get_cpt = |pos: usize| -> u32 {
if pos < n {
cpts[pos]
} else {
OOR
}
};
let get_flags = |pos: usize| -> CptFlags {
if pos < n {
cpt_flags_from_cpt(cpts[pos])
} else {
CptFlags::default()
}
};
let mut lens: Vec<usize> = Vec::new(); let mut prev_end = 0usize;
let add_token = |end: usize, prev_end: &mut usize, lens: &mut Vec<usize>| -> usize {
debug_assert!(*prev_end <= end && end <= n);
let len = end - *prev_end;
if len > 0 {
lens.push(len);
}
*prev_end = end;
len
};
let mut pos = 0usize;
while pos < n {
let cpt = get_cpt(pos);
let flags = get_flags(pos);
if cpt == b'\'' as u32 && pos + 1 < n {
let cpt_next = tolower(get_cpt(pos + 1));
if cpt_next == 's' as u32
|| cpt_next == 't' as u32
|| cpt_next == 'm' as u32
|| cpt_next == 'd' as u32
{
pos += add_token(pos + 2, &mut prev_end, &mut lens);
continue;
}
if pos + 2 < n {
let cpt_nn = tolower(get_cpt(pos + 2));
if (cpt_next == 'r' as u32 && cpt_nn == 'e' as u32)
|| (cpt_next == 'v' as u32 && cpt_nn == 'e' as u32)
|| (cpt_next == 'l' as u32 && cpt_nn == 'l' as u32)
{
pos += add_token(pos + 3, &mut prev_end, &mut lens);
continue;
}
}
}
if !(cpt == '\r' as u32 || cpt == '\n' as u32 || flags.is_number()) {
if flags.is_letter()
|| flags.is_accent_mark()
|| get_flags(pos + 1).is_accent_mark()
|| get_flags(pos + 1).is_letter()
{
pos += 1;
while get_flags(pos).is_letter() || get_flags(pos).is_accent_mark() {
pos += 1;
}
add_token(pos, &mut prev_end, &mut lens);
continue;
}
}
if flags.is_number() {
pos += 1;
add_token(pos, &mut prev_end, &mut lens);
continue;
}
let mut flags2 = if cpt == ' ' as u32 {
get_flags(pos + 1)
} else {
flags
};
if !(flags2.is_whitespace() || flags2.is_letter() || flags2.is_accent_mark() || flags2.is_number())
&& flags.as_uint() != 0
{
pos += (cpt == ' ' as u32) as usize;
while !(flags2.is_whitespace()
|| flags2.is_letter()
|| flags2.is_accent_mark()
|| flags2.is_number())
&& flags2.as_uint() != 0
{
pos += 1;
flags2 = get_flags(pos);
}
let mut cpt2 = get_cpt(pos);
while cpt2 == '\r' as u32 || cpt2 == '\n' as u32 {
pos += 1;
cpt2 = get_cpt(pos);
}
add_token(pos, &mut prev_end, &mut lens);
continue;
}
let mut num_ws = 0usize;
let mut last_end_rn = 0usize;
while get_flags(pos + num_ws).is_whitespace() {
let cpt2 = get_cpt(pos + num_ws);
if cpt2 == '\r' as u32 || cpt2 == '\n' as u32 {
last_end_rn = pos + num_ws + 1;
}
num_ws += 1;
}
if last_end_rn > 0 {
pos = last_end_rn;
add_token(pos, &mut prev_end, &mut lens);
continue;
}
if num_ws > 1 && get_cpt(pos + num_ws) != OOR {
pos += num_ws - 1;
add_token(pos, &mut prev_end, &mut lens);
continue;
}
if num_ws > 0 {
pos += num_ws;
add_token(pos, &mut prev_end, &mut lens);
continue;
}
pos += 1;
add_token(pos, &mut prev_end, &mut lens);
}
let mut words = Vec::with_capacity(lens.len());
let mut cpt_i = 0usize;
let mut byte_i = 0usize;
for &len in &lens {
let mut nbytes = 0usize;
for k in 0..len {
nbytes += cpt_bytes[cpt_i + k];
}
words.push(text[byte_i..byte_i + nbytes].to_string());
cpt_i += len;
byte_i += nbytes;
}
words
}