use alloc::{boxed::Box, vec::Vec};
use core::{iter::FusedIterator, mem::MaybeUninit};
use crate::{
allocator::{Allocator, Global},
map::DEFAULT_PREFIX_LEN,
raw::{match_concrete_node_ptr, ConcreteNodePtr, InnerNode, LeafNode, OpaqueNodePtr},
AsBytes, TreeMap,
};
pub(crate) struct StackArena {
data: Vec<MaybeUninit<usize>>,
n: usize,
}
impl StackArena {
pub fn new(n: usize) -> Self {
Self {
data: Vec::new(),
n,
}
}
pub fn size(&self) -> usize {
self.n
}
pub fn push(&mut self) -> &mut [MaybeUninit<usize>] {
let old_len = self.data.len();
let new_len = old_len + self.n;
self.data.resize_with(new_len, MaybeUninit::uninit);
unsafe { self.data.get_unchecked_mut(old_len..new_len) }
}
pub fn pop_copy<'a, 'b>(
&mut self,
buffer: &'a mut &'b mut [MaybeUninit<usize>],
) -> Option<&'a mut &'b mut [usize]> {
unsafe {
core::hint::assert_unchecked(self.data.len() % self.n == 0);
}
if self.data.is_empty() {
return None;
}
let begin = self.data.len() - self.n;
let end = self.data.len();
let s = unsafe { &self.data.get_unchecked(begin..end) };
unsafe {
core::hint::assert_unchecked(buffer.len() == s.len());
}
buffer.copy_from_slice(s);
self.pop();
Some(unsafe {
core::mem::transmute::<&mut &mut [core::mem::MaybeUninit<usize>], &mut &mut [usize]>(
buffer,
)
})
}
pub fn pop(&mut self) {
unsafe { self.data.set_len(self.data.len() - self.n) }
}
}
#[inline]
pub(crate) fn edit_dist(
key: &[u8],
c: u8,
old: &[usize],
new: &mut [MaybeUninit<usize>],
max_edit_dist: usize,
) -> bool {
unsafe {
core::hint::assert_unchecked(old.len() == new.len());
core::hint::assert_unchecked(!old.is_empty());
core::hint::assert_unchecked(key.len() + 1 == old.len());
}
let first = *new[0].write(old[0] + 1);
let mut keep = first <= max_edit_dist;
for i in 1..new.len() {
unsafe {
let b = *key.get_unchecked(i - 1) == c;
let k1: usize = b.into();
let k2: usize = (!b).into();
let substitution = *old.get_unchecked(i - 1);
let insertion = *old.get_unchecked(i);
let deletion = new.get_unchecked(i - 1).assume_init();
let v1 = k1 * substitution;
let v2 = k2 * (substitution.min(insertion).min(deletion) + 1);
let v = *new.get_unchecked_mut(i).write(v1 + v2);
keep |= v <= max_edit_dist;
}
}
keep
}
#[inline]
unsafe fn swap(old_row: &mut &mut [usize], new_row: &mut &mut [MaybeUninit<usize>]) {
let temp = unsafe {
core::mem::transmute::<&mut &mut [usize], &mut &mut [core::mem::MaybeUninit<usize>]>(
old_row,
)
};
core::mem::swap(temp, new_row);
}
trait FuzzySearch<K: AsBytes, V, const PREFIX_LEN: usize> {
fn fuzzy_search(
&self,
arena: &mut StackArena,
key: &[u8],
old_row: &mut &mut [usize],
new_row: &mut &mut [MaybeUninit<usize>],
nodes_to_search: &mut Vec<OpaqueNodePtr<K, V, PREFIX_LEN>>,
max_edit_dist: usize,
) -> bool;
#[inline]
fn fuzzy_search_prefix(
&self,
key: &[u8],
old_row: &mut &mut [usize],
new_row: &mut &mut [MaybeUninit<usize>],
max_edit_dist: usize,
) -> bool
where
Self: InnerNode<PREFIX_LEN>,
Self::Key: AsBytes,
{
let (prefix, _) = unsafe { self.read_full_prefix(*old_row.first().unwrap_unchecked()) };
let mut keep = true;
for k in prefix {
keep &= edit_dist(key, *k, old_row, new_row, max_edit_dist);
unsafe { swap(old_row, new_row) };
}
keep
}
}
impl<K: AsBytes, V, const PREFIX_LEN: usize, N: InnerNode<PREFIX_LEN>> FuzzySearch<K, V, PREFIX_LEN>
for N
where
Self: InnerNode<PREFIX_LEN, Key = K, Value = V>,
{
fn fuzzy_search(
&self,
arena: &mut StackArena,
key: &[u8],
old_row: &mut &mut [usize],
new_row: &mut &mut [MaybeUninit<usize>],
nodes_to_search: &mut Vec<OpaqueNodePtr<K, V, PREFIX_LEN>>,
max_edit_dist: usize,
) -> bool {
if !self.fuzzy_search_prefix(key, old_row, new_row, max_edit_dist) {
return false;
}
for (k, node) in self.iter() {
let new_row = arena.push();
if edit_dist(key, k, old_row, new_row, max_edit_dist) {
nodes_to_search.push(node);
} else {
arena.pop();
}
}
false
}
}
impl<K: AsBytes, V, const PREFIX_LEN: usize> FuzzySearch<K, V, PREFIX_LEN>
for LeafNode<K, V, PREFIX_LEN>
{
fn fuzzy_search(
&self,
_arena: &mut StackArena,
key: &[u8],
old_row: &mut &mut [usize],
new_row: &mut &mut [MaybeUninit<usize>],
_nodes_to_search: &mut Vec<OpaqueNodePtr<K, V, PREFIX_LEN>>,
max_edit_dist: usize,
) -> bool {
let current_len = old_row[0];
let remaining_key = unsafe { self.key_ref().as_bytes().get_unchecked(current_len..) };
for k in remaining_key {
edit_dist(key, *k, old_row, new_row, max_edit_dist);
unsafe { swap(old_row, new_row) };
}
let edit_dist = unsafe { *old_row.last().unwrap_unchecked() };
edit_dist <= max_edit_dist
}
}
macro_rules! gen_iter {
($name:ident, $tree:ty, $ret:ty, $op:ident) => {
pub struct $name<
'a,
'b,
K,
V,
const PREFIX_LEN: usize = DEFAULT_PREFIX_LEN,
A: Allocator = Global,
> {
nodes_to_search: Vec<OpaqueNodePtr<K, V, PREFIX_LEN>>,
old_row: Box<[MaybeUninit<usize>]>,
new_row: Box<[MaybeUninit<usize>]>,
arena: StackArena,
max_edit_dist: usize,
key: &'b [u8],
size: usize,
_tree: $tree,
}
impl<'a, 'b, K: AsBytes, V, const PREFIX_LEN: usize, A: Allocator>
$name<'a, 'b, K, V, PREFIX_LEN, A>
{
pub(crate) fn new(tree: $tree, key: &'b [u8], max_edit_dist: usize) -> Self {
let mut arena = StackArena::new(key.len() + 1);
let n = arena.size();
let s = arena.push();
for i in 0..n {
s[i].write(i);
}
Self {
nodes_to_search: tree
.state
.as_ref()
.map(|state| state.root)
.into_iter()
.collect(),
old_row: Box::new_uninit_slice(arena.size()),
new_row: Box::new_uninit_slice(arena.size()),
arena,
max_edit_dist,
key,
size: tree.num_entries,
_tree: tree,
}
}
}
impl<'a, 'b, K: AsBytes, V, A: Allocator, const PREFIX_LEN: usize> Iterator
for $name<'a, 'b, K, V, PREFIX_LEN, A>
{
type Item = $ret;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
let mut old_row = self.old_row.as_mut();
let mut new_row = self.new_row.as_mut();
while let (Some(node), Some(old_row)) = (
self.nodes_to_search.pop(),
self.arena.pop_copy(&mut old_row),
) {
match_concrete_node_ptr!(match (node.to_node_ptr()) {
InnerNode(inner_ptr) => {
let inner_node = unsafe { inner_ptr.as_ref() };
inner_node.fuzzy_search(
&mut self.arena,
self.key,
old_row,
&mut new_row,
&mut self.nodes_to_search,
self.max_edit_dist,
);
},
LeafNode(inner_ptr) => {
self.size -= 1;
let inner_node = unsafe { inner_ptr.as_ref() };
if inner_node.fuzzy_search(
&mut self.arena,
self.key,
old_row,
&mut new_row,
&mut self.nodes_to_search,
self.max_edit_dist,
) {
return unsafe { Some(inner_ptr.$op()) };
}
},
});
}
None
}
fn size_hint(&self) -> (usize, Option<usize>) {
(0, Some(self.size))
}
}
impl<'a, 'b, K: AsBytes, V, A: Allocator, const PREFIX_LEN: usize> FusedIterator
for $name<'a, 'b, K, V, PREFIX_LEN, A>
{
}
};
}
gen_iter!(
Fuzzy,
&'a TreeMap<K, V, PREFIX_LEN, A>,
(&'a K, &'a V),
as_key_value_ref
);
unsafe impl<K, V, A, const PREFIX_LEN: usize> Send for Fuzzy<'_, '_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
unsafe impl<K, V, A, const PREFIX_LEN: usize> Sync for Fuzzy<'_, '_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
gen_iter!(
FuzzyMut,
&'a mut TreeMap<K, V, PREFIX_LEN, A>,
(&'a K, &'a mut V),
as_key_ref_value_mut
);
unsafe impl<K, V, A, const PREFIX_LEN: usize> Send for FuzzyMut<'_, '_, K, V, PREFIX_LEN, A>
where
K: Send,
V: Send,
A: Send + Allocator,
{
}
unsafe impl<K, V, A, const PREFIX_LEN: usize> Sync for FuzzyMut<'_, '_, K, V, PREFIX_LEN, A>
where
K: Sync,
V: Sync,
A: Sync + Allocator,
{
}
#[cfg(test)]
mod tests;