diff --git a/crates/loro-core/src/change.rs b/crates/loro-core/src/change.rs index b55ee26f..28337e21 100644 --- a/crates/loro-core/src/change.rs +++ b/crates/loro-core/src/change.rs @@ -6,9 +6,6 @@ use smallvec::SmallVec; pub type Timestamp = i64; pub type Lamport = u64; -const MAX_CHANGE_LENGTH: usize = 256; -/// TODO: Should this be configurable? -const MAX_MERGABLE_INTERVAL: Timestamp = 60; /// Change #[derive(Debug)] @@ -54,12 +51,17 @@ impl HasLength for Change { } } -impl Mergable for Change { - fn merge(&mut self, other: &Self) { - self.ops.merge(&other.ops); +pub struct ChangeMergeCfg { + max_change_length: usize, + max_change_interval: usize, +} + +impl Mergable for Change { + fn merge(&mut self, other: &Self, cfg: &ChangeMergeCfg) { + self.ops.merge(&other.ops, &()); } - fn is_mergable(&self, other: &Self) -> bool { + fn is_mergable(&self, other: &Self, cfg: &ChangeMergeCfg) -> bool { if self.freezed { return false; } @@ -68,11 +70,11 @@ impl Mergable for Change { return false; } - if self.len() > MAX_CHANGE_LENGTH { + if self.len() > cfg.max_change_length { return false; } - if other.timestamp - self.timestamp > MAX_MERGABLE_INTERVAL { + if other.timestamp - self.timestamp > cfg.max_change_interval as i64 { return false; } diff --git a/crates/loro-core/src/id_span.rs b/crates/loro-core/src/id_span.rs index 74a74721..23d2e954 100644 --- a/crates/loro-core/src/id_span.rs +++ b/crates/loro-core/src/id_span.rs @@ -60,11 +60,11 @@ impl Sliceable for IdSpan { } impl Mergable for IdSpan { - fn is_mergable(&self, other: &Self) -> bool { + fn is_mergable(&self, other: &Self, _: &()) -> bool { self.client_id == other.client_id && self.to == other.from } - fn merge(&mut self, other: &Self) { + fn merge(&mut self, other: &Self, _: &()) { self.to = other.to; } } diff --git a/crates/loro-core/src/log_store.rs b/crates/loro-core/src/log_store.rs index cfdb8678..c8203dc2 100644 --- a/crates/loro-core/src/log_store.rs +++ b/crates/loro-core/src/log_store.rs @@ -2,10 +2,10 @@ use rle::RleVec; use std::collections::HashMap; use string_cache::{Atom, DefaultAtom, EmptyStaticAtomSet}; -use crate::{change::Change, id::ClientID, Lamport, ID}; +use crate::{change::Change, id::ClientID, ChangeMergeCfg, Lamport, ID}; pub struct LogStore { - ops: HashMap>, + ops: HashMap>, lamport: Lamport, } diff --git a/crates/loro-core/src/op.rs b/crates/loro-core/src/op.rs index a8e12e45..921a6059 100644 --- a/crates/loro-core/src/op.rs +++ b/crates/loro-core/src/op.rs @@ -49,7 +49,7 @@ impl Op { } impl Mergable for Op { - fn is_mergable(&self, other: &Self) -> bool { + fn is_mergable(&self, other: &Self, cfg: &()) -> bool { match &self.content { OpContent::Insert { container, content } => match other.content { OpContent::Insert { @@ -62,20 +62,26 @@ impl Mergable for Op { OpContent::Delete { target: ref other_target, lamport: ref other_lamport, - } => lamport + target.len() == *other_lamport && target.is_mergable(other_target), + } => { + lamport + target.len() == *other_lamport + && target.is_mergable(other_target, cfg) + } _ => false, }, OpContent::Restore { target, lamport } => match other.content { OpContent::Restore { target: ref other_target, lamport: ref other_lamport, - } => lamport + target.len() == *other_lamport && target.is_mergable(other_target), + } => { + lamport + target.len() == *other_lamport + && target.is_mergable(other_target, cfg) + } _ => false, }, } } - fn merge(&mut self, other: &Self) { + fn merge(&mut self, other: &Self, cfg: &()) { match &mut self.content { OpContent::Insert { container, content } => match &other.content { OpContent::Insert { @@ -91,14 +97,14 @@ impl Mergable for Op { OpContent::Delete { target: other_target, .. - } => target.merge(other_target), + } => target.merge(other_target, cfg), _ => unreachable!(), }, OpContent::Restore { target, .. } => match &other.content { OpContent::Restore { target: other_target, .. - } => target.merge(other_target), + } => target.merge(other_target, cfg), _ => unreachable!(), }, } diff --git a/crates/rle/src/rle.rs b/crates/rle/src/rle.rs index c8f707ca..f4bc33b7 100644 --- a/crates/rle/src/rle.rs +++ b/crates/rle/src/rle.rs @@ -11,17 +11,18 @@ /// - get(index) returns the atom element at the index. /// - slice(from, to) returns a slice of atom elements from the index from to the index to. #[derive(Debug, Clone)] -pub struct RleVec { +pub struct RleVec { vec: Vec, _len: usize, index: Vec, + cfg: Cfg, } -pub trait Mergable { - fn is_mergable(&self, other: &Self) -> bool +pub trait Mergable { + fn is_mergable(&self, other: &Self, conf: &Cfg) -> bool where Self: Sized; - fn merge(&mut self, other: &Self) + fn merge(&mut self, other: &Self, conf: &Cfg) where Self: Sized; } @@ -45,12 +46,12 @@ pub trait HasLength { } pub struct SearchResult<'a, T> { - element: &'a T, - merged_index: usize, - offset: usize, + pub element: &'a T, + pub merged_index: usize, + pub offset: usize, } -impl RleVec { +impl + HasLength, Cfg> RleVec { /// push a new element to the end of the array. It may be merged with last element. pub fn push(&mut self, value: T) { self._len += value.len(); @@ -62,8 +63,8 @@ impl RleVec { } let last = self.vec.last_mut().unwrap(); - if last.is_mergable(&value) { - last.merge(&value); + if last.is_mergable(&value, &self.cfg) { + last.merge(&value, &self.cfg); *self.index.last_mut().unwrap() = self._len; return; } @@ -75,13 +76,18 @@ impl RleVec { self.vec.is_empty() } + /// number of atom elements in the array. pub fn len(&self) -> usize { self._len } /// get the element at the given atom index. /// return: (element, merged_index, offset) - pub fn get(&self, index: usize) -> SearchResult<'_, T> { + pub fn get(&self, index: usize) -> Option> { + if index > self.len() { + return None; + } + let mut start = 0; let mut end = self.index.len() - 1; while start < end { @@ -103,17 +109,17 @@ impl RleVec { } let value = &self.vec[start]; - SearchResult { + Some(SearchResult { element: value, merged_index: start, offset: index - self.index[start], - } + }) } /// get a slice from `from` to `to` with atom indexes pub fn slice_iter(&self, from: usize, to: usize) -> SliceIterator<'_, T> { - let from_result = self.get(from); - let to_result = self.get(to); + let from_result = self.get(from).unwrap(); + let to_result = self.get(to).unwrap(); SliceIterator { vec: &self.vec, cur_index: from_result.merged_index, @@ -124,12 +130,24 @@ impl RleVec { } } -impl RleVec { +impl RleVec { pub fn new() -> Self { RleVec { vec: Vec::new(), _len: 0, index: Vec::new(), + cfg: Default::default(), + } + } +} + +impl RleVec { + pub fn new_cfg(cfg: Conf) -> Self { + RleVec { + vec: Vec::new(), + _len: 0, + index: Vec::new(), + cfg, } } @@ -207,12 +225,12 @@ impl<'a, T: HasLength> Iterator for SliceIterator<'a, T> { } } -impl Mergable for RleVec { - fn is_mergable(&self, _: &Self) -> bool { +impl + HasLength + Sliceable + Clone, Cfg> Mergable for RleVec { + fn is_mergable(&self, _: &Self, _: &Cfg) -> bool { true } - fn merge(&mut self, other: &Self) { + fn merge(&mut self, other: &Self, _: &Cfg) { for item in other.vec.iter() { self.push(item.clone()); } @@ -245,11 +263,11 @@ mod test { } impl Mergable for String { - fn is_mergable(&self, _: &Self) -> bool { + fn is_mergable(&self, _: &Self, _: &()) -> bool { self.len() < 8 } - fn merge(&mut self, other: &Self) { + fn merge(&mut self, other: &Self, _: &()) { self.push_str(other); } } @@ -266,13 +284,13 @@ mod test { vec.push("1234".to_string()); vec.push("5678".to_string()); vec.push("12345678".to_string()); - assert_eq!(vec.get(4).element, "12345678"); - assert_eq!(vec.get(4).merged_index, 0); - assert_eq!(vec.get(4).offset, 4); + assert_eq!(vec.get(4).unwrap().element, "12345678"); + assert_eq!(vec.get(4).unwrap().merged_index, 0); + assert_eq!(vec.get(4).unwrap().offset, 4); - assert_eq!(vec.get(8).element, "12345678"); - assert_eq!(vec.get(8).merged_index, 1); - assert_eq!(vec.get(8).offset, 0); + assert_eq!(vec.get(8).unwrap().element, "12345678"); + assert_eq!(vec.get(8).unwrap().merged_index, 1); + assert_eq!(vec.get(8).unwrap().offset, 0); } #[test]