refactor: seal change exp impl

This commit is contained in:
Zixuan Chen 2023-07-05 14:43:58 +08:00
parent 336bd1e497
commit f0f82fb581
22 changed files with 116 additions and 173 deletions

View file

@ -69,7 +69,7 @@ pub fn gen_realtime_actions(action_num: usize, client_num: usize, seed: u64) ->
Action::Text { client, action } => { Action::Text { client, action } => {
*client %= client_num; *client %= client_num;
if !action.ins.is_empty() { if !action.ins.is_empty() {
action.ins = (action.ins.as_bytes()[0] as u8).to_string(); action.ins = (action.ins.as_bytes()[0]).to_string();
} }
} }
Action::SyncAll => { Action::SyncAll => {

View file

@ -2,7 +2,7 @@ fn main() {
let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap(); let crate_dir = std::env::var("CARGO_MANIFEST_DIR").unwrap();
let config = cbindgen::Config::from_file("cbindgen.toml") let config = cbindgen::Config::from_file("cbindgen.toml")
.expect("Unable to find cbindgen.toml configuration file"); .expect("Unable to find cbindgen.toml configuration file");
cbindgen::generate_with_config(&crate_dir, config) cbindgen::generate_with_config(crate_dir, config)
.unwrap() .unwrap()
.write_to_file("target/loro_ffi.h"); .write_to_file("target/loro_ffi.h");
} }

View file

@ -124,7 +124,7 @@ mod run {
mod import { mod import {
use criterion::Criterion; use criterion::Criterion;
use loro_internal::{change::ChangeMergeCfg, configure::Configure, LoroCore}; use loro_internal::{configure::Configure, LoroCore};
pub fn causal_iter(c: &mut Criterion) { pub fn causal_iter(c: &mut Criterion) {
let mut b = c.benchmark_group("causal_iter"); let mut b = c.benchmark_group("causal_iter");
@ -133,20 +133,12 @@ mod import {
b.iter(|| { b.iter(|| {
let mut c1 = LoroCore::new( let mut c1 = LoroCore::new(
Configure { Configure {
change: ChangeMergeCfg {
max_change_length: 0,
max_change_interval: 0,
},
..Default::default() ..Default::default()
}, },
Some(1), Some(1),
); );
let mut c2 = LoroCore::new( let mut c2 = LoroCore::new(
Configure { Configure {
change: ChangeMergeCfg {
max_change_length: 0,
max_change_interval: 0,
},
..Default::default() ..Default::default()
}, },
Some(2), Some(2),

View file

@ -9,11 +9,11 @@ use crate::{
dag::DagNode, dag::DagNode,
id::{Counter, ID}, id::{Counter, ID},
op::Op, op::Op,
span::{HasId, HasIdSpan, HasLamport}, span::{HasId, HasLamport},
version::Frontiers, version::Frontiers,
}; };
use num::traits::AsPrimitive; use num::traits::AsPrimitive;
use rle::{HasIndex, HasLength, Mergable, Rle, RleVec, Sliceable}; use rle::{HasIndex, HasLength, Mergable, RleVec, Sliceable};
pub type Timestamp = i64; pub type Timestamp = i64;
pub type Lamport = u32; pub type Lamport = u32;
@ -52,6 +52,14 @@ impl<O> Change<O> {
} }
} }
impl<O: Mergable + HasLength + HasIndex + Debug> HasIndex for Change<O> {
type Int = Counter;
fn get_start_index(&self) -> Self::Int {
self.id.counter
}
}
impl<O> HasId for Change<O> { impl<O> HasId for Change<O> {
fn id_start(&self) -> ID { fn id_start(&self) -> ID {
self.id self.id
@ -63,6 +71,23 @@ impl<O> HasLamport for Change<O> {
self.lamport self.lamport
} }
} }
impl<O> Mergable for Change<O> {
fn is_mergable(&self, _other: &Self, _conf: &()) -> bool
where
Self: Sized,
{
false
}
fn merge(&mut self, _other: &Self, _conf: &())
where
Self: Sized,
{
unreachable!()
}
}
use std::fmt::Debug; use std::fmt::Debug;
impl<O: Mergable + HasLength + HasIndex + Debug> HasLength for Change<O> { impl<O: Mergable + HasLength + HasIndex + Debug> HasLength for Change<O> {
fn content_len(&self) -> usize { fn content_len(&self) -> usize {
@ -70,54 +95,6 @@ impl<O: Mergable + HasLength + HasIndex + Debug> HasLength for Change<O> {
} }
} }
#[derive(Debug, Clone)]
pub struct ChangeMergeCfg {
pub max_change_length: usize,
pub max_change_interval: usize,
}
impl ChangeMergeCfg {
pub fn new() -> Self {
ChangeMergeCfg {
max_change_length: 1024,
max_change_interval: 60,
}
}
}
impl Default for ChangeMergeCfg {
fn default() -> Self {
Self {
max_change_length: 1024,
max_change_interval: 60,
}
}
}
impl<O: Rle + HasIndex> Mergable<ChangeMergeCfg> for Change<O> {
fn merge(&mut self, other: &Self, _: &ChangeMergeCfg) {
self.ops.merge(&other.ops, &());
}
fn is_mergable(&self, other: &Self, cfg: &ChangeMergeCfg) -> bool {
if other.deps.is_empty() || !(other.deps.len() == 1 && self.id_last() == other.deps[0]) {
return false;
}
if self.content_len() > cfg.max_change_length {
return false;
}
if other.timestamp - self.timestamp > cfg.max_change_interval as i64 {
return false;
}
self.id.peer == other.id.peer
&& self.id.counter + self.content_len() as Counter == other.id.counter
&& self.lamport + self.content_len() as Lamport == other.lamport
}
}
impl<O: Mergable + HasLength + Sliceable> Sliceable for Change<O> { impl<O: Mergable + HasLength + Sliceable> Sliceable for Change<O> {
// TODO: feels slow, need to confirm whether this affects performance // TODO: feels slow, need to confirm whether this affects performance
fn slice(&self, from: usize, to: usize) -> Self { fn slice(&self, from: usize, to: usize) -> Self {

View file

@ -1,11 +1,10 @@
use std::{fmt::Debug, sync::Arc}; use std::{fmt::Debug, sync::Arc};
use crate::{change::ChangeMergeCfg, log_store::GcConfig, Timestamp}; use crate::{log_store::GcConfig, Timestamp};
use ring::rand::{SecureRandom, SystemRandom}; use ring::rand::{SecureRandom, SystemRandom};
#[derive(Clone)] #[derive(Clone)]
pub struct Configure { pub struct Configure {
pub change: ChangeMergeCfg,
pub gc: GcConfig, pub gc: GcConfig,
pub get_time: fn() -> Timestamp, pub get_time: fn() -> Timestamp,
pub rand: Arc<dyn SecureRandomGenerator>, pub rand: Arc<dyn SecureRandomGenerator>,
@ -14,7 +13,6 @@ pub struct Configure {
impl Debug for Configure { impl Debug for Configure {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Configure") f.debug_struct("Configure")
.field("change", &self.change)
.field("gc", &self.gc) .field("gc", &self.gc)
.field("get_time", &self.get_time) .field("get_time", &self.get_time)
.finish() .finish()
@ -57,7 +55,6 @@ impl SecureRandomGenerator for SystemRandom {
impl Default for Configure { impl Default for Configure {
fn default() -> Self { fn default() -> Self {
Self { Self {
change: ChangeMergeCfg::default(),
gc: GcConfig::default(), gc: GcConfig::default(),
get_time: || 0, get_time: || 0,
rand: Arc::new(SystemRandom::new()), rand: Arc::new(SystemRandom::new()),

View file

@ -27,7 +27,6 @@ use crate::{
prelim::Prelim, prelim::Prelim,
transaction::Transaction, transaction::Transaction,
value::LoroValue, value::LoroValue,
version::PatchedVersionVector,
LoroError, Transact, VersionVector, LoroError, Transact, VersionVector,
}; };

View file

@ -1,5 +1,5 @@
use std::{ use std::{
ops::{Deref, DerefMut}, ops::Deref,
sync::{ sync::{
atomic::{AtomicU32, Ordering}, atomic::{AtomicU32, Ordering},
Arc, Mutex, Weak, Arc, Mutex, Weak,
@ -20,7 +20,6 @@ use crate::{
log_store::ImportContext, log_store::ImportContext,
op::{RemoteContent, RichOp}, op::{RemoteContent, RichOp},
transaction::Transaction, transaction::Transaction,
version::PatchedVersionVector,
LoroError, LoroValue, Transact, VersionVector, LoroError, LoroValue, Transact, VersionVector,
}; };
@ -346,6 +345,8 @@ impl ContainerRegistry {
#[cfg(feature = "test_utils")] #[cfg(feature = "test_utils")]
pub fn debug_inspect(&mut self) { pub fn debug_inspect(&mut self) {
use std::ops::DerefMut;
for (_, ContainerAndId { container, id: _ }) in self.containers.iter_mut() { for (_, ContainerAndId { container, id: _ }) in self.containers.iter_mut() {
if let ContainerInstance::Text(x) = container.try_lock().unwrap().deref_mut() { if let ContainerInstance::Text(x) = container.try_lock().unwrap().deref_mut() {
x.debug_inspect() x.debug_inspect()

View file

@ -5,11 +5,7 @@ use rle::{HasLength, Mergable, Sliceable};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use crate::{ use crate::{delta::DeltaValue, smstring::SmString, LoroValue};
delta::{DeltaItem, DeltaValue},
smstring::SmString,
LoroValue,
};
use super::string_pool::PoolString; use super::string_pool::PoolString;

View file

@ -365,7 +365,6 @@ impl<'a, T: DagNode + 'a, D: Dag<Node = T>> Iterator for DagCausalIter<'a, D> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::{ use crate::{
change::ChangeMergeCfg,
configure::Configure, configure::Configure,
dag::DagUtils, dag::DagUtils,
id::{Counter, ID}, id::{Counter, ID},
@ -441,20 +440,12 @@ mod test {
fn parallel_case() { fn parallel_case() {
let mut c1 = LoroCore::new( let mut c1 = LoroCore::new(
Configure { Configure {
change: ChangeMergeCfg {
max_change_length: 0,
max_change_interval: 0,
},
..Default::default() ..Default::default()
}, },
Some(1), Some(1),
); );
let mut c2 = LoroCore::new( let mut c2 = LoroCore::new(
Configure { Configure {
change: ChangeMergeCfg {
max_change_length: 0,
max_change_interval: 0,
},
..Default::default() ..Default::default()
}, },
Some(2), Some(2),

View file

@ -16,10 +16,10 @@ use std::{
use fxhash::FxHashMap; use fxhash::FxHashMap;
use rle::{HasLength, RleVec, RleVecWithIndex, Sliceable}; use rle::{HasLength, RleVec, Sliceable};
use crate::{ use crate::{
change::{Change, ChangeMergeCfg}, change::Change,
configure::Configure, configure::Configure,
container::{ container::{
registry::{ContainerIdx, ContainerInstance, ContainerRegistry}, registry::{ContainerIdx, ContainerInstance, ContainerRegistry},
@ -59,7 +59,7 @@ impl GcConfig {
} }
} }
pub(crate) type ClientChanges = FxHashMap<PeerID, RleVecWithIndex<Change, ChangeMergeCfg>>; pub(crate) type ClientChanges = FxHashMap<PeerID, RleVec<[Change; 0]>>;
pub(crate) type RemoteClientChanges = FxHashMap<PeerID, Vec<Change<RemoteOp>>>; pub(crate) type RemoteClientChanges = FxHashMap<PeerID, Vec<Change<RemoteOp>>>;
#[derive(Debug)] #[derive(Debug)]
@ -107,7 +107,7 @@ impl LogStore {
pub fn lookup_change(&self, id: ID) -> Option<&Change> { pub fn lookup_change(&self, id: ID) -> Option<&Change> {
self.changes.get(&id.peer).and_then(|changes| { self.changes.get(&id.peer).and_then(|changes| {
if id.counter <= changes.last().unwrap().id_last().counter { if id.counter <= changes.last().unwrap().id_last().counter {
Some(changes.get(id.counter as usize).unwrap().element) Some(changes.get_by_atom_index(id.counter).unwrap().element)
} else { } else {
None None
} }
@ -131,10 +131,7 @@ impl LogStore {
fn get_changes_slice(&self, id_span: IdSpan) -> Vec<Change> { fn get_changes_slice(&self, id_span: IdSpan) -> Vec<Change> {
if let Some(changes) = self.changes.get(&id_span.client_id) { if let Some(changes) = self.changes.get(&id_span.client_id) {
let mut ans = Vec::with_capacity(id_span.atom_len() / 30); let mut ans = Vec::with_capacity(id_span.atom_len() / 30);
for change in changes.slice_iter( for change in changes.slice_iter(id_span.counter.min(), id_span.counter.norm_end()) {
id_span.counter.min() as usize,
id_span.counter.norm_end() as usize,
) {
let change = change.value.slice(change.start, change.end); let change = change.value.slice(change.start, change.end);
ans.push(change); ans.push(change);
} }
@ -273,10 +270,9 @@ impl LogStore {
self.latest_lamport = lamport + change.content_len() as u32 - 1; self.latest_lamport = lamport + change.content_len() as u32 - 1;
self.latest_timestamp = timestamp; self.latest_timestamp = timestamp;
self.vv.set_end(change.id_end()); self.vv.set_end(change.id_end());
let cfg = self.get_change_merge_cfg();
self.changes self.changes
.entry(self.this_client_id) .entry(self.this_client_id)
.or_insert_with(|| RleVecWithIndex::new_with_conf(cfg)) .or_default()
.push(change); .push(change);
} }
@ -295,7 +291,7 @@ impl LogStore {
self.changes self.changes
.get(&id.peer) .get(&id.peer)
.map_or(0, |changes| changes.atom_len()) .map_or(0, |changes| changes.atom_len())
> id.counter as usize > id.counter
} }
#[inline] #[inline]
@ -325,21 +321,6 @@ impl LogStore {
&self.vv &self.vv
} }
fn get_change_merge_cfg(&self) -> ChangeMergeCfg {
ChangeMergeCfg {
max_change_length: self.cfg.change.max_change_length,
max_change_interval: self.cfg.change.max_change_interval,
}
}
pub(crate) fn max_change_length(&mut self, max_change_length: usize) {
self.cfg.change.max_change_length = max_change_length
}
pub(crate) fn max_change_interval(&mut self, max_change_interval: usize) {
self.cfg.change.max_change_interval = max_change_interval
}
pub(crate) fn gc(&mut self, gc: bool) { pub(crate) fn gc(&mut self, gc: bool) {
self.cfg.gc.gc = gc; self.cfg.gc.gc = gc;
} }
@ -355,12 +336,12 @@ impl LogStore {
self.changes.len(), self.changes.len(),
self.changes self.changes
.values() .values()
.map(|v| format!("{}", v.vec().len())) .map(|v| format!("{}", v.len()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "), .join(", "),
self.changes self.changes
.values() .values()
.map(|v| format!("{}", v.vec().iter().map(|x| x.ops.len()).sum::<usize>())) .map(|v| format!("{}", v.iter().map(|x| x.ops.len()).sum::<usize>()))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "), .join(", "),
self.changes self.changes
@ -415,7 +396,7 @@ impl Dag for LogStore {
fn get(&self, id: ID) -> Option<&Self::Node> { fn get(&self, id: ID) -> Option<&Self::Node> {
self.changes self.changes
.get(&id.peer) .get(&id.peer)
.and_then(|x| x.get(id.counter as usize).map(|x| x.element)) .and_then(|x| x.get_by_atom_index(id.counter).map(|x| x.element))
} }
fn frontier(&self) -> &[ID] { fn frontier(&self) -> &[ID] {

View file

@ -2,13 +2,12 @@ use std::collections::VecDeque;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use itertools::Itertools; use itertools::Itertools;
use rle::{HasLength, RleVec, RleVecWithIndex}; use rle::{HasLength, RleVec, RleVecWithLen};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_columnar::{columnar, from_bytes, to_vec}; use serde_columnar::{columnar, from_bytes, to_vec};
use smallvec::SmallVec;
use crate::{ use crate::{
change::{Change, ChangeMergeCfg}, change::Change,
container::text::text_content::SliceRange, container::text::text_content::SliceRange,
container::{ container::{
list::list_op::{DeleteSpan, InnerListOp}, list::list_op::{DeleteSpan, InnerListOp},
@ -21,7 +20,7 @@ use crate::{
event::EventDiff, event::EventDiff,
hierarchy::Hierarchy, hierarchy::Hierarchy,
id::{Counter, PeerID, ID}, id::{Counter, PeerID, ID},
log_store::{encoding::encode_changes::get_lamport_by_deps, ImportContext}, log_store::{encoding::encode_changes::get_lamport_by_deps, ClientChanges, ImportContext},
op::{InnerContent, Op}, op::{InnerContent, Op},
span::HasLamportSpan, span::HasLamportSpan,
version::{Frontiers, TotalOrderStamp}, version::{Frontiers, TotalOrderStamp},
@ -454,7 +453,7 @@ pub(super) fn decode_snapshot(
} }
// calculate lamport // calculate lamport
let mut lamport_map = FxHashMap::default(); let mut lamport_map = FxHashMap::default();
let mut changes = FxHashMap::default(); let mut changes: ClientChanges = FxHashMap::default();
let mut client_ids: VecDeque<_> = changes_dq.keys().copied().collect(); let mut client_ids: VecDeque<_> = changes_dq.keys().copied().collect();
let len = client_ids.len(); let len = client_ids.len();
let mut loop_time = len; let mut loop_time = len;
@ -468,10 +467,7 @@ pub(super) fn decode_snapshot(
change.id.counter..change.id.counter + change.content_len() as Counter, change.id.counter..change.id.counter + change.content_len() as Counter,
lamport, lamport,
)); ));
changes changes.entry(client_id).or_default().push(change);
.entry(client_id)
.or_insert_with(|| RleVecWithIndex::new_with_conf(ChangeMergeCfg::new()))
.push(change);
loop_time = len; loop_time = len;
} }
Err(_not_found_client) => { Err(_not_found_client) => {
@ -531,7 +527,7 @@ fn load_snapshot(
new_store: &mut LogStore, new_store: &mut LogStore,
new_hierarchy: &mut Hierarchy, new_hierarchy: &mut Hierarchy,
vv: VersionVector, vv: VersionVector,
changes: FxHashMap<PeerID, RleVecWithIndex<Change, ChangeMergeCfg>>, changes: ClientChanges,
containers: Vec<ContainerID>, containers: Vec<ContainerID>,
container_states: Vec<EncodedStateContent>, container_states: Vec<EncodedStateContent>,
keys: &[InternalString], keys: &[InternalString],

View file

@ -4,7 +4,6 @@ use crate::hierarchy::Hierarchy;
use crate::id::{Counter, PeerID, ID}; use crate::id::{Counter, PeerID, ID};
use crate::op::RemoteOp; use crate::op::RemoteOp;
use crate::span::{CounterSpan, HasCounter, HasCounterSpan}; use crate::span::{CounterSpan, HasCounter, HasCounterSpan};
use crate::version::PatchedVersionVector;
use crate::LogStore; use crate::LogStore;
use crate::{ use crate::{
container::registry::ContainerIdx, container::registry::ContainerIdx,
@ -13,13 +12,12 @@ use crate::{
}; };
use itertools::Itertools; use itertools::Itertools;
use smallvec::{smallvec, SmallVec}; use smallvec::{smallvec, SmallVec};
use std::sync::Arc;
use std::{collections::VecDeque, sync::MutexGuard}; use std::{collections::VecDeque, sync::MutexGuard};
use tracing::instrument; use tracing::instrument;
use fxhash::{FxHashMap, FxHashSet}; use fxhash::{FxHashMap, FxHashSet};
use rle::{slice_vec_by, HasLength, RleVecWithIndex, Sliceable}; use rle::{slice_vec_by, HasLength, Sliceable};
use crate::{ use crate::{
container::{registry::ContainerInstance, ContainerID, ContainerTrait}, container::{registry::ContainerInstance, ContainerID, ContainerTrait},
@ -173,7 +171,6 @@ impl LogStore {
next_vv.set_end(changes.last().unwrap().id_end()); next_vv.set_end(changes.last().unwrap().id_end());
} }
// push changes to log stores // push changes to log stores
let cfg = self.get_change_merge_cfg();
for (client_id, changes) in changes.iter() { for (client_id, changes) in changes.iter() {
let mut inner_changes = Vec::with_capacity(changes.len()); let mut inner_changes = Vec::with_capacity(changes.len());
for change in changes.iter() { for change in changes.iter() {
@ -182,10 +179,7 @@ impl LogStore {
inner_changes.push(change); inner_changes.push(change);
} }
let rle = self let rle = self.changes.entry(*client_id).or_default();
.changes
.entry(*client_id)
.or_insert_with(|| RleVecWithIndex::new_cfg(cfg.clone()));
for change in inner_changes { for change in inner_changes {
// if let Some(last) = rle.last() { // if let Some(last) = rle.last() {
// assert_eq!( // assert_eq!(

View file

@ -9,17 +9,15 @@ use crate::span::IdSpan;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use rle::HasLength; use rle::HasLength;
use rle::RleVec;
use crate::change::ChangeMergeCfg; use rle::RleVecWithLen;
use crate::change::Change; use crate::change::Change;
use rle::RleVecWithIndex;
pub struct ClientOpIter<'a> { pub struct ClientOpIter<'a> {
pub(crate) change_index: usize, pub(crate) change_index: usize,
pub(crate) op_index: usize, pub(crate) op_index: usize,
pub(crate) changes: Option<&'a RleVecWithIndex<Change, ChangeMergeCfg>>, pub(crate) changes: Option<&'a RleVec<[Change; 0]>>,
} }
impl<'a> Iterator for ClientOpIter<'a> { impl<'a> Iterator for ClientOpIter<'a> {
@ -50,14 +48,11 @@ pub struct OpSpanIter<'a> {
} }
impl<'a> OpSpanIter<'a> { impl<'a> OpSpanIter<'a> {
pub fn new( pub fn new(changes: &'a FxHashMap<PeerID, RleVec<[Change; 0]>>, target_span: IdSpan) -> Self {
changes: &'a FxHashMap<PeerID, RleVecWithIndex<Change, ChangeMergeCfg>>,
target_span: IdSpan,
) -> Self {
let rle_changes = changes.get(&target_span.client_id).unwrap(); let rle_changes = changes.get(&target_span.client_id).unwrap();
let changes = rle_changes.vec(); let changes = rle_changes.vec();
let change_index = rle_changes let change_index = rle_changes
.get(target_span.id_start().counter as usize) .get_by_atom_index(target_span.id_start().counter)
.map(|x| x.merged_index) .map(|x| x.merged_index)
.unwrap_or(changes.len()); .unwrap_or(changes.len());
@ -67,7 +62,7 @@ impl<'a> OpSpanIter<'a> {
change_index, change_index,
op_index: rle_changes[change_index] op_index: rle_changes[change_index]
.ops .ops
.get(target_span.counter.start) .get_by_atom_index(target_span.counter.start)
.unwrap() .unwrap()
.merged_index, .merged_index,
} }

View file

@ -200,19 +200,6 @@ impl LoroCore {
} }
// config // config
pub fn max_change_length(&mut self, max_change_length: usize) {
self.log_store
.write()
.unwrap()
.max_change_length(max_change_length);
}
pub fn max_change_interval(&mut self, max_change_interval: usize) {
self.log_store
.write()
.unwrap()
.max_change_interval(max_change_interval);
}
pub fn gc(&mut self, gc: bool) { pub fn gc(&mut self, gc: bool) {
self.log_store.write().unwrap().gc(gc) self.log_store.write().unwrap().gc(gc)

View file

@ -1,6 +1,4 @@
use im::Vector; use crate::container::ContainerID;
use crate::container::{ContainerID, ContainerIdx};
/// This is shared between [OpLog] and [AppState]. /// This is shared between [OpLog] and [AppState].
/// It uses a immutable data structure inside so that we have O(1) clone time. /// It uses a immutable data structure inside so that we have O(1) clone time.

View file

@ -1,6 +1,6 @@
use fxhash::FxHashMap; use fxhash::FxHashMap;
use crate::container::{ContainerID, ContainerIdx}; use crate::container::ContainerID;
/// Calculate the diff between two versions. given [OpLog][super::oplog::OpLog] /// Calculate the diff between two versions. given [OpLog][super::oplog::OpLog]
/// and [AppState][super::state::AppState]. /// and [AppState][super::state::AppState].

View file

@ -72,7 +72,7 @@ impl Dag for AppDag {
} = id; } = id;
self.map self.map
.get(&client_id) .get(&client_id)
.and_then(|rle| rle.get(counter).map(|x| x.element)) .and_then(|rle| rle.get_by_atom_index(counter).map(|x| x.element))
} }
fn vv(&self) -> VersionVector { fn vv(&self) -> VersionVector {
@ -85,7 +85,7 @@ impl AppDag {
/// It's the version when the op is applied /// It's the version when the op is applied
pub fn get_vv(&self, id: ID) -> Option<ImVersionVector> { pub fn get_vv(&self, id: ID) -> Option<ImVersionVector> {
self.map.get(&id.peer).and_then(|rle| { self.map.get(&id.peer).and_then(|rle| {
rle.get(id.counter).map(|x| { rle.get_by_atom_index(id.counter).map(|x| {
let mut vv = x.element.vv.clone(); let mut vv = x.element.vv.clone();
vv.insert(id.peer, id.counter); vv.insert(id.peer, id.counter);
vv vv
@ -107,7 +107,7 @@ impl AppDag {
pub fn get_lamport(&self, id: &ID) -> Option<Lamport> { pub fn get_lamport(&self, id: &ID) -> Option<Lamport> {
self.map.get(&id.peer).and_then(|rle| { self.map.get(&id.peer).and_then(|rle| {
rle.get(id.counter) rle.get_by_atom_index(id.counter)
.map(|x| x.element.lamport + (id.counter - x.element.cnt) as Lamport) .map(|x| x.element.lamport + (id.counter - x.element.cnt) as Lamport)
}) })
} }

View file

@ -1,12 +1,7 @@
use enum_dispatch::enum_dispatch; use enum_dispatch::enum_dispatch;
use fxhash::FxHashMap; use fxhash::FxHashMap;
use crate::{ use crate::{container::ContainerID, event::Diff, version::Frontiers, VersionVector};
container::{ContainerID, ContainerIdx},
event::Diff,
version::Frontiers,
VersionVector,
};
use super::arena::SharedArena; use super::arena::SharedArena;

View file

@ -112,7 +112,6 @@ impl Loro {
#[wasm_bindgen(constructor)] #[wasm_bindgen(constructor)]
pub fn new() -> Self { pub fn new() -> Self {
let cfg: Configure = Configure { let cfg: Configure = Configure {
change: Default::default(),
gc: GcConfig::default().with_gc(false), gc: GcConfig::default().with_gc(false),
get_time: || js_sys::Date::now() as i64, get_time: || js_sys::Date::now() as i64,
rand: Arc::new(MathRandom), rand: Arc::new(MathRandom),

View file

@ -485,7 +485,6 @@ impl<Index: GlobalIndex, T: PartialEq + Eq> Mergable for WithStartEnd<Index, T>
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use std::ops::Range;
use super::*; use super::*;
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]

View file

@ -96,7 +96,7 @@ where
&self, &self,
index: <A::Item as HasIndex>::Int, index: <A::Item as HasIndex>::Int,
) -> Option<SearchResult<'_, A::Item, <A::Item as HasIndex>::Int>> { ) -> Option<SearchResult<'_, A::Item, <A::Item as HasIndex>::Int>> {
self.vec.get(index) self.vec.get_by_atom_index(index)
} }
pub fn iter_by_index( pub fn iter_by_index(
@ -279,13 +279,13 @@ where
return SliceIterator::new_empty(); return SliceIterator::new_empty();
} }
let start = self.get(from); let start = self.get_by_atom_index(from);
if start.is_none() { if start.is_none() {
return SliceIterator::new_empty(); return SliceIterator::new_empty();
} }
let start = start.unwrap(); let start = start.unwrap();
let end = self.get(to); let end = self.get_by_atom_index(to);
if let Some(end) = end { if let Some(end) = end {
SliceIterator { SliceIterator {
vec: &self.vec, vec: &self.vec,
@ -317,7 +317,7 @@ where
/// get the element at the given atom index. /// get the element at the given atom index.
/// return: (element, merged_index, offset) /// return: (element, merged_index, offset)
pub fn get( pub fn get_by_atom_index(
&self, &self,
index: <A::Item as HasIndex>::Int, index: <A::Item as HasIndex>::Int,
) -> Option<SearchResult<'_, A::Item, <A::Item as HasIndex>::Int>> { ) -> Option<SearchResult<'_, A::Item, <A::Item as HasIndex>::Int>> {
@ -355,10 +355,56 @@ where
}) })
} }
pub fn slice_iter(
&self,
from: <A::Item as HasIndex>::Int,
to: <A::Item as HasIndex>::Int,
) -> SliceIterator<A::Item> {
if from == to || self.merged_len() == 0 {
return SliceIterator::new_empty();
}
let from_result = self.get_by_atom_index(from);
if from_result.is_none() {
return SliceIterator::new_empty();
}
let from_result = from_result.unwrap();
let to_result = if to == self.atom_len() {
None
} else {
self.get_by_atom_index(to)
};
if let Some(to_result) = to_result {
SliceIterator {
vec: &self.vec,
cur_index: from_result.merged_index,
cur_offset: from_result.offset.as_(),
end_index: Some(to_result.merged_index),
end_offset: Some(to_result.offset.as_()),
}
} else {
SliceIterator {
vec: &self.vec,
cur_index: from_result.merged_index,
cur_offset: from_result.offset.as_(),
end_index: None,
end_offset: None,
}
}
}
#[inline] #[inline]
pub fn slice_merged(&self, range: Range<usize>) -> &[A::Item] { pub fn slice_merged(&self, range: Range<usize>) -> &[A::Item] {
&self.vec[range] &self.vec[range]
} }
pub fn atom_len(&self) -> <A::Item as HasIndex>::Int {
self.vec
.last()
.map(|x| x.get_end_index())
.unwrap_or(<A::Item as HasIndex>::Int::from_usize(0).unwrap())
}
} }
impl<A: Array> RleVec<A> impl<A: Array> RleVec<A>
where where

View file

@ -345,7 +345,7 @@ impl<T, Cfg> Deref for RleVecWithIndex<T, Cfg> {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
mod prime_value { mod prime_value {
use crate::{Mergable, RleVecWithIndex, Sliceable}; use crate::{Mergable, RleVecWithIndex};
impl Mergable for String { impl Mergable for String {
fn is_mergable(&self, _: &Self, _: &()) -> bool { fn is_mergable(&self, _: &Self, _: &()) -> bool {