mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 21:07:43 +00:00
fix: two sites sync issue
This commit is contained in:
parent
788808b055
commit
69521a97ef
6 changed files with 93 additions and 40 deletions
|
@ -66,9 +66,10 @@ impl TextContainer {
|
|||
},
|
||||
self.id.clone(),
|
||||
);
|
||||
let last_id = op.id_last();
|
||||
store.append_local_ops(vec![op]);
|
||||
self.head = smallvec![id];
|
||||
self.vv.set_last(id);
|
||||
self.head = smallvec![last_id];
|
||||
self.vv.set_last(last_id);
|
||||
id
|
||||
} else {
|
||||
unimplemented!()
|
||||
|
@ -88,8 +89,11 @@ impl TextContainer {
|
|||
self.id.clone(),
|
||||
);
|
||||
|
||||
let last_id = op.id_last();
|
||||
store.append_local_ops(vec![op]);
|
||||
self.state.delete_range(Some(pos), Some(pos + len));
|
||||
self.head = smallvec![last_id];
|
||||
self.vv.set_last(last_id);
|
||||
id
|
||||
} else {
|
||||
unimplemented!()
|
||||
|
@ -121,20 +125,23 @@ impl Container for TextContainer {
|
|||
fn apply(&mut self, op: &OpProxy, store: &LogStore) {
|
||||
let new_op_id = op.id_last();
|
||||
// TODO: may reduce following two into one op
|
||||
let common = store.find_common_ancestor(&[new_op_id], &self.head);
|
||||
let path_to_store_head = store.find_path(&common, &self.head);
|
||||
let mut common_vv = self.vv.clone();
|
||||
common_vv.retreat(&path_to_store_head.right);
|
||||
let common_ancestors = store.find_common_ancestor(&[new_op_id], &self.head);
|
||||
let path_to_head = store.find_path(&common_ancestors, &self.head);
|
||||
let mut ancestors_vv = self.vv.clone();
|
||||
ancestors_vv.retreat(&path_to_head.right);
|
||||
let mut latest_head: SmallVec<[ID; 2]> = self.head.clone();
|
||||
latest_head.push(new_op_id);
|
||||
if common.is_empty() || !common.iter().all(|x| self.tracker.contains(*x)) {
|
||||
self.tracker = Tracker::new(common_vv);
|
||||
if common_ancestors.is_empty()
|
||||
|| !common_ancestors.iter().all(|x| self.tracker.contains(*x))
|
||||
{
|
||||
self.tracker = Tracker::new(ancestors_vv);
|
||||
} else {
|
||||
self.tracker.checkout(&self.vv);
|
||||
}
|
||||
|
||||
// stage 1
|
||||
self.tracker.checkout(&self.vv);
|
||||
let path = store.find_path(&common, &latest_head);
|
||||
for iter in store.iter_partial(&common, path.right) {
|
||||
let path = store.find_path(&common_ancestors, &latest_head);
|
||||
for iter in store.iter_partial(&common_ancestors, path.right) {
|
||||
self.tracker.retreat(&iter.retreat);
|
||||
self.tracker.forward(&iter.forward);
|
||||
// TODO: avoid this clone
|
||||
|
@ -152,7 +159,12 @@ impl Container for TextContainer {
|
|||
// stage 2
|
||||
let path = store.find_path(&latest_head, &self.head);
|
||||
self.tracker.retreat(&path.left);
|
||||
// println!(
|
||||
// "Iterate path: {:?} from {:?} => {:?}",
|
||||
// path.left, &self.head, &latest_head
|
||||
// );
|
||||
for effect in self.tracker.iter_effects(path.left) {
|
||||
// println!("{:?}", &effect);
|
||||
match effect {
|
||||
Effect::Del { pos, len } => self.state.delete_range(Some(pos), Some(pos + len)),
|
||||
Effect::Ins { pos, content } => {
|
||||
|
@ -163,6 +175,7 @@ impl Container for TextContainer {
|
|||
|
||||
self.head.push(new_op_id);
|
||||
self.vv.set_last(new_op_id);
|
||||
// println!("------------------------------------------------------------------------");
|
||||
}
|
||||
|
||||
fn checkout_version(&mut self, _vv: &crate::VersionVector) {
|
||||
|
|
|
@ -77,11 +77,11 @@ impl Tracker {
|
|||
Tracker {
|
||||
content,
|
||||
id_to_cursor,
|
||||
start_vv,
|
||||
#[cfg(feature = "fuzzing")]
|
||||
client_id: 0,
|
||||
head_vv: Default::default(),
|
||||
all_vv: Default::default(),
|
||||
head_vv: start_vv.clone(),
|
||||
all_vv: start_vv.clone(),
|
||||
start_vv,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -45,33 +45,37 @@ impl<'a> Iterator for EffectIter<'a> {
|
|||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(ref mut delete_targets) = self.current_delete_targets {
|
||||
let target = delete_targets.pop().unwrap();
|
||||
let result = self
|
||||
.tracker
|
||||
.id_to_cursor
|
||||
.get_first_cursors_at_id_span(target)
|
||||
.unwrap();
|
||||
let (id, cursor) = result.as_ins().unwrap();
|
||||
assert_eq!(*id, target.id_start());
|
||||
if cursor.len != target.len() {
|
||||
delete_targets.push(IdSpan {
|
||||
client_id: target.client_id,
|
||||
counter: CounterSpan::new(
|
||||
id.counter + cursor.len as Counter,
|
||||
target.counter.end,
|
||||
),
|
||||
if let Some(target) = delete_targets.pop() {
|
||||
let result = self
|
||||
.tracker
|
||||
.id_to_cursor
|
||||
.get_first_cursors_at_id_span(target)
|
||||
.unwrap();
|
||||
let (id, cursor) = result.as_ins().unwrap();
|
||||
assert_eq!(*id, target.id_start());
|
||||
if cursor.len != target.len() {
|
||||
let new_target = IdSpan {
|
||||
client_id: target.client_id,
|
||||
counter: CounterSpan::new(
|
||||
id.counter + cursor.len as Counter,
|
||||
target.counter.end,
|
||||
),
|
||||
};
|
||||
if new_target.len() > 0 {
|
||||
delete_targets.push(new_target);
|
||||
}
|
||||
}
|
||||
|
||||
// SAFETY: we know that the cursor is valid here
|
||||
let pos = unsafe { cursor.get_index() };
|
||||
let changed_len = self
|
||||
.tracker
|
||||
.update_cursors(smallvec![*cursor], StatusChange::Delete);
|
||||
return Some(Effect::Del {
|
||||
pos,
|
||||
len: (-changed_len) as usize,
|
||||
});
|
||||
}
|
||||
|
||||
// SAFETY: we know that the cursor is valid here
|
||||
let pos = unsafe { cursor.get_index() };
|
||||
let changed_len = self
|
||||
.tracker
|
||||
.update_cursors(smallvec![*cursor], StatusChange::Delete);
|
||||
return Some(Effect::Del {
|
||||
pos,
|
||||
len: (-changed_len) as usize,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(ref mut current) = self.current_span {
|
||||
|
|
|
@ -177,7 +177,7 @@ impl LogStore {
|
|||
self.this_client_id
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[inline(always)]
|
||||
pub fn frontier(&self) -> &[ID] {
|
||||
&self.frontier
|
||||
}
|
||||
|
@ -299,6 +299,11 @@ impl LogStore {
|
|||
pub(crate) fn iter_op(&self) -> iter::OpIter<'_> {
|
||||
iter::OpIter::new(&self.changes)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn get_vv(&self) -> &VersionVector {
|
||||
&self.vv
|
||||
}
|
||||
}
|
||||
|
||||
impl Dag for LogStore {
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::{
|
|||
text::text_container::TextContainer,
|
||||
ContainerID, ContainerType,
|
||||
},
|
||||
dag::DagUtils,
|
||||
id::ClientID,
|
||||
InternalString, LogStore, VersionVector,
|
||||
};
|
||||
|
@ -41,6 +42,10 @@ impl LoroCore {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn vv(&self) -> VersionVector {
|
||||
self.log_store.read().unwrap().get_vv().clone()
|
||||
}
|
||||
|
||||
pub fn get_container(
|
||||
&mut self,
|
||||
name: InternalString,
|
||||
|
|
|
@ -22,6 +22,32 @@ fn test() {
|
|||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "0563412");
|
||||
|
||||
text_container.delete(0, 2);
|
||||
text_container.insert(4, "789");
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "63417892");
|
||||
drop(text_container);
|
||||
|
||||
store.import(store_b.export(store.vv()));
|
||||
let mut text_container = store.get_text_container("haha".into());
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "63417892");
|
||||
text_container.delete(0, 8);
|
||||
text_container.insert(0, "abc");
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "abc");
|
||||
drop(text_container);
|
||||
|
||||
store_b.import(store.export(Default::default()));
|
||||
let mut text_container = store_b.get_text_container("haha".into());
|
||||
text_container.check();
|
||||
let value = text_container.get_value();
|
||||
let value = value.as_string().unwrap();
|
||||
assert_eq!(value.as_str(), "abc");
|
||||
}
|
||||
|
||||
#[ctor]
|
||||
|
|
Loading…
Reference in a new issue