refactor: make merge trait configurable

This commit is contained in:
Zixuan Chen 2022-07-15 16:01:35 +08:00
parent 3eb415718c
commit 1444029e5e
5 changed files with 72 additions and 46 deletions

View file

@ -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<ChangeMergeCfg> 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;
}

View file

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

View file

@ -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<ClientID, RleVec<Change>>,
ops: HashMap<ClientID, RleVec<Change, ChangeMergeCfg>>,
lamport: Lamport,
}

View file

@ -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!(),
},
}

View file

@ -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<T> {
pub struct RleVec<T, Cfg = ()> {
vec: Vec<T>,
_len: usize,
index: Vec<usize>,
cfg: Cfg,
}
pub trait Mergable {
fn is_mergable(&self, other: &Self) -> bool
pub trait Mergable<Cfg = ()> {
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<T: Mergable + HasLength> RleVec<T> {
impl<T: Mergable<Cfg> + HasLength, Cfg> RleVec<T, Cfg> {
/// 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<T: Mergable + HasLength> RleVec<T> {
}
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<T: Mergable + HasLength> RleVec<T> {
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<SearchResult<'_, T>> {
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<T: Mergable + HasLength> RleVec<T> {
}
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<T: Mergable + HasLength> RleVec<T> {
}
}
impl<T> RleVec<T> {
impl<T, Conf: Default> RleVec<T, Conf> {
pub fn new() -> Self {
RleVec {
vec: Vec::new(),
_len: 0,
index: Vec::new(),
cfg: Default::default(),
}
}
}
impl<T, Conf> RleVec<T, Conf> {
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<T: Mergable + HasLength + Sliceable + Clone> Mergable for RleVec<T> {
fn is_mergable(&self, _: &Self) -> bool {
impl<T: Mergable<Cfg> + HasLength + Sliceable + Clone, Cfg> Mergable<Cfg> for RleVec<T, Cfg> {
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]