mirror of
https://github.com/loro-dev/loro.git
synced 2024-12-01 04:28:18 +00:00
fix: fix a few bugs
This commit is contained in:
parent
6473d9c11e
commit
61c27ca58b
8 changed files with 77 additions and 33 deletions
|
@ -1,4 +1,4 @@
|
|||
use rle::RleTree;
|
||||
use rle::{RleTree, Sliceable};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{
|
||||
|
@ -104,6 +104,7 @@ impl Container for TextContainer {
|
|||
}
|
||||
|
||||
// TODO: move main logic to tracker module
|
||||
// TODO: we don't need op proxy, only ids are enough
|
||||
fn apply(&mut self, op: &OpProxy, store: &LogStore) {
|
||||
let new_op_id = op.id_last();
|
||||
// TODO: may reduce following two into one op
|
||||
|
@ -111,7 +112,6 @@ impl Container for TextContainer {
|
|||
let path_to_store_head = store.find_path(&common, store.frontier());
|
||||
let mut common_vv = store.vv();
|
||||
common_vv.retreat(&path_to_store_head.right);
|
||||
|
||||
let mut latest_head: SmallVec<[ID; 2]> = store.frontier().into();
|
||||
latest_head.push(new_op_id);
|
||||
if common.is_empty() || !common.iter().all(|x| self.tracker.contains(*x)) {
|
||||
|
@ -121,8 +121,13 @@ impl Container for TextContainer {
|
|||
for iter in store.iter_partial(&common, path.right) {
|
||||
self.tracker.retreat(&iter.retreat);
|
||||
self.tracker.forward(&iter.forward);
|
||||
for op in iter.data.ops.iter() {
|
||||
// TODO: avoid this clone
|
||||
let change = iter
|
||||
.data
|
||||
.slice(iter.slice.start as usize, iter.slice.end as usize);
|
||||
for op in change.ops.iter() {
|
||||
if op.container == self.id {
|
||||
// TODO: convert op to local
|
||||
self.tracker.apply(op.id, &op.content)
|
||||
}
|
||||
}
|
||||
|
@ -133,7 +138,6 @@ impl Container for TextContainer {
|
|||
let path = store.find_path(&latest_head, store.frontier());
|
||||
self.tracker.retreat(&path.left);
|
||||
for effect in self.tracker.iter_effects(path.left) {
|
||||
dbg!(&effect);
|
||||
match effect {
|
||||
Effect::Del { pos, len } => self.state.delete_range(Some(pos), Some(pos + len)),
|
||||
Effect::Ins { pos, content } => {
|
||||
|
@ -156,6 +160,7 @@ impl Container for TextContainer {
|
|||
let content = v.as_ref();
|
||||
match content {
|
||||
ListSlice::Slice(range) => ans_str.push_str(&self.raw_str.get_str(range)),
|
||||
ListSlice::RawStr(raw) => ans_str.push_str(raw),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -160,10 +160,10 @@ impl Tracker {
|
|||
self.content.update_at_cursors_twice(
|
||||
&[&to_set_as_applied, &to_delete],
|
||||
&mut |v| {
|
||||
v.status.apply(StatusChange::SetAsFuture);
|
||||
v.status.apply(StatusChange::SetAsCurrent);
|
||||
},
|
||||
&mut |v: &mut YSpan| {
|
||||
v.status.apply(StatusChange::UndoDelete);
|
||||
v.status.apply(StatusChange::Delete);
|
||||
},
|
||||
&mut make_notify(&mut self.id_to_cursor),
|
||||
)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use std::{
|
||||
borrow::Cow,
|
||||
fmt::Debug,
|
||||
ops::{Deref, DerefMut},
|
||||
ptr::NonNull,
|
||||
|
@ -11,8 +12,12 @@ use rle::{
|
|||
rle_tree::{node::LeafNode, Position, SafeCursor, SafeCursorMut, UnsafeCursor},
|
||||
HasLength, Mergable, RleVec, Sliceable, ZeroElement,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::{id::ID, span::IdSpan};
|
||||
use crate::{
|
||||
id::ID,
|
||||
span::{HasId, IdSpan},
|
||||
};
|
||||
|
||||
use super::y_span::{YSpan, YSpanTreeTrait};
|
||||
|
||||
|
@ -62,6 +67,16 @@ impl Marker {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_first_span(
|
||||
&self,
|
||||
id_span: IdSpan,
|
||||
) -> Option<UnsafeCursor<'static, YSpan, YSpanTreeTrait>> {
|
||||
let mut ans = self.get_spans(id_span);
|
||||
// SAFETY: inner invariants ensures that the cursor is valid
|
||||
ans.sort_by_cached_key(|x| unsafe { x.as_ref() }.id.counter);
|
||||
ans.into_iter().next()
|
||||
}
|
||||
|
||||
pub fn get_spans(&self, id_span: IdSpan) -> Vec<UnsafeCursor<'static, YSpan, YSpanTreeTrait>> {
|
||||
match self {
|
||||
Marker::Insert { ptr, len: _ } => {
|
||||
|
@ -215,9 +230,10 @@ pub(super) struct IdSpanQueryResult<'a> {
|
|||
}
|
||||
|
||||
#[derive(EnumAsInner)]
|
||||
pub enum FirstCursorResult<'a> {
|
||||
pub enum FirstCursorResult {
|
||||
// TODO: REMOVE id field?
|
||||
Ins(ID, UnsafeCursor<'static, YSpan, YSpanTreeTrait>),
|
||||
Del(ID, &'a RleVec<IdSpan>),
|
||||
Del(ID, RleVec<IdSpan>),
|
||||
}
|
||||
|
||||
impl CursorMap {
|
||||
|
@ -249,16 +265,21 @@ impl CursorMap {
|
|||
}
|
||||
|
||||
pub fn get_first_cursors_at_id_span(&self, span: IdSpan) -> Option<FirstCursorResult> {
|
||||
// TODO: do we need this index
|
||||
for (id, marker) in self.get_range_with_index(span.min_id().into(), span.end_id().into()) {
|
||||
let id: ID = id.into();
|
||||
match marker {
|
||||
Marker::Insert { .. } => {
|
||||
let spans = marker.get_spans(span);
|
||||
if !spans.is_empty() {
|
||||
return Some(FirstCursorResult::Ins(id, spans[0]));
|
||||
if let Some(cursor) = marker.get_first_span(span) {
|
||||
return Some(FirstCursorResult::Ins(span.id_start(), cursor));
|
||||
}
|
||||
}
|
||||
Marker::Delete(del) => return Some(FirstCursorResult::Del(id, del)),
|
||||
Marker::Delete(del) => {
|
||||
return Some(FirstCursorResult::Del(
|
||||
span.id_start(),
|
||||
del.slice((span.id_start().counter - id.counter) as usize, del.len()),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -173,7 +173,9 @@ impl<'a, T: DagNode> Iterator for DagIteratorVV<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// visit every span in the target IdSpanVector
|
||||
/// Visit every span in the target IdSpanVector.
|
||||
/// It's guaranteed that the spans are visited in causal order, and each span is visited only once.
|
||||
/// When visiting a span, we will checkout to the version where the span was created
|
||||
pub(crate) struct DagPartialIter<'a, Dag> {
|
||||
dag: &'a Dag,
|
||||
frontier: SmallVec<[ID; 2]>,
|
||||
|
@ -248,7 +250,7 @@ impl<'a, T: DagNode + 'a, D: Dag<Node = T>> Iterator for DagPartialIter<'a, D> {
|
|||
} else {
|
||||
0
|
||||
};
|
||||
let slice_end = if counter.end > target_span.end {
|
||||
let slice_end = if counter.end < target_span.end {
|
||||
counter.end - counter.start
|
||||
} else {
|
||||
target_span.end - counter.start
|
||||
|
|
|
@ -635,7 +635,7 @@ mod find_path {
|
|||
|
||||
#[test]
|
||||
fn proptest_path_large(
|
||||
interactions in prop::collection::vec(gen_interaction(10), 0..5 * PROPTEST_FACTOR_10 * PROPTEST_FACTOR_10 + 10),
|
||||
interactions in prop::collection::vec(gen_interaction(10), 0..2 * PROPTEST_FACTOR_10 * PROPTEST_FACTOR_10 + 10),
|
||||
) {
|
||||
test_find_path(10, interactions)?;
|
||||
}
|
||||
|
|
|
@ -55,6 +55,7 @@ pub type LogStoreWeakRef = Weak<RwLock<LogStore>>;
|
|||
/// TODO: Refactor we need to move the things about the current state out of LogStore (container, latest_lamport, ..)
|
||||
pub struct LogStore {
|
||||
changes: FxHashMap<ClientID, RleVec<Change, ChangeMergeCfg>>,
|
||||
vv: VersionVector,
|
||||
cfg: Configure,
|
||||
latest_lamport: Lamport,
|
||||
latest_timestamp: Timestamp,
|
||||
|
@ -83,6 +84,7 @@ impl LogStore {
|
|||
frontier: Default::default(),
|
||||
container,
|
||||
to_self: x.clone(),
|
||||
vv: Default::default(),
|
||||
_pin: PhantomPinned,
|
||||
})
|
||||
})
|
||||
|
@ -102,6 +104,7 @@ impl LogStore {
|
|||
.into_iter()
|
||||
.filter(|x| !self_vv.includes_id(x.last_id()))
|
||||
{
|
||||
check_import_change_valid(&change);
|
||||
// TODO: cache pending changes
|
||||
assert!(change.deps.iter().all(|x| self_vv.includes_id(*x)));
|
||||
self.apply_remote_change(change)
|
||||
|
@ -116,6 +119,9 @@ impl LogStore {
|
|||
let mut changes = self.get_changes_slice(span.id_span());
|
||||
ans.append(&mut changes);
|
||||
}
|
||||
for change in ans.iter_mut() {
|
||||
self.change_to_export_format(change);
|
||||
}
|
||||
|
||||
ans
|
||||
}
|
||||
|
@ -137,7 +143,7 @@ impl LogStore {
|
|||
}
|
||||
}
|
||||
|
||||
fn change_to_export_format(&self, mut change: Change) {
|
||||
fn change_to_export_format(&self, change: &mut Change) {
|
||||
let container_manager = self.container.read().unwrap();
|
||||
for op in change.ops.vec_mut().iter_mut() {
|
||||
let container = container_manager.get(op.container.clone()).unwrap();
|
||||
|
@ -200,6 +206,7 @@ impl LogStore {
|
|||
));
|
||||
self.latest_lamport = lamport + change.len() as u32 - 1;
|
||||
self.latest_timestamp = timestamp;
|
||||
self.vv.set_end(change.id_end());
|
||||
self.changes
|
||||
.entry(self.this_client_id)
|
||||
.or_insert_with(RleVec::new)
|
||||
|
@ -218,7 +225,7 @@ impl LogStore {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: find a way to remove this clone?
|
||||
// TODO: find a way to remove this clone? we don't need change in apply method actually
|
||||
let change = self.push_change(change).clone();
|
||||
|
||||
// Apply ops.
|
||||
|
@ -243,7 +250,7 @@ impl LogStore {
|
|||
self.latest_timestamp = change.timestamp;
|
||||
}
|
||||
|
||||
todo!("update vv");
|
||||
self.vv.set_end(change.id_end());
|
||||
}
|
||||
|
||||
pub(crate) fn get_op_content(&self, id_span: IdSpan) -> Option<OpContent> {
|
||||
|
@ -316,15 +323,19 @@ impl Dag for LogStore {
|
|||
}
|
||||
|
||||
fn vv(&self) -> crate::VersionVector {
|
||||
self.changes
|
||||
.iter()
|
||||
.map(|(client, changes)| {
|
||||
changes
|
||||
.vec()
|
||||
.last()
|
||||
.map(|x| x.id_last())
|
||||
.unwrap_or_else(|| ID::new(*client, 0))
|
||||
})
|
||||
.collect()
|
||||
self.vv.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn check_import_change_valid(change: &Change) {
|
||||
for op in change.ops.iter() {
|
||||
if let Some((slice, _)) = op
|
||||
.content
|
||||
.as_normal()
|
||||
.and_then(|x| x.as_list())
|
||||
.and_then(|x| x.as_insert())
|
||||
{
|
||||
assert!(slice.as_raw_str().is_some())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ use ctor::ctor;
|
|||
use loro_core::container::Container;
|
||||
use loro_core::LoroCore;
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
fn test() {
|
||||
let mut store = LoroCore::new(Default::default(), Some(10));
|
||||
|
@ -14,7 +13,7 @@ fn test() {
|
|||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "avvxxbc");
|
||||
drop(text_container);
|
||||
let mut store_b = LoroCore::new(Default::default(), Some(10));
|
||||
let mut store_b = LoroCore::new(Default::default(), Some(11));
|
||||
store_b.import(store.export(Default::default()));
|
||||
let mut text_container = store_b.get_text_container("haha".into());
|
||||
let value = text_container.get_value();
|
||||
|
|
|
@ -180,8 +180,14 @@ impl<'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, T, A> {
|
|||
///
|
||||
/// we need to make sure that the leaf is still valid
|
||||
pub unsafe fn get_index(&self) -> A::Int {
|
||||
let index = A::get_index(self.leaf.as_ref(), self.index);
|
||||
index + A::Int::from_usize(self.offset).unwrap()
|
||||
let leaf = self.leaf.as_ref();
|
||||
let index = A::get_index(leaf, self.index);
|
||||
let item = self.as_ref();
|
||||
if item.len() == 0 {
|
||||
index
|
||||
} else {
|
||||
index + A::Int::from_usize(self.offset).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// move cursor forward
|
||||
|
|
Loading…
Reference in a new issue