use crate::core::encoder::Encoder;
pub(crate) fn merge_ranks<'a>(
merged: Vec<String>,
vocab_in_id_order: impl Iterator<Item = &'a str>,
) -> Encoder {
let bytes: Vec<&[u8]> = merged.iter().map(|s| s.as_bytes()).collect();
merge_ranks_bytes(
bytes.iter().copied(),
bytes.len(),
vocab_in_id_order.map(str::as_bytes),
)
}
pub(crate) fn merge_ranks_bytes<'m, 'v>(
merged: impl Iterator<Item = &'m [u8]> + Clone,
merged_len: usize,
vocab_in_id_order: impl Iterator<Item = &'v [u8]>,
) -> Encoder {
let mut merge_set: rustc_hash::FxHashSet<&'m [u8]> =
rustc_hash::FxHashSet::with_capacity_and_hasher(merged_len, rustc_hash::FxBuildHasher);
merge_set.extend(merged.clone());
let mut ranks: Encoder = Encoder::with_capacity(merged_len + 512);
for token in vocab_in_id_order.filter(|t| !merge_set.contains(t)) {
let next = ranks.len() as u32;
ranks.insert_if_absent(token, next);
}
drop(merge_set);
let base_count = ranks.len() as u32;
for (i, token) in merged.enumerate() {
ranks.insert_if_absent(token, base_count + i as u32);
}
ranks
}
const SHORT_MIN: usize = 3;
const SHORT_MAX: usize = 4;
const SHORT_MAX_RANK: u32 = u32::MAX >> 1;
pub(crate) struct BytePairRanks {
ranks: Box<[u32]>,
short: Option<ShortRanks>,
}
impl BytePairRanks {
pub(crate) fn build(map: &Encoder) -> Self {
let mut ranks = vec![u32::MAX; 256 * 256];
for (key, rank) in map {
if let [hi, lo] = key[..] {
ranks[(hi as usize) << 8 | lo as usize] = rank;
}
}
Self {
ranks: ranks.into_boxed_slice(),
short: ShortRanks::build(map),
}
}
#[inline]
fn get(&self, hi: u8, lo: u8) -> u32 {
self.ranks[(hi as usize) << 8 | lo as usize]
}
}
struct ShortRanks {
slots: Box<[u64]>,
mask: usize,
}
impl ShortRanks {
const EMPTY: u64 = u64::MAX;
#[inline]
fn pack_key(key: &[u8]) -> u64 {
let mut bytes = [0u8; 4];
bytes[..key.len()].copy_from_slice(key);
u32::from_le_bytes(bytes) as u64 | ((key.len() - SHORT_MIN) as u64) << 32
}
#[inline]
fn slot_of(&self, packed_key: u64) -> usize {
(packed_key.wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as usize & self.mask
}
fn build(map: &Encoder) -> Option<Self> {
let count = map
.keys()
.filter(|k| (SHORT_MIN..=SHORT_MAX).contains(&k.len()))
.count();
let capacity = (count * 2).next_power_of_two().max(16);
let mut table = Self {
slots: vec![Self::EMPTY; capacity].into_boxed_slice(),
mask: capacity - 1,
};
for (key, rank) in map {
if !(SHORT_MIN..=SHORT_MAX).contains(&key.len()) {
continue;
}
if rank > SHORT_MAX_RANK {
return None;
}
let packed_key = Self::pack_key(key);
let mut slot = table.slot_of(packed_key);
while table.slots[slot] != Self::EMPTY {
slot = (slot + 1) & table.mask;
}
table.slots[slot] = packed_key | (rank as u64) << 33;
}
Some(table)
}
#[inline]
fn get(&self, key: &[u8]) -> u32 {
let packed_key = Self::pack_key(key);
let mut slot = self.slot_of(packed_key);
loop {
let entry = self.slots[slot];
if entry == Self::EMPTY {
return u32::MAX;
}
if entry & 0x1_FFFF_FFFF == packed_key {
return (entry >> 33) as u32;
}
slot = (slot + 1) & self.mask;
}
}
}
const PAIR_ID_BITS: u32 = 20;
const PAIR_ID_LIMIT: u32 = 1 << PAIR_ID_BITS;
pub(crate) struct PairRanks {
slots: Box<[u64]>,
mask: usize,
dense: Box<[u32]>,
absent: Box<[u64]>,
absent_mask: usize,
rank_by_id: Option<Box<[u32]>>,
byte_ids: Box<[u32; 256]>,
pair_byte_ids: Box<[u32]>,
symbol_ids: SymbolIds,
seeds_by_char: bool,
raw_byte_ids: Option<Box<[u32; 256]>>,
raw_pair_ids: Option<Box<[u32]>>,
char_seeds: Option<CharSeeds>,
}
impl PairRanks {
#[inline]
pub(crate) fn byte_id(&self, byte: u8) -> u32 {
self.byte_ids[byte as usize]
}
#[inline]
pub(crate) fn seed_id(&self, bytes: &[u8]) -> u32 {
match bytes {
[byte] => self.byte_ids[*byte as usize],
[hi, lo] => self.pair_byte_ids[(*hi as usize) << 8 | *lo as usize],
_ => self.symbol_ids.get(bytes),
}
}
#[inline]
pub(crate) fn raw_chunk_id(&self, bytes: &[u8]) -> u32 {
match *bytes {
[byte] => self.raw_byte_id(byte),
[hi, lo] => match &self.raw_pair_ids {
Some(ids) => ids[(hi as usize) << 8 | lo as usize],
None => u32::MAX,
},
_ => u32::MAX,
}
}
#[inline]
pub(crate) fn seeds_by_char(&self) -> bool {
self.seeds_by_char
}
#[inline]
pub(crate) fn seeds_raw(&self) -> bool {
self.raw_byte_ids.is_some()
}
#[inline]
pub(crate) fn char_seed(&self, bytes: &[u8]) -> u32 {
match &self.char_seeds {
Some(seeds) => seeds.get(bytes),
None => u32::MAX,
}
}
#[inline]
pub(crate) fn seeds_chars(&self) -> bool {
self.char_seeds.is_some()
}
#[inline]
pub(crate) fn raw_byte_id(&self, byte: u8) -> u32 {
match &self.raw_byte_ids {
Some(ids) => ids[byte as usize],
None => u32::MAX,
}
}
}
const DENSE_IDS: u32 = 512;
impl PairRanks {
const EMPTY: u64 = u64::MAX;
pub(crate) fn build(
rank_map: &Encoder,
id_encoder: &Encoder,
raw_encoder: Option<&Encoder>,
) -> Option<Self> {
let mut max_id = 0u32;
for (_, id) in id_encoder {
if id >= PAIR_ID_LIMIT {
return None;
}
max_id = max_id.max(id);
}
let mut pairs: Vec<(u64, u32)> = Vec::with_capacity(rank_map.len() * 2);
for (key, _) in rank_map {
let Some(merged) = id_encoder.get(key) else {
continue;
};
for split in 1..key.len() {
let Some(left) = id_encoder.get(&key[..split]) else {
continue;
};
let Some(right) = id_encoder.get(&key[split..]) else {
continue;
};
pairs.push((Self::pack(left, right), merged));
}
}
let seeds_by_char = (0..=u8::MAX).any(|byte| id_encoder.get(&[byte][..]).is_none());
if seeds_by_char && std::ptr::eq(rank_map, id_encoder) {
return None;
}
let capacity = (pairs.len() * 2).next_power_of_two().max(16);
let filter_bits = (pairs.len() * 8).next_power_of_two().max(64);
let mut table = Self {
slots: vec![Self::EMPTY; capacity].into_boxed_slice(),
mask: capacity - 1,
absent: vec![0u64; filter_bits / 64].into_boxed_slice(),
absent_mask: filter_bits - 1,
dense: vec![u32::MAX; (DENSE_IDS * DENSE_IDS) as usize].into_boxed_slice(),
rank_by_id: None,
byte_ids: Box::new([u32::MAX; 256]),
pair_byte_ids: vec![u32::MAX; 256 * 256].into_boxed_slice(),
symbol_ids: SymbolIds::build(id_encoder)?,
seeds_by_char,
char_seeds: None,
raw_pair_ids: raw_encoder.map(|raw| {
let mut ids = vec![u32::MAX; 256 * 256].into_boxed_slice();
for (key, id) in raw {
if let [hi, lo] = key {
ids[(*hi as usize) << 8 | *lo as usize] = id;
}
}
ids
}),
raw_byte_ids: raw_encoder.and_then(|raw| {
let mut ids = Box::new([u32::MAX; 256]);
for (byte, slot) in ids.iter_mut().enumerate() {
*slot = raw.get(&[byte as u8][..])?;
}
Some(ids)
}),
};
for (key, merged) in pairs {
let (left, right) = (key >> PAIR_ID_BITS, key & ((1 << PAIR_ID_BITS) - 1));
if left < DENSE_IDS as u64 && right < DENSE_IDS as u64 {
table.dense[(left * DENSE_IDS as u64 + right) as usize] = merged;
continue;
}
let bit = Self::filter_bit(key, table.absent_mask);
table.absent[bit >> 6] |= 1 << (bit & 63);
let mut slot = table.slot_of(key);
while table.slots[slot] != Self::EMPTY {
slot = (slot + 1) & table.mask;
}
table.slots[slot] = key | (merged as u64) << (2 * PAIR_ID_BITS);
}
for byte in 0..=u8::MAX {
if let Some(id) = id_encoder.get(&[byte][..]) {
table.byte_ids[byte as usize] = id;
}
}
for (key, id) in id_encoder {
if let [hi, lo] = key {
table.pair_byte_ids[(*hi as usize) << 8 | *lo as usize] = id;
}
}
let ranks = (!std::ptr::eq(rank_map, id_encoder)).then(|| {
let mut ranks = vec![u32::MAX; max_id as usize + 1].into_boxed_slice();
for (key, rank) in rank_map {
if let Some(id) = id_encoder.get(key) {
ranks[id as usize] = rank;
}
}
ranks
});
let rank_of = |id: u32| match &ranks {
Some(ranks) => ranks[id as usize],
None => id,
};
table.char_seeds = raw_encoder.map(|raw| Self::char_seeds(raw, &rank_of));
if let Some(ranks) = ranks {
let ordered = ranks
.iter()
.filter(|&&rank| rank != u32::MAX)
.is_sorted_by(|a, b| a < b);
if !ordered {
table.rank_by_id = Some(ranks);
}
}
Some(table)
}
fn char_seeds(raw: &Encoder, rank_of: &impl Fn(u32) -> u32) -> CharSeeds {
let mut min_suffix: rustc_hash::FxHashMap<u32, u32> = rustc_hash::FxHashMap::default();
let mut min_prefix: rustc_hash::FxHashMap<u32, u32> = rustc_hash::FxHashMap::default();
for (key, id) in raw {
let rank = rank_of(id);
for len in 1..key.len().min(SYMBOL_MAX) {
let head = SymbolIds::pack_key(&key[..len]) as u32;
let tail = SymbolIds::pack_key(&key[key.len() - len..]) as u32;
let slot = min_prefix.entry(head).or_insert(u32::MAX);
*slot = (*slot).min(rank);
let slot = min_suffix.entry(tail).or_insert(u32::MAX);
*slot = (*slot).min(rank);
}
}
let mut safe: Vec<(&[u8], u32)> = Vec::new();
for (key, id) in raw {
if key.len() > SYMBOL_MAX || !Self::is_one_character(key) {
continue;
}
let rank = rank_of(id);
let reachable = (1..key.len()).any(|at| {
let head = SymbolIds::pack_key(&key[..at]) as u32;
let tail = SymbolIds::pack_key(&key[at..]) as u32;
min_suffix.get(&head).is_some_and(|&r| r < rank)
|| min_prefix.get(&tail).is_some_and(|&r| r < rank)
});
if !reachable {
safe.push((key, id));
}
}
CharSeeds::new(&safe)
}
fn is_one_character(key: &[u8]) -> bool {
std::str::from_utf8(key).is_ok_and(|text| text.chars().nth(1).is_none())
}
#[inline]
fn pack(left: u32, right: u32) -> u64 {
(left as u64) << PAIR_ID_BITS | right as u64
}
#[inline]
fn slot_of(&self, key: u64) -> usize {
(key.wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as usize & self.mask
}
#[inline]
fn filter_bit(key: u64, mask: usize) -> usize {
(key.wrapping_mul(0xD6E8_FEB8_6659_FD93) >> 24) as usize & mask
}
#[inline]
fn merged(&self, left: u32, right: u32) -> u32 {
if left < DENSE_IDS && right < DENSE_IDS {
return self.dense[(left * DENSE_IDS + right) as usize];
}
let key = Self::pack(left, right);
let bit = Self::filter_bit(key, self.absent_mask);
if self.absent[bit >> 6] & (1 << (bit & 63)) == 0 {
return u32::MAX;
}
let mut slot = self.slot_of(key);
loop {
let entry = self.slots[slot];
if entry == Self::EMPTY {
return u32::MAX;
}
if entry & ((1 << (2 * PAIR_ID_BITS)) - 1) == key {
return (entry >> (2 * PAIR_ID_BITS)) as u32;
}
slot = (slot + 1) & self.mask;
}
}
#[inline]
fn rank(&self, id: u32) -> u32 {
match &self.rank_by_id {
Some(ranks) => ranks[id as usize],
None => id,
}
}
}
const TWO_BYTE_BASE: u32 = 0x80;
const THREE_BYTE_BASE: u32 = 0x800;
const FOUR_BYTE_BASE: u32 = 0x1_0000;
#[derive(Clone)]
struct CharSeeds {
two: Box<[u32]>,
three: Box<[u32]>,
}
impl CharSeeds {
fn new(entries: &[(&[u8], u32)]) -> Self {
let mut seeds = Self {
two: vec![u32::MAX; (THREE_BYTE_BASE - TWO_BYTE_BASE) as usize].into_boxed_slice(),
three: vec![u32::MAX; (FOUR_BYTE_BASE - THREE_BYTE_BASE) as usize].into_boxed_slice(),
};
for (key, id) in entries {
let Some(codepoint) = std::str::from_utf8(key)
.ok()
.and_then(|text| text.chars().next())
.map(u32::from)
else {
continue;
};
match key.len() {
2 => seeds.two[(codepoint - TWO_BYTE_BASE) as usize] = *id,
3 => seeds.three[(codepoint - THREE_BYTE_BASE) as usize] = *id,
_ => {}
}
}
seeds
}
#[inline]
fn get(&self, bytes: &[u8]) -> u32 {
match *bytes {
[b0, b1] => {
let codepoint = (b0 as u32 & 0x1F) << 6 | (b1 as u32 & 0x3F);
match codepoint.checked_sub(TWO_BYTE_BASE) {
Some(at) => self.two[at as usize],
None => u32::MAX,
}
}
[b0, b1, b2] => {
let codepoint =
(b0 as u32 & 0x0F) << 12 | (b1 as u32 & 0x3F) << 6 | (b2 as u32 & 0x3F);
match codepoint.checked_sub(THREE_BYTE_BASE) {
Some(at) => self.three[at as usize],
None => u32::MAX,
}
}
_ => u32::MAX,
}
}
}
const SYMBOL_MAX: usize = 4;
struct SymbolIds {
slots: Box<[u64]>,
mask: usize,
}
impl SymbolIds {
const EMPTY: u64 = u64::MAX;
#[inline]
fn pack_key(key: &[u8]) -> u64 {
let mut bytes = [0u8; 4];
bytes[..key.len()].copy_from_slice(key);
u32::from_le_bytes(bytes) as u64 | ((key.len() - 1) as u64) << 32
}
#[inline]
fn slot_of(&self, packed_key: u64) -> usize {
(packed_key.wrapping_mul(0x9E37_79B9_7F4A_7C15) >> 32) as usize & self.mask
}
fn build(map: &Encoder) -> Option<Self> {
let count = map.keys().filter(|k| k.len() <= SYMBOL_MAX).count();
let capacity = (count * 2).next_power_of_two().max(16);
let mut table = Self {
slots: vec![Self::EMPTY; capacity].into_boxed_slice(),
mask: capacity - 1,
};
for (key, id) in map {
if key.is_empty() || key.len() > SYMBOL_MAX {
continue;
}
if id >= PAIR_ID_LIMIT {
return None;
}
let packed_key = Self::pack_key(key);
let mut slot = table.slot_of(packed_key);
while table.slots[slot] != Self::EMPTY {
slot = (slot + 1) & table.mask;
}
table.slots[slot] = packed_key | (id as u64) << 34;
}
Some(table)
}
#[inline]
fn get(&self, key: &[u8]) -> u32 {
if key.is_empty() || key.len() > SYMBOL_MAX {
return u32::MAX;
}
let packed_key = Self::pack_key(key);
let mut slot = self.slot_of(packed_key);
loop {
let entry = self.slots[slot];
if entry == Self::EMPTY {
return u32::MAX;
}
if entry & 0x3_FFFF_FFFF == packed_key {
return (entry >> 34) as u32;
}
slot = (slot + 1) & self.mask;
}
}
}
#[derive(Clone, Copy)]
pub(crate) struct RankLookup<'a> {
map: &'a Encoder,
pairs: Option<&'a BytePairRanks>,
short: Option<&'a ShortRanks>,
by_id: Option<&'a PairRanks>,
}
impl<'a> RankLookup<'a> {
pub(crate) fn new(map: &'a Encoder) -> Self {
Self {
map,
pairs: None,
short: None,
by_id: None,
}
}
pub(crate) fn with_pairs(map: &'a Encoder, pairs: &'a BytePairRanks) -> Self {
Self {
map,
pairs: Some(pairs),
short: pairs.short.as_ref(),
by_id: None,
}
}
pub(crate) fn with_ids(mut self, by_id: Option<&'a PairRanks>) -> Self {
self.by_id = by_id;
self
}
pub(crate) fn without_ids(mut self) -> Self {
self.by_id = None;
self
}
#[inline]
pub(crate) fn by_id(&self) -> Option<&'a PairRanks> {
self.by_id
}
#[inline]
pub(crate) fn pair(&self, table: &PairRanks, left: u32, right: u32) -> (u32, u32) {
let merged = table.merged(left, right);
match merged {
u32::MAX => (u32::MAX, u32::MAX),
id => (table.rank(id), id),
}
}
#[inline]
pub(crate) fn get(&self, key: &[u8]) -> u32 {
if let Some(pairs) = self.pairs {
if let [hi, lo] = key {
return pairs.get(*hi, *lo);
}
}
if let Some(short) = self.short {
if (SHORT_MIN..=SHORT_MAX).contains(&key.len()) {
return short.get(key);
}
}
self.map.get(key).unwrap_or(u32::MAX)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn encoder(entries: &[(&[u8], u32)]) -> Encoder {
entries.iter().copied().collect()
}
#[test]
fn a_trailing_zero_byte_is_not_the_shorter_key() {
let map = encoder(&[(b"abc", 7), (b"abc\0", 9)]);
let short = ShortRanks::build(&map).unwrap();
assert_eq!(short.get(b"abc"), 7);
assert_eq!(short.get(b"abc\0"), 9);
}
#[test]
fn the_table_answers_for_every_short_key_and_only_those() {
let map = encoder(&[(b"ab", 1), (b"xyz", 2), (b"wxyz", 3), (b"abcde", 4)]);
let short = ShortRanks::build(&map).unwrap();
assert_eq!(short.get(b"xyz"), 2);
assert_eq!(short.get(b"wxyz"), 3);
assert_eq!(short.get(b"qqq"), u32::MAX);
}
#[test]
fn an_unpackable_rank_gives_up_on_the_whole_table() {
assert!(ShortRanks::build(&encoder(&[(b"abc", u32::MAX)])).is_none());
}
#[test]
fn the_fronted_lookup_agrees_with_the_map() {
let entries: &[(&[u8], u32)] = &[
(b"ab", 1),
(b"abc", 2),
(b"abcd", 3),
(b"abcde", 4),
(b"abcdef", 5),
];
let map = encoder(entries);
let pairs = BytePairRanks::build(&map);
let fronted = RankLookup::with_pairs(&map, &pairs);
let plain = RankLookup::new(&map);
for (key, rank) in entries {
assert_eq!(fronted.get(key), *rank);
assert_eq!(plain.get(key), *rank);
}
for miss in [&b"zz"[..], b"zzz", b"zzzz", b"zzzzz"] {
assert_eq!(fronted.get(miss), u32::MAX);
assert_eq!(plain.get(miss), u32::MAX);
}
}
}