diff --git a/grovedb/src/merk_cache.rs b/grovedb/src/merk_cache.rs index ab0afc21..0e59ec88 100644 --- a/grovedb/src/merk_cache.rs +++ b/grovedb/src/merk_cache.rs @@ -8,7 +8,7 @@ use std::{ use grovedb_costs::{cost_return_on_error, cost_return_on_error_no_add, CostResult, CostsExt}; use grovedb_merk::Merk; -use grovedb_path::SubtreePath; +use grovedb_path::{SubtreePath, SubtreePathBuilder}; use grovedb_storage::{ rocksdb_storage::{PrefixedRocksDbTransactionContext, RocksDbStorage}, StorageBatch, @@ -25,7 +25,7 @@ pub(crate) struct MerkCache<'db, 'b, B: AsRef<[u8]>> { version: &'db GroveVersion, batch: Box, tx: &'db Transaction<'db>, - merks: UnsafeCell, Box<(Cell, TxMerk<'db>)>>>, + merks: UnsafeCell, Box<(Cell, TxMerk<'db>)>>>, } impl<'db, 'b, B: AsRef<[u8]>> MerkCache<'db, 'b, B> { @@ -51,7 +51,7 @@ impl<'db, 'b, B: AsRef<[u8]>> MerkCache<'db, 'b, B> { /// shall reach end of the scope or to be `drop`ped manually. pub(crate) fn get_merk<'c>( &'c self, - path: SubtreePath<'b, B>, + path: SubtreePathBuilder<'b, B>, ) -> CostResult, Error> { let mut cost = Default::default(); @@ -62,13 +62,13 @@ impl<'db, 'b, B: AsRef<[u8]>> MerkCache<'db, 'b, B> { .as_mut() .expect("`UnsafeCell` is never null") } - .entry(path.clone()) + .entry(path) { Entry::Vacant(e) => { let merk = cost_return_on_error!( &mut cost, self.db.open_transactional_merk_at_path( - e.key().clone(), + e.key().into(), self.tx, // SAFETY: batch is allocated on the heap and we use only shared // references, so as long as the `Box` allocation @@ -149,7 +149,7 @@ impl<'db, 'b, B: AsRef<[u8]>> MerkCache<'db, 'b, B> { // path's first. while let Some((path, flag_and_merk)) = self.merks.get_mut().pop_first() { let merk = flag_and_merk.1; - if let Some((parent_path, parent_key)) = path.derive_parent() { + if let Some((parent_path, parent_key)) = path.derive_parent_owned() { let mut parent_merk = cost_return_on_error!(&mut cost, self.get_merk(parent_path)); let (root_hash, root_key, sum) = cost_return_on_error!( @@ -221,8 +221,14 @@ mod tests { let cache = MerkCache::new(&db, &tx, version); - cache.get_merk(SubtreePath::empty()).unwrap().unwrap(); - cache.get_merk(SubtreePath::empty()).unwrap().unwrap(); + cache + .get_merk(SubtreePath::empty().derive_owned()) + .unwrap() + .unwrap(); + cache + .get_merk(SubtreePath::empty().derive_owned()) + .unwrap() + .unwrap(); } #[test] @@ -251,7 +257,7 @@ mod tests { let cache = MerkCache::new(&db, &tx, version); - let mut merk = cache.get_merk(path).unwrap().unwrap(); + let mut merk = cache.get_merk(path.derive_owned()).unwrap().unwrap(); item.insert(&mut merk, b"k1", None, &version) .unwrap() diff --git a/grovedb/src/operations/insert/mod.rs b/grovedb/src/operations/insert/mod.rs index b6e00be0..929a9a81 100644 --- a/grovedb/src/operations/insert/mod.rs +++ b/grovedb/src/operations/insert/mod.rs @@ -13,8 +13,8 @@ use grovedb_version::{ }; use crate::{ - reference_path::path_from_reference_path_type, util::TxRef, Element, Error, GroveDb, - Transaction, TransactionArg, + merk_cache::MerkCache, reference_path::path_from_reference_path_type, util::TxRef, Element, + Error, GroveDb, Transaction, TransactionArg, }; #[derive(Clone)] /// Insert options @@ -70,79 +70,29 @@ impl GroveDb { grove_version.grovedb_versions.operations.insert.insert ); - let subtree_path: SubtreePath = path.into(); - let batch = StorageBatch::new(); - let tx = TxRef::new(&self.db, transaction); - - let collect_costs = self.insert_on_transaction( - subtree_path, - key, - element, - options.unwrap_or_default(), - tx.as_ref(), - &batch, - grove_version, - ); - - collect_costs.flat_map_ok(|_| { - self.db - .commit_multi_context_batch(batch, Some(tx.as_ref())) - .map_err(Into::into) - .map_ok(|_| tx.commit_local()) - .flatten() - }) - } - - fn insert_on_transaction<'db, 'b, B: AsRef<[u8]>>( - &self, - path: SubtreePath<'b, B>, - key: &[u8], - element: Element, - options: InsertOptions, - transaction: &'db Transaction, - batch: &StorageBatch, - grove_version: &GroveVersion, - ) -> CostResult<(), Error> { - check_grovedb_v0_with_cost!( - "insert_on_transaction", - grove_version - .grovedb_versions - .operations - .insert - .insert_on_transaction - ); - let mut cost = OperationCost::default(); + let merk_cache = MerkCache::new(&self, tx.as_ref(), grove_version); - let mut merk_cache: HashMap, Merk> = - HashMap::default(); - - let merk = cost_return_on_error!( + cost_return_on_error!( &mut cost, self.add_element_on_transaction( - path.clone(), + path.into(), key, element, - options, - transaction, - batch, - grove_version - ) - ); - merk_cache.insert(path.clone(), merk); - cost_return_on_error!( - &mut cost, - self.propagate_changes_with_transaction( - merk_cache, - path, - transaction, - batch, + options.unwrap_or_default(), + &merk_cache, grove_version ) ); - Ok(()).wrap_with_cost(cost) + let batch = cost_return_on_error!(&mut cost, merk_cache.into_batch()); + self.db + .commit_multi_context_batch(*batch, Some(tx.as_ref())) + .map_err(Into::into) + .map_ok(|_| tx.commit_local()) + .flatten() + .add_cost(cost) } /// Add subtree to another subtree. @@ -150,14 +100,13 @@ impl GroveDb { /// first make sure other merk exist /// if it exists, then create merk to be inserted, and get root hash /// we only care about root hash of merk to be inserted - fn add_element_on_transaction<'db, B: AsRef<[u8]>>( + fn add_element_on_transaction<'db, 'b, B: AsRef<[u8]>>( &'db self, - path: SubtreePath, + path: SubtreePath<'b, B>, key: &[u8], element: Element, options: InsertOptions, - transaction: &'db Transaction, - batch: &'db StorageBatch, + merk_cache: &MerkCache<'db, 'b, B>, grove_version: &GroveVersion, ) -> CostResult>, Error> { check_grovedb_v0_with_cost!( @@ -171,17 +120,10 @@ impl GroveDb { let mut cost = OperationCost::default(); - let mut subtree_to_insert_into = cost_return_on_error!( - &mut cost, - self.open_transactional_merk_at_path( - path.clone(), - transaction, - Some(batch), - grove_version - ) - ); - // if we don't allow a tree override then we should check + let mut subtree_to_insert_into = + cost_return_on_error!(&mut cost, merk_cache.get_merk(path.derive_owned())); + // if we don't allow a tree override then we should check if options.checks_for_override() { let maybe_element_bytes = cost_return_on_error!( &mut cost, @@ -229,15 +171,16 @@ impl GroveDb { .wrap_with_cost(OperationCost::default()) ); - let referenced_item = cost_return_on_error!( - &mut cost, - self.follow_reference( - reference_path.as_slice().into(), - false, - transaction, - grove_version - ) - ); + let referenced_item: Element = todo!(); + // cost_return_on_error!( + // &mut cost, + // self.follow_reference( + // reference_path.as_slice().into(), + // false, + // transaction, + // grove_version + // ) + // ); if matches!( referenced_item, @@ -295,7 +238,8 @@ impl GroveDb { } } - Ok(subtree_to_insert_into).wrap_with_cost(cost) + // Ok(subtree_to_insert_into).wrap_with_cost(cost) + todo!() } /// Inserts an element at the specified path and key if it does not already diff --git a/grovedb/src/util.rs b/grovedb/src/util.rs index f8761aa3..7a8e3081 100644 --- a/grovedb/src/util.rs +++ b/grovedb/src/util.rs @@ -8,7 +8,7 @@ use grovedb_storage::{ use grovedb_version::version::GroveVersion; use grovedb_visualize::DebugByteVectors; -use crate::{Element, Error, Transaction, TransactionArg}; +use crate::{merk_cache::MerkCache, Element, Error, Transaction, TransactionArg}; pub(crate) enum TxRef<'a, 'db: 'a> { Owned(Transaction<'db>), @@ -104,3 +104,6 @@ where .add_cost(cost) } } + +// pub(crate) fn follow_reference<'db, 'b, B>(merk_cache: &MerkCache<'db, 'b, +// B>, path: ) diff --git a/path/src/subtree_path.rs b/path/src/subtree_path.rs index 502a74c9..7864f84e 100644 --- a/path/src/subtree_path.rs +++ b/path/src/subtree_path.rs @@ -105,6 +105,25 @@ where } } +impl<'bl, 'br, BL, BR> PartialOrd> for SubtreePathBuilder<'bl, BL> +where + BL: AsRef<[u8]>, + BR: AsRef<[u8]>, +{ + fn partial_cmp(&self, other: &SubtreePathBuilder<'br, BR>) -> Option { + let iter_a = self.reverse_iter(); + let iter_b = other.reverse_iter(); + + Some( + iter_a + .len() + .cmp(&iter_b.len()) + .reverse() + .then_with(|| iter_a.cmp(iter_b)), + ) + } +} + impl<'bl, 'br, BL, BR> PartialOrd> for SubtreePath<'bl, BL> where BL: AsRef<[u8]>, @@ -124,6 +143,15 @@ where } } +impl<'bl, BL> Ord for SubtreePathBuilder<'bl, BL> +where + BL: AsRef<[u8]>, +{ + fn cmp(&self, other: &Self) -> cmp::Ordering { + self.partial_cmp(other).expect("order is totally defined") + } +} + impl<'b, B: AsRef<[u8]>> Eq for SubtreePath<'b, B> {} impl<'b, B> From> for SubtreePath<'b, B> { diff --git a/path/src/subtree_path_builder.rs b/path/src/subtree_path_builder.rs index 4ef25f0a..4428e0c2 100644 --- a/path/src/subtree_path_builder.rs +++ b/path/src/subtree_path_builder.rs @@ -149,6 +149,28 @@ impl Default for SubtreePathBuilder<'static, [u8; 0]> { } } +impl SubtreePathBuilder<'static, B> { + /// Makes an owned `SubtreePathBuilder` out of iterator. + pub fn owned_from_iter>(iter: impl IntoIterator) -> Self { + let bytes = iter.into_iter().fold(CompactBytes::new(), |mut bytes, s| { + bytes.add_segment(s.as_ref()); + bytes + }); + + SubtreePathBuilder { + base: SubtreePath { + ref_variant: SubtreePathInner::Slice(&[]), + }, + relative: SubtreePathRelative::Multi(bytes), + } + } + + /// Create an owned version of `SubtreePathBuilder` from `SubtreePath`. + pub fn owned_from_path<'b, S: AsRef<[u8]>>(path: SubtreePath<'b, S>) -> Self { + Self::owned_from_iter(path.to_vec()) + } +} + impl SubtreePathBuilder<'_, B> { /// Returns the length of the subtree path. pub fn len(&self) -> usize { @@ -159,6 +181,24 @@ impl SubtreePathBuilder<'_, B> { pub fn is_empty(&self) -> bool { self.base.is_empty() && self.relative.is_empty() } + + /// Adds path segment in place. + pub fn push_segment(&mut self, segment: &[u8]) { + match &mut self.relative { + SubtreePathRelative::Empty => { + let mut bytes = CompactBytes::new(); + bytes.add_segment(segment); + self.relative = SubtreePathRelative::Multi(bytes); + } + SubtreePathRelative::Single(old_segment) => { + let mut bytes = CompactBytes::new(); + bytes.add_segment(old_segment); + bytes.add_segment(segment); + self.relative = SubtreePathRelative::Multi(bytes); + } + SubtreePathRelative::Multi(bytes) => bytes.add_segment(segment), + } + } } impl<'b, B: AsRef<[u8]>> SubtreePathBuilder<'b, B> { @@ -191,6 +231,37 @@ impl<'b, B: AsRef<[u8]>> SubtreePathBuilder<'b, B> { } } + /// Get a derived path for a parent and a chopped segment. Returned + /// [SubtreePath] will be linked to this [SubtreePath] because it might + /// contain owned data and it has to outlive [SubtreePath]. + pub fn derive_parent_owned(&self) -> Option<(SubtreePathBuilder<'b, B>, Vec)> { + match &self.relative { + SubtreePathRelative::Empty => self + .base + .derive_parent() + .map(|(path, key)| (path.derive_owned(), key.to_vec())), + SubtreePathRelative::Single(relative) => { + Some((self.base.derive_owned(), relative.to_vec())) + } + SubtreePathRelative::Multi(bytes) => { + let mut new_bytes = bytes.clone(); + if let Some(key) = new_bytes.pop_segment() { + Some(( + SubtreePathBuilder { + base: self.base.clone(), + relative: SubtreePathRelative::Multi(new_bytes), + }, + key, + )) + } else { + self.base + .derive_parent() + .map(|(path, key)| (path.derive_owned(), key.to_vec())) + } + } + } + } + /// Get a derived path with a child path segment added. pub fn derive_owned_with_child<'s, S>(&'b self, segment: S) -> SubtreePathBuilder<'b, B> where @@ -203,24 +274,6 @@ impl<'b, B: AsRef<[u8]>> SubtreePathBuilder<'b, B> { } } - /// Adds path segment in place. - pub fn push_segment(&mut self, segment: &[u8]) { - match &mut self.relative { - SubtreePathRelative::Empty => { - let mut bytes = CompactBytes::new(); - bytes.add_segment(segment); - self.relative = SubtreePathRelative::Multi(bytes); - } - SubtreePathRelative::Single(old_segment) => { - let mut bytes = CompactBytes::new(); - bytes.add_segment(old_segment); - bytes.add_segment(segment); - self.relative = SubtreePathRelative::Multi(bytes); - } - SubtreePathRelative::Multi(bytes) => bytes.add_segment(segment), - } - } - /// Returns an iterator for the subtree path by path segments. pub fn reverse_iter(&'b self) -> SubtreePathIter<'b, B> { match &self.relative { diff --git a/path/src/util/compact_bytes.rs b/path/src/util/compact_bytes.rs index 1e4362cb..d4cdba9d 100644 --- a/path/src/util/compact_bytes.rs +++ b/path/src/util/compact_bytes.rs @@ -31,7 +31,7 @@ use std::mem; /// Bytes vector wrapper to have multiple byte arrays allocated continuosuly. -#[derive(Debug, Default)] +#[derive(Debug, Default, Clone)] pub(crate) struct CompactBytes { n_segments: usize, data: Vec, @@ -64,6 +64,29 @@ impl CompactBytes { pub fn len(&self) -> usize { self.n_segments } + + pub fn pop_segment(&mut self) -> Option> { + if self.n_segments < 1 { + return None; + } + + let length_size = mem::size_of::(); + let last_segment_length = usize::from_ne_bytes( + self.data[self.data.len() - length_size..] + .try_into() + .expect("internal structure bug"), + ); + + let segment = self.data + [self.data.len() - last_segment_length - length_size..self.data.len() - length_size] + .to_vec(); + + self.data + .truncate(self.data.len() - last_segment_length - length_size); + self.n_segments -= 1; + + Some(segment) + } } #[derive(Debug, Clone)] @@ -160,4 +183,25 @@ mod tests { assert_eq!(iter.next(), None); assert_eq!(iter.next(), None); } + + #[test] + fn pop_segment() { + let mut bytes = CompactBytes::default(); + bytes.add_segment(b"ayya"); + bytes.add_segment(b"ayyb"); + bytes.add_segment(b"ayyc"); + bytes.add_segment(b"ayyd"); + + assert_eq!(bytes.pop_segment(), Some(b"ayyd".to_vec())); + assert_eq!(bytes.pop_segment(), Some(b"ayyc".to_vec())); + + let mut v: Vec<_> = bytes.reverse_iter().collect(); + v.reverse(); + assert_eq!(v, vec![b"ayya".to_vec(), b"ayyb".to_vec()]); + + assert_eq!(bytes.pop_segment(), Some(b"ayyb".to_vec())); + assert_eq!(bytes.pop_segment(), Some(b"ayya".to_vec())); + assert_eq!(bytes.pop_segment(), None); + assert_eq!(bytes.pop_segment(), None); + } }