diff --git a/crates/loro-internal/src/container/tree/bool_rle_vec.rs b/crates/loro-internal/src/container/tree/bool_rle_vec.rs index 27cc4c49..b9922ca1 100644 --- a/crates/loro-internal/src/container/tree/bool_rle_vec.rs +++ b/crates/loro-internal/src/container/tree/bool_rle_vec.rs @@ -52,6 +52,57 @@ impl BoolRleVec { Some(is_last_run_true) } + pub fn drop_last_n(&mut self, n: usize) { + if n > self.len { + panic!("Attempted to drop more elements than exist in the vector"); + } + + let mut remaining = n; + while remaining > 0 { + if let Some(last) = self.rle_vec.last_mut() { + if *last <= remaining as u32 { + remaining -= *last as usize; + self.rle_vec.pop(); + } else { + *last -= remaining as u32; + remaining = 0; + } + } else { + break; + } + } + + self.len -= n; + + // Remove any trailing zero-length runs + while self.rle_vec.last().map_or(false, |&x| x == 0) { + self.rle_vec.pop(); + } + } + + pub fn merge(&mut self, other: &BoolRleVec) { + if self.is_empty() { + self.rle_vec = other.rle_vec.clone(); + self.len = other.len; + return; + } + + if other.is_empty() { + return; + } + + // Align the end of self with the start of other + let self_last_run_true = self.rle_vec.len() % 2 == 0; + if self_last_run_true { + self.rle_vec.extend_from_slice(&other.rle_vec); + } else { + *self.rle_vec.last_mut().unwrap() += other.rle_vec[0]; + self.rle_vec.extend_from_slice(&other.rle_vec[1..]); + } + + self.len += other.len; + } + pub fn len(&self) -> usize { self.len } @@ -281,4 +332,109 @@ mod test { let iter = long_rle_vec.iter().skip(999); assert_eq!(iter.collect::>(), vec![true, false, true]); } + + #[test] + fn test_bool_rle_vec_merge() { + // Test merging two empty vectors + let mut vec1 = BoolRleVec::new(); + let vec2 = BoolRleVec::new(); + vec1.merge(&vec2); + assert!(vec1.is_empty()); + + // Test merging an empty vector with a non-empty one + let mut vec1 = BoolRleVec::new(); + let mut vec2 = BoolRleVec::new(); + vec2.push(true); + vec2.push(false); + vec1.merge(&vec2); + assert_eq!(vec1.iter().collect::>(), vec![true, false]); + + // Test merging two non-empty vectors + let mut vec1 = BoolRleVec::new(); + let mut vec2 = BoolRleVec::new(); + vec1.push(true); + vec1.push(true); + vec2.push(false); + vec2.push(true); + vec1.merge(&vec2); + assert_eq!( + vec1.iter().collect::>(), + vec![true, true, false, true] + ); + + // Test merging with alignment (last run of vec1 matches first run of vec2) + let mut vec1 = BoolRleVec::new(); + let mut vec2 = BoolRleVec::new(); + vec1.push(true); + vec1.push(false); + vec2.push(false); + vec2.push(true); + vec1.merge(&vec2); + assert_eq!( + vec1.iter().collect::>(), + vec![true, false, false, true] + ); + + // Test merging with long runs + let mut vec1 = BoolRleVec::new(); + let mut vec2 = BoolRleVec::new(); + for _ in 0..100 { + vec1.push(true); + } + for _ in 0..100 { + vec2.push(false); + } + vec1.merge(&vec2); + assert_eq!(vec1.len(), 200); + assert_eq!(vec1.rle_vec.len(), 3); + assert_eq!(vec1.iter().take(100).filter(|&x| x).count(), 100); + assert_eq!(vec1.iter().skip(100).filter(|&x| !x).count(), 100); + } + + #[test] + fn test_drop_last_n() { + // Test dropping from a single run + let mut vec = BoolRleVec::new(); + for _ in 0..5 { + vec.push(true); + } + vec.drop_last_n(3); + assert_eq!(vec.iter().collect::>(), vec![true, true]); + + // Test dropping across multiple runs + let mut vec = BoolRleVec::new(); + vec.push(true); + vec.push(true); + vec.push(false); + vec.push(false); + vec.push(true); + vec.drop_last_n(3); + assert_eq!(vec.iter().collect::>(), vec![true, true]); + + // Test dropping entire vector + let mut vec = BoolRleVec::new(); + vec.push(true); + vec.push(false); + vec.drop_last_n(2); + assert!(vec.is_empty()); + + // Test dropping from long runs + let mut vec = BoolRleVec::new(); + for _ in 0..100 { + vec.push(true); + } + for _ in 0..100 { + vec.push(false); + } + vec.drop_last_n(150); + assert_eq!(vec.len(), 50); + assert!(vec.iter().all(|x| x)); + + // Test dropping zero elements + let mut vec = BoolRleVec::new(); + vec.push(true); + vec.push(false); + vec.drop_last_n(0); + assert_eq!(vec.iter().collect::>(), vec![true, false]); + } } diff --git a/crates/loro-internal/src/container/tree/mod.rs b/crates/loro-internal/src/container/tree/mod.rs index 52f31ddc..d9f97a10 100644 --- a/crates/loro-internal/src/container/tree/mod.rs +++ b/crates/loro-internal/src/container/tree/mod.rs @@ -1,2 +1,3 @@ pub(crate) mod bool_rle_vec; +pub(crate) mod tree_ds; pub(crate) mod tree_op; diff --git a/crates/loro-internal/src/container/tree/tree_ds.rs b/crates/loro-internal/src/container/tree/tree_ds.rs new file mode 100644 index 00000000..7888376c --- /dev/null +++ b/crates/loro-internal/src/container/tree/tree_ds.rs @@ -0,0 +1,104 @@ +use either::Either; +use fxhash::{FxHashMap, FxHashSet}; +use loro_common::{IdFull, IdLp, TreeID, ID}; + +use crate::VersionVector; + +use super::bool_rle_vec::BoolRleVec; + +pub(crate) struct TreeLinearHistory { + ops: Vec, + has_effect: BoolRleVec, + target_to_op_idx: FxHashMap, +} + +struct TreeOpWrap { + op: TreeOp, + last_update_on_target: Option, +} + +pub(crate) struct TreeOp { + pub id: IdFull, + pub target: TreeID, +} + +pub(crate) struct PoppedTreeOp { + pub op: TreeOp, + pub has_effect: bool, + pub last_update_on_target: Option, +} + +impl TreeLinearHistory { + pub fn new() -> Self { + Self { + ops: Vec::new(), + has_effect: BoolRleVec::new(), + target_to_op_idx: FxHashMap::default(), + } + } + + pub fn from_vec(ops: Vec, has_effect: BoolRleVec) -> Self { + let mut this = Self::new(); + for (op, e) in ops.into_iter().zip(has_effect.iter()) { + this.push(op, e); + } + + this + } + + pub fn push(&mut self, op: TreeOp, has_effect: bool) { + let last = self.target_to_op_idx.insert(op.target, self.ops.len()); + self.ops.push(TreeOpWrap { + op, + last_update_on_target: last, + }); + self.has_effect.push(has_effect); + } + + pub fn pop_util(&mut self, threshold: IdLp) -> Vec { + let index = match self + .ops + .binary_search_by(|op_wrap| op_wrap.op.id.idlp().cmp(&threshold)) + { + Ok(index) => index, + Err(index) => index, + }; + + self._pop_util(index) + } + + // Some of them need to be pushed back later if they are inside the vv + #[must_use] + pub fn pop_until_all_inside_vv(&mut self, vv: &VersionVector) -> Vec { + for (i, op_wrap) in self.ops.iter().enumerate() { + if vv.get(&op_wrap.op.id.peer).unwrap() <= &op_wrap.op.id.counter { + return self._pop_util(i); + } + } + + vec![] + } + + fn _pop_util(&mut self, index: usize) -> Vec { + todo!() + } + + pub fn last_update_on(&self, target: TreeID) -> Option { + self.target_to_op_idx + .get(&target) + .and_then(|&idx| self.ops.get(idx)) + .map(|op_wrap| op_wrap.op.id) + } + + pub fn greatest_idlp(&self) -> Option { + self.ops.last().map(|op| op.op.id.idlp()) + } + + pub fn len(&self) -> usize { + self.ops.len() + } + + pub fn get_bool_rle_vec(&self) -> &BoolRleVec { + &self.has_effect + } +}