diff --git a/crates/examples/examples/outliner.rs b/crates/examples/examples/outliner.rs index f24c51d0..ff03963a 100644 --- a/crates/examples/examples/outliner.rs +++ b/crates/examples/examples/outliner.rs @@ -86,6 +86,11 @@ pub fn main() { "Memory usage after freeing diff calculator: {}", get_mem_usage() ); + doc.free_history_cache(); + println!( + "Memory usage after freeing history cache: {}", + get_mem_usage() + ); println!( "Updates size: {}", diff --git a/crates/loro-internal/src/container/tree/tree_op.rs b/crates/loro-internal/src/container/tree/tree_op.rs index 79063a70..9e25d7c2 100644 --- a/crates/loro-internal/src/container/tree/tree_op.rs +++ b/crates/loro-internal/src/container/tree/tree_op.rs @@ -38,6 +38,15 @@ impl TreeOp { TreeOp::Delete { target, .. } => *target, } } + + pub(crate) fn parent(&self) -> Option { + match self { + TreeOp::Create { parent, .. } => *parent, + TreeOp::Move { parent, .. } => *parent, + TreeOp::Delete { .. } => Some(TreeID::delete_root()), + } + } + pub(crate) fn parent_id(&self) -> TreeParentId { match self { TreeOp::Create { parent, .. } => TreeParentId::from(*parent), @@ -45,6 +54,7 @@ impl TreeOp { TreeOp::Delete { .. } => TreeParentId::Deleted, } } + pub(crate) fn fractional_index(&self) -> Option { match self { TreeOp::Create { position, .. } | TreeOp::Move { position, .. } => { diff --git a/crates/loro-internal/src/diff_calc/tree.rs b/crates/loro-internal/src/diff_calc/tree.rs index 3cb4e3d7..6f2951b8 100644 --- a/crates/loro-internal/src/diff_calc/tree.rs +++ b/crates/loro-internal/src/diff_calc/tree.rs @@ -1,4 +1,4 @@ -use std::collections::BTreeSet; +use std::{collections::BTreeSet, sync::Arc}; use fractional_index::FractionalIndex; use fxhash::FxHashMap; @@ -6,7 +6,7 @@ use itertools::Itertools; use loro_common::{ContainerID, IdFull, IdLp, IdSpan, Lamport, PeerID, TreeID, ID}; use crate::{ - container::idx::ContainerIdx, + container::{idx::ContainerIdx, tree::tree_op::TreeOp}, dag::DagUtils, delta::{TreeDelta, TreeDeltaItem, TreeInternalDiff}, event::InternalDiff, @@ -178,17 +178,21 @@ impl TreeDiffCalculator { let mut retreat_ops = vec![]; for (_target, ops) in tree_cache.tree.iter() { for op in ops.iter().rev() { - if op.lamport < min_lamport { + if op.id.lamport < min_lamport { break; } - if !to.includes_id(op.id) { + if !to.includes_id(op.id.id()) { retreat_ops.push(op.clone()); } } } tracing::info!(msg="retreat ops", retreat_ops=?retreat_ops); for op in retreat_ops { - tree_cache.tree.get_mut(&op.target).unwrap().remove(&op); + tree_cache + .tree + .get_mut(&op.op.target()) + .unwrap() + .remove(&op); tree_cache.current_vv.shrink_to_exclude(IdSpan::new( op.id.peer, op.id.counter, @@ -228,11 +232,8 @@ impl TreeDiffCalculator { // tracing::info!("forward ops {:?}", forward_ops); for (id, op) in forward_ops { let op = MoveLamportAndID { - target: op.value.target(), - parent: op.value.parent_id(), - position: op.value.fractional_index(), - id: id.id(), - lamport: id.lamport, + id, + op: op.value.clone(), effected: false, }; tree_cache.apply(op); @@ -277,10 +278,10 @@ impl TreeDiffCalculator { let mut retreat_ops = vec![]; for (_target, ops) in tree_cache.tree.iter() { for op in ops.iter().rev() { - if op.lamport < lca_min_lamport { + if op.id.lamport < lca_min_lamport { break; } - if !lca_vv.includes_id(op.id) { + if !lca_vv.includes_id(op.id.id()) { retreat_ops.push(op.clone()); } } @@ -288,22 +289,26 @@ impl TreeDiffCalculator { // tracing::info!("retreat ops {:?}", retreat_ops); for op in retreat_ops.into_iter().sorted().rev() { - tree_cache.tree.get_mut(&op.target).unwrap().remove(&op); + tree_cache + .tree + .get_mut(&op.op.target()) + .unwrap() + .remove(&op); tree_cache.current_vv.shrink_to_exclude(IdSpan::new( op.id.peer, op.id.counter, op.id.counter + 1, )); let (old_parent, position, last_effective_move_op_id) = - tree_cache.get_parent_with_id(op.target); + tree_cache.get_parent_with_id(op.op.target()); if op.effected { // we need to know whether old_parent is deleted - let is_parent_deleted = tree_cache.is_parent_deleted(op.parent); + let is_parent_deleted = tree_cache.is_parent_deleted(op.op.parent_id()); let is_old_parent_deleted = tree_cache.is_parent_deleted(old_parent); let this_diff = TreeDeltaItem::new( - op.target, + op.op.target(), old_parent, - op.parent, + op.op.parent_id(), last_effective_move_op_id, is_old_parent_deleted, is_parent_deleted, @@ -312,7 +317,7 @@ impl TreeDiffCalculator { let is_create = matches!(this_diff.action, TreeInternalDiff::Create { .. }); diffs.push(this_diff); if is_create { - let mut s = vec![op.target]; + let mut s = vec![op.op.target()]; while let Some(t) = s.pop() { let children = tree_cache.get_children_with_id(TreeParentId::Node(t)); children.iter().for_each(|c| { @@ -350,32 +355,34 @@ impl TreeDiffCalculator { let id = ID::new(idlp.peer, op.counter); if !tree_cache.current_vv.includes_id(id) && to.includes_id(id) { let op = MoveLamportAndID { - target: op.value.target(), - parent: op.value.parent_id(), - position: op.value.fractional_index(), - id, - lamport: idlp.lamport, + id: IdFull { + peer: id.peer, + lamport: idlp.lamport, + counter: id.counter, + }, + op: op.value.clone(), effected: false, }; - let (old_parent, _position, _id) = tree_cache.get_parent_with_id(op.target); - let is_parent_deleted = tree_cache.is_parent_deleted(op.parent); + let (old_parent, _position, _id) = + tree_cache.get_parent_with_id(op.op.target()); + let is_parent_deleted = tree_cache.is_parent_deleted(op.op.parent_id()); let is_old_parent_deleted = tree_cache.is_parent_deleted(old_parent); let effected = tree_cache.apply(op.clone()); if effected { let this_diff = TreeDeltaItem::new( - op.target, - op.parent, + op.op.target(), + op.op.parent_id(), old_parent, op.id_full(), is_parent_deleted, is_old_parent_deleted, - op.position, + op.op.fractional_index(), ); let is_create = matches!(this_diff.action, TreeInternalDiff::Create { .. }); diffs.push(this_diff); if is_create { // TODO: per - let mut s = vec![op.target]; + let mut s = vec![op.op.target()]; while let Some(t) = s.pop() { let children = tree_cache.get_children_with_id(TreeParentId::Node(t)); @@ -417,13 +424,10 @@ impl TreeDiffCalculator { } /// All information of an operation for diff calculating of movable tree. -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone)] pub struct MoveLamportAndID { - pub(crate) lamport: Lamport, - pub(crate) id: ID, - pub(crate) target: TreeID, - pub(crate) parent: TreeParentId, - pub(crate) position: Option, + pub(crate) id: IdFull, + pub(crate) op: Arc, /// Whether this action is applied in the current version. /// If this action will cause a circular reference, then this action will not be applied. pub(crate) effected: bool, @@ -431,14 +435,18 @@ pub struct MoveLamportAndID { impl MoveLamportAndID { fn id_full(&self) -> IdFull { - IdFull { - peer: self.id.peer, - lamport: self.lamport, - counter: self.id.counter, - } + self.id } } +impl PartialEq for MoveLamportAndID { + fn eq(&self, other: &Self) -> bool { + self.id == other.id + } +} + +impl Eq for MoveLamportAndID {} + impl PartialOrd for MoveLamportAndID { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) @@ -447,9 +455,10 @@ impl PartialOrd for MoveLamportAndID { impl Ord for MoveLamportAndID { fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.lamport - .cmp(&other.lamport) - .then_with(|| self.id.cmp(&other.id)) + self.id + .lamport + .cmp(&other.id.lamport) + .then_with(|| self.id.peer.cmp(&other.id.peer)) } } @@ -484,12 +493,12 @@ impl TreeCacheForDiff { fn apply(&mut self, mut node: MoveLamportAndID) -> bool { let mut effected = true; - if self.is_ancestor_of(&node.target, &node.parent) { + if self.is_ancestor_of(&node.op.target(), &node.op.parent_id()) { effected = false; } node.effected = effected; - self.current_vv.set_last(node.id); - self.tree.entry(node.target).or_default().insert(node); + self.current_vv.set_last(node.id.id()); + self.tree.entry(node.op.target()).or_default().insert(node); effected } @@ -511,7 +520,11 @@ impl TreeCacheForDiff { if let Some(cache) = self.tree.get(&tree_id) { for op in cache.iter().rev() { if op.effected { - ans = (op.parent, op.position.clone(), op.id_full()); + ans = ( + op.op.parent_id(), + op.op.fractional_index().clone(), + op.id_full(), + ); break; } } @@ -548,8 +561,8 @@ impl TreeCacheForDiff { continue; }; - if op.parent == parent { - ans.push((*tree_id, op.position.clone(), op.id_full())); + if op.op.parent_id() == parent { + ans.push((*tree_id, op.op.fractional_index().clone(), op.id_full())); } } // The children should be sorted by the position.