use core::cmp::Ordering;
use core::marker::PhantomData;
use core::mem::replace;
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use crate::endian::Native;
use crate::pointer::{Coerce, Pointee};
use crate::slice::{BinarySearch, Slice};
use crate::{Buf, ByteOrder, Error, OwnedBuf, Ref, Size, ZeroCopy};
use super::{prefix, DefaultFlavor, Flavor, LinksRef, NodeRef, TrieRef};
#[cfg(feature = "alloc")]
pub fn store<S, E, O, I, T>(
buf: &mut OwnedBuf<E, O>,
it: I,
) -> Result<TrieRef<T, DefaultFlavor<E, O>>, Error>
where
I: IntoIterator<Item = (Ref<S, E, O>, T)>,
T: ZeroCopy,
S: ?Sized + Pointee + Coerce<[u8]>,
E: ByteOrder,
O: Size,
{
let mut trie = Builder::with_flavor();
for (string, value) in it {
trie.insert(buf, string, value)?;
}
trie.build(buf)
}
#[cfg(feature = "alloc")]
pub struct Builder<T, F = DefaultFlavor>
where
F: Flavor,
{
links: Links<T>,
_marker: PhantomData<F>,
}
#[cfg(feature = "alloc")]
impl<T> Builder<T> {
#[inline]
pub const fn new() -> Self {
Self::with_flavor()
}
}
#[cfg(feature = "alloc")]
impl<T> Default for Builder<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "alloc")]
impl<T, F> Builder<T, F>
where
F: Flavor,
{
pub const fn with_flavor() -> Self {
Self {
links: Links::empty(),
_marker: PhantomData,
}
}
pub fn insert<S, E, O>(
&mut self,
buf: &Buf,
string: Ref<S, E, O>,
value: T,
) -> Result<(), Error>
where
S: ?Sized + Pointee + Coerce<[u8]>,
E: ByteOrder,
O: Size,
{
let mut string = string.coerce::<[u8]>();
let mut current = buf.load(string)?;
let mut this = &mut self.links;
loop {
let search =
try_binary_search_by(&this.children, |c| Ok(buf.load(c.string)?.cmp(current)))?;
match search {
BinarySearch::Found(n) => {
this.children[n].links.values.push(value);
return Ok(());
}
BinarySearch::Missing(0) => {
this.children.insert(
0,
Node {
string: Ref::try_with_metadata(string.offset(), string.len())?,
links: Links::new(value),
},
);
return Ok(());
}
BinarySearch::Missing(n) => {
let pre = n - 1;
let prefix = prefix(buf.load(this.children[pre].string)?, current);
if prefix == 0 {
this.children.insert(
n,
Node {
string: Ref::try_with_metadata(string.offset(), string.len())?,
links: Links::new(value),
},
);
return Ok(());
}
let child = &mut this.children[pre];
if prefix != child.string.len() {
let (prefix, suffix) = child.string.split_at(prefix);
let new_node = Node::new(prefix);
let mut replaced = replace(child, new_node);
replaced.string = suffix;
child.links.children.push(replaced);
}
current = ¤t[prefix..];
string = string.split_at(prefix).1;
this = &mut child.links;
}
}
}
}
pub fn build<E, O>(self, buf: &mut OwnedBuf<E, O>) -> Result<TrieRef<T, F>, Error>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
{
Ok(TrieRef {
links: self.links.into_ref(buf)?,
})
}
}
#[cfg(feature = "alloc")]
struct Links<T> {
values: Vec<T>,
children: Vec<Node<T>>,
}
#[cfg(feature = "alloc")]
impl<T> Links<T> {
const fn empty() -> Self {
Self {
values: Vec::new(),
children: Vec::new(),
}
}
fn new(value: T) -> Self {
Self {
values: alloc::vec![value],
children: Vec::new(),
}
}
fn into_ref<E, O, F>(self, buf: &mut OwnedBuf<E, O>) -> Result<LinksRef<T, F>, Error>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
F: Flavor,
{
let values = F::Values::try_from_ref(buf.store_slice(&self.values))?;
let mut children = Vec::with_capacity(self.children.len());
for node in self.children {
children.push(node.into_ref(buf)?);
}
let children = F::Children::try_from_ref(buf.store_slice(&children))?;
Ok(LinksRef { values, children })
}
}
#[cfg(feature = "alloc")]
struct Node<T> {
string: Ref<[u8], Native, usize>,
links: Links<T>,
}
#[cfg(feature = "alloc")]
impl<T> Node<T> {
const fn new(string: Ref<[u8], Native, usize>) -> Self {
Self {
string,
links: Links::empty(),
}
}
fn into_ref<E, O, F>(self, buf: &mut OwnedBuf<E, O>) -> Result<NodeRef<T, F>, Error>
where
T: ZeroCopy,
E: ByteOrder,
O: Size,
F: Flavor,
{
Ok(NodeRef {
string: F::String::try_from_ref(self.string)?,
links: self.links.into_ref(buf)?,
})
}
}
fn try_binary_search_by<T, F, E>(slice: &[T], mut f: F) -> Result<BinarySearch, E>
where
F: FnMut(&T) -> Result<Ordering, E>,
{
let mut size = slice.len();
let mut left = 0;
let mut right = size;
while left < right {
let mid = left + size / 2;
let value = unsafe { slice.get_unchecked(mid) };
let cmp = f(value)?;
if cmp == Ordering::Less {
left = mid + 1;
} else if cmp == Ordering::Greater {
right = mid;
} else {
return Ok(BinarySearch::Found(mid));
}
size = right - left;
}
Ok(BinarySearch::Missing(left))
}