use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use crate::compressor::core::codecs::FnEncoder;
use super::util::{push_le, read_le};
use crate::compressor::core::data::{Stream, Type};
use crate::compressor::core::error::NodeError;
use crate::compressor::core::exec::NodeCx;
pub(crate) const SORT_PID: i32 = crate::graph::params::tokenize::SORT;
const TOKENIZER_PID: i32 = 1;
pub(crate) static TOKENIZE: FnEncoder = FnEncoder(tokenize);
pub(crate) static TOKENIZE_STRING: FnEncoder = FnEncoder(tokenize_string);
fn should_sort(cx: &NodeCx<'_, '_>) -> bool {
cx.int(SORT_PID).is_some_and(|value| value != 0)
}
fn index_width(size: usize, elt_width: usize) -> Result<usize, NodeError> {
if size as u64 > u64::from(u32::MAX) {
return Err(NodeError::InvalidInput(
"only 4 byte indices are supported",
));
}
if size <= 1 << 8 {
Ok(1)
} else if elt_width > 1 && size <= 1 << 16 {
Ok(2)
} else if elt_width > 2 {
Ok(4)
} else {
Err(NodeError::InvalidInput("impossible alphabet size"))
}
}
fn tokenize<'a>(cx: &mut NodeCx<'_, 'a>, inputs: &[Stream<'a>]) -> Result<(), NodeError> {
let input = &inputs[0];
if cx.copy_param(TOKENIZER_PID).is_some() {
return Err(NodeError::UnsupportedParam("custom tokenizers"));
}
let sort = should_sort(cx);
if input.ty() != Type::Numeric && sort {
return Err(NodeError::InvalidInput(
"sort only works on numeric inputs",
));
}
let width = input.width();
if !matches!(width, 1 | 2 | 4 | 8) {
return Err(NodeError::InvalidInput("element width not supported"));
}
let tokens: Vec<u64> = input.bytes().chunks_exact(width).map(read_le).collect();
let mut positions: BTreeMap<u64, usize> = BTreeMap::new();
let mut alphabet: Vec<u64> = Vec::new();
for &token in &tokens {
positions.entry(token).or_insert_with(|| {
alphabet.push(token);
alphabet.len() - 1
});
}
if sort {
alphabet.sort_unstable();
for (rank, token) in alphabet.iter().enumerate() {
positions.insert(*token, rank);
}
}
let idx_width = index_width(alphabet.len(), width)?;
let mut alphabet_bytes = Vec::with_capacity(alphabet.len() * width);
for &token in &alphabet {
push_le(&mut alphabet_bytes, token, width);
}
cx.emit(0, width, alphabet_bytes)?;
let mut index_bytes = Vec::with_capacity(tokens.len() * idx_width);
for token in &tokens {
push_le(&mut index_bytes, positions[token] as u64, idx_width);
}
cx.emit(1, idx_width, index_bytes)?;
Ok(())
}
fn tokenize_string<'a>(cx: &mut NodeCx<'_, 'a>, inputs: &[Stream<'a>]) -> Result<(), NodeError> {
let input = &inputs[0];
let sort = should_sort(cx);
let lens = input.lens();
let mut keys: Vec<&[u8]> = Vec::with_capacity(lens.len());
let mut rest = input.bytes();
for &len in lens {
let (key, tail) = rest.split_at(len as usize);
keys.push(key);
rest = tail;
}
let mut positions: BTreeMap<&[u8], usize> = BTreeMap::new();
let mut alphabet: Vec<&[u8]> = Vec::new();
for &key in &keys {
positions.entry(key).or_insert_with(|| {
alphabet.push(key);
alphabet.len() - 1
});
}
if sort {
alphabet.sort_unstable();
for (rank, key) in alphabet.iter().enumerate() {
positions.insert(key, rank);
}
}
let idx_width = if alphabet.len() as u64 > u64::from(u32::MAX) {
return Err(NodeError::InvalidInput(
"only 4 byte indices are supported",
));
} else if alphabet.len() <= 1 << 8 {
1
} else if alphabet.len() <= 1 << 16 {
2
} else {
4
};
let mut content = Vec::new();
let mut alphabet_lens = Vec::with_capacity(alphabet.len());
for key in &alphabet {
content.extend_from_slice(key);
alphabet_lens.push(key.len() as u32);
}
cx.emit_strings(0, content, alphabet_lens)?;
let mut index_bytes = Vec::with_capacity(keys.len() * idx_width);
for key in &keys {
push_le(&mut index_bytes, positions[key] as u64, idx_width);
}
cx.emit(1, idx_width, index_bytes)?;
Ok(())
}