use crate::utok;
use std::{iter::zip, pin::Pin, slice::from_ref};
pub(crate) fn collect_vocabs<'s>(
vocabs: impl IntoIterator<Item = &'s [u8]>,
unk: utok,
) -> (Vec<&'s [u8]>, Box<[utok; 256]>, usize) {
let mut bytes = Box::new([unk; 256]);
let mut total_len = 0;
let vocabs = vocabs
.into_iter()
.enumerate()
.map(|(i, piece)| {
let piece = match as_byte_token(piece) {
Some(b) => {
let b = b as usize;
bytes[b] = i as _;
from_ref(&BYTES[b])
}
None => piece,
};
total_len += piece.len();
piece
})
.collect();
(vocabs, bytes, total_len)
}
pub(crate) fn collect_vocabs_with_hint<'s>(
vocabs: impl IntoIterator<Item = &'s [u8]>,
is_byte: impl IntoIterator<Item = bool>,
unk: utok,
) -> (Vec<&'s [u8]>, Box<[utok; 256]>, usize) {
let mut bytes = Box::new([unk; 256]);
let mut total_len = 0;
let vocabs = zip(vocabs, is_byte)
.enumerate()
.map(|(i, (piece, is_byte))| {
if is_byte {
let b = as_byte_token(piece)
.unwrap_or_else(|| panic!("{piece:?} is not a valid byte token"))
as usize;
bytes[b] = i as _;
from_ref(&BYTES[b])
} else {
piece
};
total_len += piece.len();
piece
})
.collect();
(vocabs, bytes, total_len)
}
pub(crate) struct CompressedVocab {
pub vocabs: Pin<Box<[u8]>>,
pub slices: Vec<(usize, usize)>,
}
impl CompressedVocab {
pub fn new(vocabs: &[&[u8]], total_len: usize) -> Self {
let mut slices = vec![(0usize, 0usize); vocabs.len()];
let mut text_buf = Vec::<u8>::with_capacity(total_len);
let mut indices = (0..vocabs.len()).collect::<Vec<_>>();
indices.sort_unstable_by_key(|&i| -(vocabs[i].len() as isize));
for i in indices {
let v = vocabs[i];
let off = memchr::memmem::find(&text_buf, v).unwrap_or_else(|| {
let off = text_buf.len();
text_buf.extend(v);
off
});
slices[i] = (off, v.len());
}
Self {
vocabs: unsafe { Pin::new_unchecked(text_buf.into_boxed_slice()) },
slices,
}
}
}
const BYTES: [u8; 256] = {
let mut bytes = [0u8; 256];
let mut i = 0usize;
while i < 256 {
bytes[i] = i as _;
i += 1;
}
bytes
};
const fn as_byte_token(piece: &[u8]) -> Option<u8> {
match piece {
&[b'<', b'0', b'x', a, b, b'>'] if a.is_ascii_hexdigit() && b.is_ascii_hexdigit() => {
#[inline(always)]
const fn to_num(c: u8) -> u8 {
match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => unreachable!(),
}
}
Some(to_num(a) * 16 + to_num(b))
}
_ => None,
}
}