mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-05 20:17:13 +00:00
fix: origin left should points to non-deleted
This commit is contained in:
parent
71aca40c7b
commit
7c032b6321
8 changed files with 279 additions and 466 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
/target
|
||||
*.log
|
||||
flamegraph.svg
|
||||
|
|
|
@ -18,7 +18,7 @@ crev:
|
|||
cargo crev crate check
|
||||
|
||||
fuzz:
|
||||
cargo fuzz run yata -- -max_total_time=60 -max_len=10000 -jobs=2
|
||||
cargo fuzz run yata -- -max_total_time=300 -max_len=8000 -jobs=2
|
||||
|
||||
flame:
|
||||
cargo flamegraph --example test --features=fuzzing --root
|
||||
|
|
|
@ -23,7 +23,7 @@ struct CursorWithId<'tree> {
|
|||
|
||||
impl ContentMap {
|
||||
#[inline]
|
||||
pub(super) fn get_yspan_at_pos(&mut self, id: ID, pos: usize, len: usize) -> YSpan {
|
||||
pub(super) fn get_yspan_at_pos(&self, id: ID, pos: usize, len: usize) -> YSpan {
|
||||
let (left, right) = self.get_sibling_at_dumb(pos);
|
||||
YSpan {
|
||||
origin_left: left.as_ref().map(|x| x.id),
|
||||
|
@ -39,48 +39,44 @@ impl ContentMap {
|
|||
pos: usize,
|
||||
) -> (Option<CursorWithId<'_>>, Option<CursorWithId<'_>>) {
|
||||
if let Some(cursor) = self.get(pos) {
|
||||
let cursor: SafeCursor<'_, YSpan, YSpanTreeTrait> =
|
||||
let mut cursor: SafeCursor<'_, YSpan, YSpanTreeTrait> =
|
||||
// SAFETY: we only change the lifetime of the cursor; the returned lifetime is kinda wrong in this situation
|
||||
// because Bumpalo's lifetime is static due to the self-referential structure limitation; Maybe there is a better way?
|
||||
unsafe { std::mem::transmute(cursor) };
|
||||
let (mut prev, mut next) = match cursor.pos() {
|
||||
Position::Start => {
|
||||
let id = cursor.as_ref().id;
|
||||
(
|
||||
None,
|
||||
Some(CursorWithId {
|
||||
id,
|
||||
cursor: cursor.unwrap(),
|
||||
}),
|
||||
)
|
||||
}
|
||||
let mut prev = match cursor.pos() {
|
||||
Position::Start => None,
|
||||
Position::Middle => {
|
||||
let id = cursor.as_ref().id;
|
||||
let offset = cursor.offset();
|
||||
let mut prev_offset_cursor = cursor.unwrap();
|
||||
prev_offset_cursor.offset -= 1;
|
||||
(
|
||||
Some(CursorWithId {
|
||||
id: id.inc(offset as i32 - 1),
|
||||
cursor: prev_offset_cursor,
|
||||
}),
|
||||
Some(CursorWithId {
|
||||
id: id.inc(offset as i32),
|
||||
cursor: cursor.unwrap(),
|
||||
}),
|
||||
)
|
||||
if cursor.as_ref().can_be_origin() {
|
||||
return (
|
||||
Some(CursorWithId {
|
||||
id: id.inc(offset as i32 - 1),
|
||||
cursor: prev_offset_cursor,
|
||||
}),
|
||||
Some(CursorWithId {
|
||||
id: id.inc(offset as i32),
|
||||
cursor: cursor.unwrap(),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Position::End => {
|
||||
let mut prev_offset_cursor = cursor.unwrap();
|
||||
prev_offset_cursor.offset -= 1;
|
||||
prev_offset_cursor.pos = Position::Middle;
|
||||
(
|
||||
if cursor.as_ref().can_be_origin() {
|
||||
let mut prev_offset_cursor = cursor.unwrap();
|
||||
prev_offset_cursor.offset -= 1;
|
||||
prev_offset_cursor.pos = Position::Middle;
|
||||
Some(CursorWithId {
|
||||
id: cursor.as_ref().last_id(),
|
||||
cursor: prev_offset_cursor,
|
||||
}),
|
||||
None,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
unreachable!()
|
||||
|
@ -89,31 +85,50 @@ impl ContentMap {
|
|||
|
||||
if prev.is_none() {
|
||||
let mut prev_cursor = cursor.prev_elem();
|
||||
if let Some(prev_inner) = prev_cursor {
|
||||
let cursor = prev_inner;
|
||||
let offset = cursor.as_ref().content_len() - 1;
|
||||
let mut cursor = cursor.unwrap();
|
||||
cursor.offset = offset;
|
||||
cursor.pos = Position::Middle;
|
||||
prev = Some(CursorWithId {
|
||||
id: prev_inner.as_ref().last_id(),
|
||||
cursor,
|
||||
});
|
||||
while let Some(prev_inner) = prev_cursor {
|
||||
cursor = prev_inner;
|
||||
if prev_inner.as_ref().status.is_activated() {
|
||||
let cursor = prev_inner;
|
||||
let offset = cursor.as_ref().content_len() - 1;
|
||||
let mut cursor = cursor.unwrap();
|
||||
cursor.offset = offset;
|
||||
cursor.pos = Position::Middle;
|
||||
prev = Some(CursorWithId {
|
||||
id: prev_inner.as_ref().last_id(),
|
||||
cursor,
|
||||
});
|
||||
break;
|
||||
}
|
||||
prev_cursor = prev_inner.prev_elem();
|
||||
}
|
||||
}
|
||||
|
||||
if next.is_none() {
|
||||
let mut next_cursor = cursor.next_elem_start();
|
||||
let next = if prev.is_some() {
|
||||
let next_cursor = cursor.next_elem_start();
|
||||
if let Some(next_inner) = next_cursor {
|
||||
let mut cursor = next_inner.unwrap();
|
||||
cursor.offset = 0;
|
||||
cursor.pos = Position::Start;
|
||||
next = Some(CursorWithId {
|
||||
Some(CursorWithId {
|
||||
id: next_inner.as_ref().id,
|
||||
cursor,
|
||||
});
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if prev is none, next should be the first element in the tree
|
||||
let mut prev = cursor.prev_elem();
|
||||
while let Some(prev_inner) = prev {
|
||||
cursor = prev_inner;
|
||||
prev = prev_inner.prev_elem();
|
||||
}
|
||||
|
||||
Some(CursorWithId {
|
||||
id: cursor.as_ref().id,
|
||||
cursor: cursor.unwrap(),
|
||||
})
|
||||
};
|
||||
|
||||
(prev, next)
|
||||
} else {
|
||||
|
@ -265,3 +280,98 @@ pub(super) fn change_status(
|
|||
cursor.update_cache_recursively();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_get_yspan_at_pos {
|
||||
use crate::{
|
||||
container::text::tracker::y_span::{Status, YSpan},
|
||||
id::ID,
|
||||
};
|
||||
|
||||
use super::ContentMap;
|
||||
|
||||
fn insert(map: &mut ContentMap, id: ID, pos: usize, len: usize) {
|
||||
map.insert(
|
||||
pos,
|
||||
YSpan {
|
||||
id,
|
||||
len,
|
||||
status: Default::default(),
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn delete(map: &mut ContentMap, pos: usize, len: usize) {
|
||||
map.0.update_range(
|
||||
pos,
|
||||
Some(pos + len),
|
||||
&mut |v| v.status.delete_times = 1,
|
||||
&mut |_, _| {},
|
||||
)
|
||||
}
|
||||
|
||||
fn insert_deleted(map: &mut ContentMap, id: ID, pos: usize, len: usize) {
|
||||
map.insert(
|
||||
pos,
|
||||
YSpan {
|
||||
id,
|
||||
len,
|
||||
status: Status {
|
||||
delete_times: 1,
|
||||
unapplied: false,
|
||||
undo_times: 0,
|
||||
},
|
||||
origin_left: None,
|
||||
origin_right: None,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn assert_at_pos(
|
||||
map: &ContentMap,
|
||||
pos: usize,
|
||||
origin_left: Option<ID>,
|
||||
origin_right: Option<ID>,
|
||||
) {
|
||||
let ans = map.get_yspan_at_pos(ID::new(111, 11), pos, 1);
|
||||
assert_eq!(ans.origin_left, origin_left);
|
||||
assert_eq!(ans.origin_right, origin_right);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn simple() {
|
||||
let mut map = ContentMap::default();
|
||||
insert(&mut map, ID::new(0, 0), 0, 10);
|
||||
assert_at_pos(&map, 0, None, Some(ID::new(0, 0)));
|
||||
assert_at_pos(&map, 10, Some(ID::new(0, 9)), None);
|
||||
assert_at_pos(&map, 3, Some(ID::new(0, 2)), Some(ID::new(0, 3)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn complicated() {
|
||||
let mut map = ContentMap::default();
|
||||
insert(&mut map, ID::new(0, 0), 0, 20);
|
||||
delete(&mut map, 10, 10);
|
||||
insert(&mut map, ID::new(1, 0), 10, 10);
|
||||
insert(&mut map, ID::new(2, 0), 20, 10);
|
||||
insert(&mut map, ID::new(3, 0), 30, 10);
|
||||
|
||||
// dbg!(&map);
|
||||
assert_at_pos(&map, 10, Some(ID::new(0, 9)), Some(ID::new(0, 10)));
|
||||
assert_at_pos(&map, 11, Some(ID::new(1, 0)), Some(ID::new(1, 1)));
|
||||
|
||||
assert_at_pos(&map, 20, Some(ID::new(1, 9)), Some(ID::new(2, 0)));
|
||||
assert_at_pos(&map, 21, Some(ID::new(2, 0)), Some(ID::new(2, 1)));
|
||||
delete(&mut map, 20, 1);
|
||||
assert_at_pos(&map, 20, Some(ID::new(1, 9)), Some(ID::new(2, 0)));
|
||||
assert_at_pos(&map, 21, Some(ID::new(2, 1)), Some(ID::new(2, 2)));
|
||||
|
||||
delete(&mut map, 0, 10);
|
||||
assert_at_pos(&map, 0, None, Some(ID::new(0, 0)));
|
||||
assert_at_pos(&map, 29, Some(ID::new(3, 9)), None);
|
||||
delete(&mut map, 0, 28);
|
||||
assert_at_pos(&map, 1, Some(ID::new(3, 9)), None);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ impl Marker {
|
|||
Marker::Insert { ptr, len: _ } => {
|
||||
// SAFETY: tree data is always valid
|
||||
let node = unsafe { ptr.as_ref() };
|
||||
debug_assert!(!node.is_deleted());
|
||||
let position = node.children().iter().position(|x| x.contain_id(id))?;
|
||||
let child = &node.children()[position];
|
||||
let start_counter = child.id.counter;
|
||||
|
@ -64,7 +63,6 @@ impl Marker {
|
|||
Marker::Insert { ptr, len: _ } => {
|
||||
// SAFETY: tree data is always valid
|
||||
let node = unsafe { ptr.as_ref() };
|
||||
debug_assert!(!node.is_deleted());
|
||||
node.children()
|
||||
.iter()
|
||||
.enumerate()
|
||||
|
@ -108,7 +106,6 @@ impl Marker {
|
|||
match self {
|
||||
Marker::Insert { ptr, len: _ } => {
|
||||
let node = ptr.as_ref();
|
||||
debug_assert!(!node.is_deleted());
|
||||
let position = node.children().iter().position(|x| x.contain_id(id))?;
|
||||
let child = &node.children()[position];
|
||||
let start_counter = child.id.counter;
|
||||
|
|
|
@ -5,9 +5,9 @@ use rle::{rle_tree::tree_trait::CumulateTreeTrait, HasLength, Mergable, Sliceabl
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default, Hash)]
|
||||
pub struct Status {
|
||||
unapplied: bool,
|
||||
delete_times: usize,
|
||||
undo_times: usize,
|
||||
pub unapplied: bool,
|
||||
pub delete_times: usize,
|
||||
pub undo_times: usize,
|
||||
}
|
||||
|
||||
impl Status {
|
||||
|
@ -61,7 +61,7 @@ pub enum StatusChange {
|
|||
UndoDelete,
|
||||
}
|
||||
|
||||
pub(super) type YSpanTreeTrait = CumulateTreeTrait<YSpan, 10>;
|
||||
pub(super) type YSpanTreeTrait = CumulateTreeTrait<YSpan, 4>;
|
||||
|
||||
impl YSpan {
|
||||
/// this is the last id of the span, which is **included** by self
|
||||
|
|
|
@ -303,429 +303,72 @@ pub mod fuzz {
|
|||
#[test]
|
||||
fn issue_1() {
|
||||
crdt_list::test::test_with_actions::<YataImpl>(
|
||||
3,
|
||||
5,
|
||||
100,
|
||||
vec![
|
||||
NewOp {
|
||||
client_id: 2,
|
||||
pos: 2,
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 2,
|
||||
client_id: 39,
|
||||
pos: 252,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
len: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 2,
|
||||
client_id: 145,
|
||||
pos: 145,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 4,
|
||||
},
|
||||
Sync { from: 0, to: 1 },
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 2,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 4,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 1,
|
||||
},
|
||||
Sync { from: 0, to: 0 },
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
client_id: 145,
|
||||
pos: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
len: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 1,
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 252,
|
||||
},
|
||||
Sync { from: 0, to: 0 },
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 3,
|
||||
len: 2,
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 1,
|
||||
len: 1,
|
||||
},
|
||||
Sync { from: 0, to: 0 },
|
||||
Sync { from: 0, to: 0 },
|
||||
Sync { from: 0, to: 0 },
|
||||
Sync { from: 0, to: 0 },
|
||||
Sync { from: 0, to: 0 },
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
len: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 3,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 1,
|
||||
len: 2,
|
||||
},
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
len: 2,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 1,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
},
|
||||
Sync { from: 0, to: 1 },
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 4,
|
||||
len: 4,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
len: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 2,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 4,
|
||||
},
|
||||
Sync { from: 0, to: 1 },
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 2,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 4,
|
||||
len: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 2,
|
||||
pos: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 4,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 2,
|
||||
len: 0,
|
||||
},
|
||||
Sync { from: 2, to: 2 },
|
||||
Delete {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
len: 3,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 4,
|
||||
len: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Sync { from: 0, to: 0 },
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 3,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 0,
|
||||
len: 1,
|
||||
},
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 4,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 1,
|
||||
pos: 0,
|
||||
len: 0,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
len: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 2,
|
||||
pos: 1,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 1,
|
||||
pos: 4,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 1,
|
||||
len: 4,
|
||||
},
|
||||
Delete {
|
||||
client_id: 0,
|
||||
pos: 4,
|
||||
len: 3,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 2,
|
||||
pos: 0,
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
},
|
||||
],
|
||||
)
|
||||
|
@ -733,9 +376,74 @@ pub mod fuzz {
|
|||
|
||||
#[test]
|
||||
fn normalize() {
|
||||
let mut actions = vec![];
|
||||
let mut actions = vec![
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 39,
|
||||
pos: 252,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 145,
|
||||
pos: 145,
|
||||
len: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 145,
|
||||
pos: 252,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 252,
|
||||
},
|
||||
Sync { from: 0, to: 0 },
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 178,
|
||||
len: 178,
|
||||
},
|
||||
Delete {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
len: 252,
|
||||
},
|
||||
NewOp {
|
||||
client_id: 252,
|
||||
pos: 252,
|
||||
},
|
||||
];
|
||||
|
||||
crdt_list::test::normalize_actions(&mut actions, 3, 5);
|
||||
crdt_list::test::normalize_actions(&mut actions, 5, 100);
|
||||
dbg!(actions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,10 +204,6 @@ impl<'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, T, A> {
|
|||
|
||||
while shift > 0 {
|
||||
let diff = leaf.children[self.index].content_len() - self.offset;
|
||||
#[cfg(test)]
|
||||
{
|
||||
leaf.check();
|
||||
}
|
||||
match shift.cmp(&diff) {
|
||||
std::cmp::Ordering::Less => {
|
||||
self.offset += shift;
|
||||
|
|
|
@ -140,13 +140,13 @@ impl<T: Rle, const MAX_CHILD: usize> RleTreeTrait<T> for CumulateTreeTrait<T, MA
|
|||
last_cache = match child {
|
||||
Node::Internal(x) => {
|
||||
if index <= x.cache {
|
||||
return FindPosResult::new(i, index, get_pos(index, child.len()));
|
||||
return FindPosResult::new(i, index, Position::get_pos(index, child.len()));
|
||||
}
|
||||
x.cache
|
||||
}
|
||||
Node::Leaf(x) => {
|
||||
if index <= x.cache {
|
||||
return FindPosResult::new(i, index, get_pos(index, child.len()));
|
||||
return FindPosResult::new(i, index, Position::get_pos(index, child.len()));
|
||||
}
|
||||
x.cache
|
||||
}
|
||||
|
@ -169,7 +169,7 @@ impl<T: Rle, const MAX_CHILD: usize> RleTreeTrait<T> for CumulateTreeTrait<T, MA
|
|||
|
||||
for (i, child) in node.children().iter().enumerate() {
|
||||
if index < HasLength::len(&**child) {
|
||||
return FindPosResult::new(i, index, get_pos(index, child.len()));
|
||||
return FindPosResult::new(i, index, Position::get_pos(index, child.len()));
|
||||
}
|
||||
|
||||
index -= HasLength::len(&**child);
|
||||
|
@ -199,14 +199,15 @@ impl<T: Rle, const MAX_CHILD: usize> RleTreeTrait<T> for CumulateTreeTrait<T, MA
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn get_pos(index: usize, len: usize) -> Position {
|
||||
if index == 0 {
|
||||
Position::Start
|
||||
} else if index == len {
|
||||
Position::End
|
||||
} else {
|
||||
Position::Middle
|
||||
impl Position {
|
||||
pub fn get_pos(index: usize, len: usize) -> Position {
|
||||
if index == 0 {
|
||||
Position::Start
|
||||
} else if index == len {
|
||||
Position::End
|
||||
} else {
|
||||
Position::Middle
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue