mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-02 02:59:51 +00:00
chore: tree ds
This commit is contained in:
parent
32687f61e8
commit
68b717cfb3
3 changed files with 261 additions and 0 deletions
|
@ -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<_>>(), 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<_>>(), 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<_>>(),
|
||||
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<_>>(),
|
||||
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<_>>(), 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<_>>(), 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<_>>(), vec![true, false]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,2 +1,3 @@
|
|||
pub(crate) mod bool_rle_vec;
|
||||
pub(crate) mod tree_ds;
|
||||
pub(crate) mod tree_op;
|
||||
|
|
104
crates/loro-internal/src/container/tree/tree_ds.rs
Normal file
104
crates/loro-internal/src/container/tree/tree_ds.rs
Normal file
|
@ -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<TreeOpWrap>,
|
||||
has_effect: BoolRleVec,
|
||||
target_to_op_idx: FxHashMap<TreeID, usize>,
|
||||
}
|
||||
|
||||
struct TreeOpWrap {
|
||||
op: TreeOp,
|
||||
last_update_on_target: Option<usize>,
|
||||
}
|
||||
|
||||
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<IdFull>,
|
||||
}
|
||||
|
||||
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<TreeOp>, 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<PoppedTreeOp> {
|
||||
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<PoppedTreeOp> {
|
||||
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<PoppedTreeOp> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn last_update_on(&self, target: TreeID) -> Option<IdFull> {
|
||||
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<IdLp> {
|
||||
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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue