diff --git a/Cargo.lock b/Cargo.lock index 18db7be..89a0509 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -207,7 +207,7 @@ dependencies = [ ] [[package]] -name = "kytes" +name = "kytz" version = "0.1.0" dependencies = [ "argon2", @@ -251,6 +251,7 @@ dependencies = [ "proptest", "redb", "tempfile", + "thiserror", ] [[package]] @@ -444,18 +445,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "b2cd5904763bad08ad5513ddbb12cf2ae273ca53fa9f68e843e236ec6dfccc09" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.53" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "3dcf4a824cce0aeacd6f38ae6f24234c8e80d68632338ebaa1443b5df9e29e19" dependencies = [ "proc-macro2", "quote", diff --git a/mast/Cargo.toml b/mast/Cargo.toml index a8cfed1..ac282be 100644 --- a/mast/Cargo.toml +++ b/mast/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] blake3 = "1.5.0" redb = "1.4.0" +thiserror = "1.0.53" [dev-dependencies] proptest = "1.4.0" diff --git a/mast/src/db.rs b/mast/src/db.rs new file mode 100644 index 0000000..2d882dc --- /dev/null +++ b/mast/src/db.rs @@ -0,0 +1,123 @@ +//! Kytz Database + +use crate::node::Node; +use crate::operations::read::{get_node, root_hash, root_node}; +use crate::operations::{insert, remove}; +use crate::{Hash, Result}; + +pub struct Database { + pub(crate) inner: redb::Database, +} + +impl Database { + /// Create a new in-memory database. + pub fn in_memory() -> Self { + let backend = redb::backends::InMemoryBackend::new(); + let inner = redb::Database::builder() + .create_with_backend(backend) + .unwrap(); + + Self { inner } + } + + pub fn begin_write(&self) -> Result { + let txn = self.inner.begin_write().unwrap(); + WriteTransaction::new(txn) + } + + pub fn iter(&self, treap: &str) -> TreapIterator<'_> { + // TODO: save tables instead of opening a new one on every next() call. + TreapIterator::new(self, treap.to_string()) + } + + // === Private Methods === + + pub(crate) fn get_node(&self, hash: &Option) -> Option { + get_node(&self, hash) + } + + pub(crate) fn root_hash(&self, treap: &str) -> Option { + root_hash(&self, treap) + } + + pub(crate) fn root(&self, treap: &str) -> Option { + root_node(self, treap) + } +} + +pub struct TreapIterator<'db> { + db: &'db Database, + treap: String, + stack: Vec, +} + +impl<'db> TreapIterator<'db> { + fn new(db: &'db Database, treap: String) -> Self { + let mut iter = TreapIterator { + db, + treap: treap.clone(), + stack: Vec::new(), + }; + + if let Some(root) = db.root(&treap) { + iter.push_left(root) + }; + + iter + } + + fn push_left(&mut self, mut node: Node) { + while let Some(left) = self.db.get_node(node.left()) { + self.stack.push(node); + node = left; + } + self.stack.push(node); + } +} + +impl<'a> Iterator for TreapIterator<'a> { + type Item = Node; + + fn next(&mut self) -> Option { + match self.stack.pop() { + Some(node) => { + if let Some(right) = self.db.get_node(node.right()) { + self.push_left(right) + } + + Some(node.clone()) + } + _ => None, + } + } +} + +pub struct WriteTransaction<'db> { + inner: redb::WriteTransaction<'db>, +} + +impl<'db> WriteTransaction<'db> { + pub(crate) fn new(inner: redb::WriteTransaction<'db>) -> Result { + Ok(Self { inner }) + } + + pub fn insert( + &mut self, + treap: &str, + key: impl AsRef<[u8]>, + value: impl AsRef<[u8]>, + ) -> Option { + // TODO: validate key and value length. + // key and value mast be less than 2^32 bytes. + + insert(&mut self.inner, treap, key.as_ref(), value.as_ref()) + } + + pub fn remove(&mut self, treap: &str, key: impl AsRef<[u8]>) -> Option { + remove(&mut self.inner, treap, key.as_ref()) + } + + pub fn commit(self) -> Result<()> { + self.inner.commit().map_err(|e| e.into()) + } +} diff --git a/mast/src/error.rs b/mast/src/error.rs new file mode 100644 index 0000000..adc2769 --- /dev/null +++ b/mast/src/error.rs @@ -0,0 +1,24 @@ +//! Main Crate Error + +#[derive(thiserror::Error, Debug)] +/// Mainline crate error enum. +pub enum Error { + /// For starter, to remove as code matures. + #[error("Generic error: {0}")] + Generic(String), + /// For starter, to remove as code matures. + #[error("Static error: {0}")] + Static(&'static str), + + #[error(transparent)] + /// Transparent [std::io::Error] + IO(#[from] std::io::Error), + + #[error(transparent)] + /// Transparent [redb::CommitError] + CommitError(#[from] redb::CommitError), + + #[error(transparent)] + /// Error from `redb::TransactionError`. + TransactionError(#[from] redb::TransactionError), +} diff --git a/mast/src/lib.rs b/mast/src/lib.rs index 995d8eb..35c9984 100644 --- a/mast/src/lib.rs +++ b/mast/src/lib.rs @@ -1,12 +1,18 @@ #![allow(unused)] +pub mod db; +pub mod error; mod node; mod operations; -pub mod treap; #[cfg(test)] mod test; pub(crate) use blake3::{Hash, Hasher}; +pub(crate) const HASH_LEN: usize = 32; -pub const HASH_LEN: usize = 32; +pub use db::Database; +pub use error::Error; + +// Alias Result to be the crate Result. +pub type Result = core::result::Result; diff --git a/mast/src/operations/insert.rs b/mast/src/operations/insert.rs index c1ad523..4593a67 100644 --- a/mast/src/operations/insert.rs +++ b/mast/src/operations/insert.rs @@ -1,7 +1,7 @@ use blake3::Hash; use redb::Table; -use super::search::binary_search_path; +use super::{read::root_node_inner, search::binary_search_path, NODES_TABLE, ROOTS_TABLE}; use crate::node::{Branch, Node}; // Watch this [video](https://youtu.be/NxRXhBur6Xs?si=GNwaUOfuGwr_tBKI&t=1763) for a good explanation of the unzipping algorithm. @@ -101,12 +101,17 @@ use crate::node::{Branch, Node}; // pub(crate) fn insert( - nodes_table: &'_ mut Table<&'static [u8], (u64, &'static [u8])>, - root: Option, + write_txn: &mut redb::WriteTransaction, + treap: &str, key: &[u8], value: &[u8], -) -> Node { - let mut path = binary_search_path(nodes_table, root, key); +) -> Option { + let mut roots_table = write_txn.open_table(ROOTS_TABLE).unwrap(); + let mut nodes_table = write_txn.open_table(NODES_TABLE).unwrap(); + + let old_root = root_node_inner(&roots_table, &nodes_table, treap); + + let mut path = binary_search_path(&nodes_table, old_root, key); let mut left_subtree: Option = None; let mut right_subtree: Option = None; @@ -114,7 +119,7 @@ pub(crate) fn insert( // Unzip the lower path to get left and right children of the inserted node. for (node, branch) in path.lower.iter_mut().rev() { // Decrement the old version. - node.decrement_ref_count().save(nodes_table); + node.decrement_ref_count().save(&mut nodes_table); match branch { Branch::Right => { @@ -127,28 +132,27 @@ pub(crate) fn insert( } } - node.increment_ref_count().save(nodes_table); + node.increment_ref_count().save(&mut nodes_table); } - let mut root; + let mut new_root; if let Some(mut found) = path.found { if found.value() == value { // There is really nothing to update. Skip traversing upwards. - - return path.upper.first().map(|(n, _)| n.clone()).unwrap_or(found); + return Some(found); } // Decrement the old version. - found.decrement_ref_count().save(nodes_table); + found.decrement_ref_count().save(&mut nodes_table); // Else, update the value and rehashe the node so that we can update the hashes upwards. found .set_value(value) .increment_ref_count() - .save(nodes_table); + .save(&mut nodes_table); - root = found + new_root = found } else { // Insert the new node. let mut node = Node::new(key, value); @@ -156,29 +160,34 @@ pub(crate) fn insert( node.set_left_child(left_subtree) .set_right_child(right_subtree) .increment_ref_count() - .save(nodes_table); + .save(&mut nodes_table); - root = node + new_root = node }; let mut upper_path = path.upper; // Propagate the new hashes upwards if there are any nodes in the upper_path. while let Some((mut node, branch)) = upper_path.pop() { - node.decrement_ref_count().save(nodes_table); + node.decrement_ref_count().save(&mut nodes_table); match branch { - Branch::Left => node.set_left_child(Some(root.hash())), - Branch::Right => node.set_right_child(Some(root.hash())), + Branch::Left => node.set_left_child(Some(new_root.hash())), + Branch::Right => node.set_right_child(Some(new_root.hash())), }; - node.increment_ref_count().save(nodes_table); + node.increment_ref_count().save(&mut nodes_table); - root = node; + new_root = node; } - // Finally return the new root to be set to the root. - root + // Finally set the new root . + roots_table + .insert(treap.as_bytes(), new_root.hash().as_bytes().as_slice()) + .unwrap(); + + // No older value was found. + None } #[cfg(test)] diff --git a/mast/src/operations/mod.rs b/mast/src/operations/mod.rs index 497e2ef..d45d9e3 100644 --- a/mast/src/operations/mod.rs +++ b/mast/src/operations/mod.rs @@ -1,6 +1,25 @@ pub mod insert; +pub mod read; pub mod remove; mod search; pub(crate) use insert::insert; pub(crate) use remove::remove; + +use redb::{ReadableTable, TableDefinition}; + +// Table: Nodes v0 +// stores all the hash treap nodes from all the treaps in the storage. +// +// Key: `[u8; 32]` # Node hash +// Value: `(u64, [u8])` # (RefCount, EncodedNode) +pub const NODES_TABLE: TableDefinition<&[u8], (u64, &[u8])> = + TableDefinition::new("kytz:hash_treap:nodes:v0"); + +// Table: Roots v0 +// stores all the current roots for all treaps in the storage. +// +// Key: `[u8; 32]` # Treap name +// Value: `[u8; 32]` # Hash +pub const ROOTS_TABLE: TableDefinition<&[u8], &[u8]> = + TableDefinition::new("kytz:hash_treap:roots:v0"); diff --git a/mast/src/operations/read.rs b/mast/src/operations/read.rs new file mode 100644 index 0000000..24d9434 --- /dev/null +++ b/mast/src/operations/read.rs @@ -0,0 +1,47 @@ +use super::{ReadableTable, NODES_TABLE, ROOTS_TABLE}; +use crate::node::Node; +use crate::{Database, Hash, HASH_LEN}; + +pub fn root_hash_inner( + table: &'_ impl ReadableTable<&'static [u8], &'static [u8]>, + treap: &str, +) -> Option { + let existing = table.get(treap.as_bytes()).unwrap(); + existing.as_ref()?; + + let hash = existing.unwrap(); + + let hash: [u8; HASH_LEN] = hash.value().try_into().expect("Invalid root hash"); + + Some(Hash::from_bytes(hash)) +} + +pub fn root_node_inner( + roots_table: &'_ impl ReadableTable<&'static [u8], &'static [u8]>, + nodes_table: &'_ impl ReadableTable<&'static [u8], (u64, &'static [u8])>, + treap: &str, +) -> Option { + root_hash_inner(roots_table, treap).and_then(|hash| Node::open(nodes_table, hash)) +} + +pub fn get_node(db: &Database, hash: &Option) -> Option { + let read_txn = db.inner.begin_read().unwrap(); + let table = read_txn.open_table(NODES_TABLE).unwrap(); + + hash.and_then(|h| Node::open(&table, h)) +} + +pub fn root_hash(db: &Database, treap: &str) -> Option { + let read_txn = db.inner.begin_read().unwrap(); + let table = read_txn.open_table(ROOTS_TABLE).unwrap(); + + root_hash_inner(&table, treap) +} + +pub fn root_node(db: &Database, treap: &str) -> Option { + let read_txn = db.inner.begin_read().unwrap(); + let roots_table = read_txn.open_table(ROOTS_TABLE).unwrap(); + let nodes_table = read_txn.open_table(NODES_TABLE).unwrap(); + + root_node_inner(&roots_table, &nodes_table, treap) +} diff --git a/mast/src/operations/remove.rs b/mast/src/operations/remove.rs index c8b07e3..1ca4fdc 100644 --- a/mast/src/operations/remove.rs +++ b/mast/src/operations/remove.rs @@ -1,41 +1,54 @@ use blake3::Hash; use redb::Table; -use super::search::binary_search_path; +use super::{read::root_node_inner, search::binary_search_path, NODES_TABLE, ROOTS_TABLE}; use crate::node::{Branch, Node}; /// Removes the target node if it exists, and returns the new root and the removed node. pub(crate) fn remove( - nodes_table: &'_ mut Table<&'static [u8], (u64, &'static [u8])>, - root: Option, + write_txn: &mut redb::WriteTransaction, + treap: &str, key: &[u8], -) -> (Option, Option) { - let mut path = binary_search_path(nodes_table, root, key); +) -> Option { + let mut roots_table = write_txn.open_table(ROOTS_TABLE).unwrap(); + let mut nodes_table = write_txn.open_table(NODES_TABLE).unwrap(); + + let old_root = root_node_inner(&roots_table, &nodes_table, treap); + + let mut path = binary_search_path(&nodes_table, old_root, key); - let mut root = None; + let mut new_root = None; if let Some(mut target) = path.found.clone() { - root = zip(nodes_table, &mut target) + new_root = zip(&mut nodes_table, &mut target) } else { // clearly the lower path has the highest node, and it won't be changed. - root = path.lower.first().map(|(n, _)| n.clone()); + new_root = path.lower.first().map(|(n, _)| n.clone()); } // If there is an upper path, we propagate the hash updates upwards. while let Some((mut node, branch)) = path.upper.pop() { - node.decrement_ref_count().save(nodes_table); + node.decrement_ref_count().save(&mut nodes_table); match branch { - Branch::Left => node.set_left_child(root.map(|mut n| n.hash())), - Branch::Right => node.set_right_child(root.map(|mut n| n.hash())), + Branch::Left => node.set_left_child(new_root.map(|mut n| n.hash())), + Branch::Right => node.set_right_child(new_root.map(|mut n| n.hash())), }; - node.increment_ref_count().save(nodes_table); + node.increment_ref_count().save(&mut nodes_table); - root = Some(node); + new_root = Some(node); + } + + if let Some(mut new_root) = new_root { + roots_table + .insert(treap.as_bytes(), new_root.hash().as_bytes().as_slice()) + .unwrap(); + } else { + roots_table.remove(treap.as_bytes()).unwrap(); } - (root, path.found) + path.found } fn zip( diff --git a/mast/src/test.rs b/mast/src/test.rs index 5e4c08e..f366f69 100644 --- a/mast/src/test.rs +++ b/mast/src/test.rs @@ -4,12 +4,9 @@ use std::assert_eq; use std::collections::BTreeMap; use crate::node::Node; -use crate::treap::HashTreap; +use crate::Database; use crate::Hash; -use redb::backends::InMemoryBackend; -use redb::Database; - #[derive(Clone, Debug)] pub enum Operation { Insert, @@ -56,27 +53,24 @@ impl std::fmt::Debug for Entry { } pub fn test_operations(input: &[(Entry, Operation)], root_hash: Option<&str>) { - let inmemory = InMemoryBackend::new(); - let db = Database::builder() - .create_with_backend(inmemory) - .expect("Failed to create DB"); - - let mut treap = HashTreap::new(&db, "test"); + let db = Database::in_memory(); + let mut txn = db.begin_write().unwrap(); + let treap = "test"; for (entry, operation) in input { match operation { - Operation::Insert => treap.insert(&entry.key, &entry.value), - Operation::Remove => { - treap.remove(&entry.key); - } - } + Operation::Insert => txn.insert(treap, &entry.key, &entry.value), + Operation::Remove => txn.remove(treap, &entry.key), + }; } + txn.commit(); + // Uncomment to see the graph // println!("{}", into_mermaid_graph(&treap)); - let collected = treap - .iter() + let collected = db + .iter(treap) .map(|n| { assert_eq!( *n.ref_count(), @@ -92,7 +86,7 @@ pub fn test_operations(input: &[(Entry, Operation)], root_hash: Option<&str>) { }) .collect::>(); - verify_ranks(&treap); + verify_ranks(&db, treap); let mut btree = BTreeMap::new(); for (entry, operation) in input { @@ -117,28 +111,28 @@ pub fn test_operations(input: &[(Entry, Operation)], root_hash: Option<&str>) { assert_eq!(collected, expected, "{}", format!("Entries do not match")); if root_hash.is_some() { - assert_root(&treap, root_hash.unwrap()); + assert_root(&db, treap, root_hash.unwrap()); } } /// Verify that every node has higher rank than its children. -fn verify_ranks(treap: &HashTreap) { +fn verify_ranks(db: &Database, treap: &str) { assert!( - verify_children_rank(treap, treap.root()), + verify_children_rank(db, treap, db.root(treap)), "Ranks are not sorted correctly" ) } -fn verify_children_rank(treap: &HashTreap, node: Option) -> bool { +fn verify_children_rank(db: &Database, treap: &str, node: Option) -> bool { match node { Some(n) => { - let left_check = treap.get_node(n.left()).map_or(true, |left| { + let left_check = db.get_node(n.left()).map_or(true, |left| { n.rank().as_bytes() > left.rank().as_bytes() - && verify_children_rank(treap, Some(left)) + && verify_children_rank(db, treap, Some(left)) }); - let right_check = treap.get_node(n.right()).map_or(true, |right| { + let right_check = db.get_node(n.right()).map_or(true, |right| { n.rank().as_bytes() > right.rank().as_bytes() - && verify_children_rank(treap, Some(right)) + && verify_children_rank(db, treap, Some(right)) }); left_check && right_check @@ -147,11 +141,8 @@ fn verify_children_rank(treap: &HashTreap, node: Option) -> bool { } } -fn assert_root(treap: &HashTreap, expected_root_hash: &str) { - let root_hash = treap - .root() - .map(|mut n| n.hash()) - .expect("Has root hash after insertion"); +fn assert_root(db: &Database, treap: &str, expected_root_hash: &str) { + let root_hash = db.root_hash(treap).expect("Has root hash after insertion"); assert_eq!( root_hash, @@ -162,13 +153,13 @@ fn assert_root(treap: &HashTreap, expected_root_hash: &str) { // === Visualize the treap to verify the structure === -fn into_mermaid_graph(treap: &HashTreap) -> String { +fn into_mermaid_graph(db: &Database, treap: &str) -> String { let mut graph = String::new(); graph.push_str("graph TD;\n"); - if let Some(mut root) = treap.root() { - build_graph_string(&treap, &mut root, &mut graph); + if let Some(mut root) = db.root(treap) { + build_graph_string(db, treap, &mut root, &mut graph); } graph.push_str(&format!( @@ -178,28 +169,28 @@ fn into_mermaid_graph(treap: &HashTreap) -> String { graph } -fn build_graph_string(treap: &HashTreap, node: &mut Node, graph: &mut String) { +fn build_graph_string(db: &Database, treap: &str, node: &mut Node, graph: &mut String) { let key = format_key(node.key()); let node_label = format!("{}(({}))", node.hash(), key); // graph.push_str(&format!("## START node {}\n", node_label)); - if let Some(mut child) = treap.get_node(node.left()) { + if let Some(mut child) = db.get_node(node.left()) { let key = format_key(child.key()); let child_label = format!("{}(({}))", child.hash(), key); graph.push_str(&format!(" {} --l--> {};\n", node_label, child_label)); - build_graph_string(&treap, &mut child, graph); + build_graph_string(db, treap, &mut child, graph); } else { graph.push_str(&format!(" {} -.-> {}l((l));\n", node_label, node.hash())); graph.push_str(&format!(" class {}l null;\n", node.hash())); } - if let Some(mut child) = treap.get_node(node.right()) { + if let Some(mut child) = db.get_node(node.right()) { let key = format_key(child.key()); let child_label = format!("{}(({}))", child.hash(), key); graph.push_str(&format!(" {} --r--> {};\n", node_label, child_label)); - build_graph_string(&treap, &mut child, graph); + build_graph_string(db, treap, &mut child, graph); } else { graph.push_str(&format!(" {} -.-> {}r((r));\n", node_label, node.hash())); graph.push_str(&format!(" class {}r null;\n", node.hash())); diff --git a/mast/src/treap.rs b/mast/src/treap.rs deleted file mode 100644 index 6d8acdf..0000000 --- a/mast/src/treap.rs +++ /dev/null @@ -1,196 +0,0 @@ -use blake3::Hash; -use redb::*; - -use crate::{node::Node, HASH_LEN}; - -// TODO: test that order is correct -// TODO: test that there are no extr anodes. - -// Table: Nodes v0 -// stores all the hash treap nodes from all the treaps in the storage. -// -// Key: `[u8; 32]` # Node hash -// Value: `(u64, [u8])` # (RefCount, EncodedNode) -pub const NODES_TABLE: TableDefinition<&[u8], (u64, &[u8])> = - TableDefinition::new("kytz:hash_treap:nodes:v0"); - -// Table: Roots v0 -// stores all the current roots for all treaps in the storage. -// -// Key: `[u8; 32]` # Treap name -// Value: `[u8; 32]` # Hash -pub const ROOTS_TABLE: TableDefinition<&[u8], &[u8]> = - TableDefinition::new("kytz:hash_treap:roots:v0"); - -#[derive(Debug)] -pub struct HashTreap<'treap> { - /// Redb database to store the nodes. - pub(crate) db: &'treap Database, - pub(crate) name: &'treap str, -} - -impl<'treap> HashTreap<'treap> { - // TODO: add name to open from storage with. - pub fn new(db: &'treap Database, name: &'treap str) -> Self { - // Setup tables - let write_tx = db.begin_write().unwrap(); - { - let _table = write_tx.open_table(NODES_TABLE).unwrap(); - let _table = write_tx.open_table(ROOTS_TABLE).unwrap(); - } - write_tx.commit().unwrap(); - - Self { name, db } - } - - // === Getters === - - /// Returns the root hash of the treap. - pub fn root_hash(&self) -> Option { - let read_txn = self.db.begin_read().unwrap(); - let table = read_txn.open_table(ROOTS_TABLE).unwrap(); - - self.root_hash_inner(&table) - } - - // === Public Methods === - - pub fn insert(&mut self, key: &[u8], value: &[u8]) { - // TODO: validate key and value length. - // key and value mast be less than 2^32 bytes. - - let write_txn = self.db.begin_write().unwrap(); - - { - let mut roots_table = write_txn.open_table(ROOTS_TABLE).unwrap(); - let mut nodes_table = write_txn.open_table(NODES_TABLE).unwrap(); - - let old_root = self - .root_hash_inner(&roots_table) - .and_then(|hash| Node::open(&nodes_table, hash)); - - let mut new_root = crate::operations::insert(&mut nodes_table, old_root, key, value); - - roots_table - .insert(self.name.as_bytes(), new_root.hash().as_bytes().as_slice()) - .unwrap(); - }; - - // Finally commit the changes to the storage. - write_txn.commit().unwrap(); - } - - pub fn remove(&mut self, key: &[u8]) -> Option> { - let write_txn = self.db.begin_write().unwrap(); - - let mut removed_node; - - { - let mut roots_table = write_txn.open_table(ROOTS_TABLE).unwrap(); - let mut nodes_table = write_txn.open_table(NODES_TABLE).unwrap(); - - let old_root = self - .root_hash_inner(&roots_table) - .and_then(|hash| Node::open(&nodes_table, hash)); - - let (new_root, old_node) = crate::operations::remove(&mut nodes_table, old_root, key); - - removed_node = old_node; - - if let Some(mut new_root) = new_root { - roots_table - .insert(self.name.as_bytes(), new_root.hash().as_bytes().as_slice()) - .unwrap(); - } else { - roots_table.remove(self.name.as_bytes()).unwrap(); - } - }; - - // Finally commit the changes to the storage. - write_txn.commit().unwrap(); - - removed_node.map(|node| node.value().to_vec().into_boxed_slice()) - } - - pub fn iter(&self) -> TreapIterator<'_> { - TreapIterator::new(self) - } - - // === Private Methods === - - pub(crate) fn root(&self) -> Option { - let read_txn = self.db.begin_read().unwrap(); - - let roots_table = read_txn.open_table(ROOTS_TABLE).unwrap(); - let nodes_table = read_txn.open_table(NODES_TABLE).unwrap(); - - self.root_hash_inner(&roots_table) - .and_then(|hash| Node::open(&nodes_table, hash)) - } - - fn root_hash_inner( - &self, - table: &'_ impl ReadableTable<&'static [u8], &'static [u8]>, - ) -> Option { - let existing = table.get(self.name.as_bytes()).unwrap(); - existing.as_ref()?; - - let hash = existing.unwrap(); - - let hash: [u8; HASH_LEN] = hash.value().try_into().expect("Invalid root hash"); - - Some(Hash::from_bytes(hash)) - } - - pub(crate) fn get_node(&self, hash: &Option) -> Option { - let read_txn = self.db.begin_read().unwrap(); - let table = read_txn.open_table(NODES_TABLE).unwrap(); - - hash.and_then(|h| Node::open(&table, h)) - } -} - -pub struct TreapIterator<'treap> { - treap: &'treap HashTreap<'treap>, - stack: Vec, -} - -impl<'a> TreapIterator<'a> { - fn new(treap: &'a HashTreap<'a>) -> Self { - let mut iter = TreapIterator { - treap, - stack: Vec::new(), - }; - - if let Some(root) = treap.root() { - iter.push_left(root) - }; - - iter - } - - fn push_left(&mut self, mut node: Node) { - while let Some(left) = self.treap.get_node(node.left()) { - self.stack.push(node); - node = left; - } - self.stack.push(node); - } -} - -impl<'a> Iterator for TreapIterator<'a> { - type Item = Node; - - fn next(&mut self) -> Option { - match self.stack.pop() { - Some(node) => { - if let Some(right) = self.treap.get_node(node.right()) { - self.push_left(right) - } - - Some(node.clone()) - } - _ => None, - } - } -}