use rio_unicode::grapheme::{grapheme_class, is_break, BreakState, GraphemeClass};
use std::sync::OnceLock;
const TABLE_LEN: usize = 0x2_0000;
static CLASS_TABLE: OnceLock<Box<[u8]>> = OnceLock::new();
static TRANSITIONS: OnceLock<Box<[u8]>> = OnceLock::new();
const CLASSES: usize = GraphemeClass::COUNT;
const STATES: usize = BreakState::COUNT;
const BREAK_BIT: u8 = 0x80;
fn class_table() -> &'static [u8] {
CLASS_TABLE.get_or_init(|| {
let mut table = vec![0u8; TABLE_LEN].into_boxed_slice();
for (cp, slot) in table.iter_mut().enumerate() {
if let Some(c) = char::from_u32(cp as u32) {
*slot = grapheme_class(c) as u8;
}
}
table
})
}
fn transitions() -> &'static [u8] {
TRANSITIONS.get_or_init(|| {
let mut table = vec![0u8; CLASSES * CLASSES * STATES].into_boxed_slice();
for prev in 0..CLASSES {
let prev_class = GraphemeClass::from_u8(prev as u8).unwrap();
for next in 0..CLASSES {
let next_class = GraphemeClass::from_u8(next as u8).unwrap();
for state in 0..STATES {
let mut break_state = BreakState::unpack(state as u8).unwrap();
let breaks = is_break(prev_class, next_class, &mut break_state);
table[(prev * CLASSES + next) * STATES + state] =
break_state.pack() | if breaks { BREAK_BIT } else { 0 };
}
}
}
table
})
}
#[inline]
pub fn class_of(c: char) -> u8 {
let cp = c as usize;
if cp < TABLE_LEN {
class_table()[cp]
} else {
grapheme_class(c) as u8
}
}
#[inline]
pub fn is_break_lut(prev: u8, next: u8, state: &mut u8) -> bool {
let entry = transitions()
[(prev as usize * CLASSES + next as usize) * STATES + *state as usize];
*state = entry & !BREAK_BIT;
entry & BREAK_BIT != 0
}
#[inline]
pub fn start_state(first: u8) -> u8 {
let entry = transitions()[(first as usize) * STATES]; entry & !BREAK_BIT
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn tables_match_reference() {
for cp in (0..TABLE_LEN as u32 + 0x100).step_by(7) {
if let Some(c) = char::from_u32(cp) {
assert_eq!(class_of(c), grapheme_class(c) as u8, "class of U+{cp:04X}");
}
}
for prev in 0..CLASSES as u8 {
for next in 0..CLASSES as u8 {
for state in 0..STATES as u8 {
let mut reference = BreakState::unpack(state).unwrap();
let expected = is_break(
GraphemeClass::from_u8(prev).unwrap(),
GraphemeClass::from_u8(next).unwrap(),
&mut reference,
);
let mut packed = state;
let got = is_break_lut(prev, next, &mut packed);
assert_eq!(got, expected, "break ({prev},{next},{state})");
assert_eq!(packed, reference.pack(), "state ({prev},{next},{state})");
}
}
}
}
#[test]
fn start_state_matches_reference() {
for class in 0..CLASSES as u8 {
let reference = BreakState::start(GraphemeClass::from_u8(class).unwrap());
assert_eq!(start_state(class), reference.pack(), "start({class})");
}
}
}