chore: tree ds

This commit is contained in:
Zixuan Chen 2024-08-13 16:43:39 +08:00
parent 32687f61e8
commit 68b717cfb3
No known key found for this signature in database
3 changed files with 261 additions and 0 deletions

View file

@ -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]);
}
}

View file

@ -1,2 +1,3 @@
pub(crate) mod bool_rle_vec;
pub(crate) mod tree_ds;
pub(crate) mod tree_op;

View 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
}
}