mod insert;
mod into_bytes;
mod rehash;
mod remove;
mod search;
mod split_node;
mod try_from_bytes;
use astro_format::IntoBytes;
use crate::hash::blake_3;
use std::collections::{HashMap, BTreeMap};
#[derive(Debug,Clone)]
pub struct RadixTree<K,V> {
nodes: HashMap<[u8; 32], RadixNode<K,V>>,
parents: HashMap<[u8;32], [u8;32]>,
root: [u8; 32],
}
#[derive(Debug,Clone)]
pub struct RadixNode<K,V> {
children: BTreeMap<K,[u8;32]>,
key: Vec<K>,
value: Option<V>,
}
impl<K,V> RadixNode<K,V>
where
K: Eq + Clone + Ord + IntoBytes,
V: IntoBytes,
{
pub fn hash(&self) -> [u8; 32] {
let children_hash = match self.children.iter().next() {
Some((_, &first_child_hash)) => {
self.children.iter().skip(1).fold(first_child_hash, |acc, (child_key, &child_hash)| {
let child_key_hash = blake_3(&child_key.into_bytes());
let combined = [acc, blake_3(&[child_key_hash, child_hash].concat())].concat();
blake_3(&combined)
})
},
None => blake_3(&vec![]),
};
let key_bytes = self.key.iter().flat_map(|k| k.clone().into_bytes()).collect::<Vec<u8>>();
let key_hash = blake_3(&key_bytes);
let children_key_concat = [children_hash, key_hash].concat();
let children_key_hash = blake_3(&children_key_concat);
let value_bytes = match &self.value {
Some(v) => v.into_bytes(),
None => Vec::new(),
};
let value_hash = blake_3(&value_bytes);
let children_key_value_concat = [children_key_hash, value_hash].concat();
blake_3(&children_key_value_concat)
}
fn new() -> Self {
RadixNode {
children: BTreeMap::new(),
key: vec![],
value: None,
}
}
}
impl<K, V> RadixTree<K, V>
where
K: Eq + std::hash::Hash + Clone + std::cmp::Ord + IntoBytes + std::fmt::Display + std::fmt::Debug,
V: Clone + IntoBytes + std::fmt::Debug,
{
pub fn new() -> Self {
RadixTree {
nodes: HashMap::new(),
parents: HashMap::new(),
root: [0;32],
}
}
pub fn hash(self) -> [u8; 32] {
self.root
}
}
#[cfg(test)]
mod tests {
use astro_format::TryFromBytes;
use super::*;
#[test]
fn test_new() {
let tree: RadixTree<String, i32> = RadixTree::new();
assert!(tree.nodes.is_empty());
assert!(tree.parents.is_empty());
}
#[test]
fn test_insert() {
let mut tree = RadixTree::new();
tree.insert("key1".as_bytes(), 1);
assert_eq!(tree.search("key1".as_bytes()), Some(&1));
}
#[test]
fn test_remove() {
let mut tree = RadixTree::new();
tree.insert("key1".chars(), 1);
assert!(tree.remove("key1".chars()).is_ok());
assert_eq!(tree.search("key1".chars()), None);
}
#[test]
fn test_search() {
let mut tree = RadixTree::new();
tree.insert("key1".as_bytes(), 1);
assert_eq!(tree.search("key1".as_bytes()), Some(&1));
assert_eq!(tree.search("nonexistent".as_bytes()), None);
}
#[test]
fn test_node_splitting_with_chars() {
let mut tree1 = RadixTree::new();
tree1.insert("abcde".chars(), 1);
tree1.insert("abcfg".chars(), 2);
assert_eq!(tree1.search("abcde".chars()), Some(&1));
assert_eq!(tree1.search("abcfg".chars()), Some(&2));
assert_eq!(tree1.search("abcf".chars()), None);
assert!(tree1.remove("abcde".chars()).is_ok());
assert_eq!(tree1.search("abcde".chars()), None);
assert_eq!(tree1.search("abcfg".chars()), Some(&2));
let mut tree2 = RadixTree::new();
tree2.insert("abcfg".chars(), 2);
assert_eq!(tree1.root, tree2.root);
}
#[test]
fn test_into_bytes_and_back() {
let mut tree_1 = RadixTree::new();
tree_1.insert("key1".chars(), 1_u8);
tree_1.insert("key2".chars(), 2);
println!("tree1 -> {:?}", tree_1.root);
for node in &tree_1.nodes {
println!(" * {:?}", node.0);
println!(" * {:?}", node.1);
}
assert_eq!(tree_1.search("key1".chars()), Some(&1));
assert_eq!(tree_1.search("key2".chars()), Some(&2));
let tree_bytes = tree_1.into_bytes();
let tree_2: RadixTree<char,u8> = RadixTree::try_from_bytes(&tree_bytes).expect("Deserialization failed");
assert_eq!(tree_2.search("key1".chars()), Some(&1));
assert_eq!(tree_2.search("key2".chars()), Some(&2));
}
}