mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 12:57:20 +00:00
fix: string fuzzy
This commit is contained in:
parent
0d99ceb01c
commit
1df1f1d2bf
8 changed files with 177 additions and 85 deletions
|
@ -1,6 +1,6 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use rle::{rle_tree::SafeCursorMut, Rle, RleTree, RleTreeTrait};
|
||||
use rle::{rle_tree::SafeCursorMut, RleTree};
|
||||
|
||||
use super::y_span::{StatusChange, YSpan, YSpanTreeTrait};
|
||||
|
||||
|
@ -22,8 +22,8 @@ impl DerefMut for ContentMap {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn change_status(
|
||||
cursor: &mut SafeCursorMut<'_, '_, YSpan, YSpanTreeTrait>,
|
||||
pub(super) fn change_status<'a, 'b: 'a>(
|
||||
cursor: &mut SafeCursorMut<'a, 'b, YSpan, YSpanTreeTrait>,
|
||||
change: StatusChange,
|
||||
) {
|
||||
let value = cursor.as_mut();
|
||||
|
|
|
@ -30,13 +30,14 @@ pub struct SafeCursor<'tree, 'bump, T: Rle, A: RleTreeTrait<T>>(
|
|||
pub(crate) UnsafeCursor<'tree, 'bump, T, A>,
|
||||
);
|
||||
|
||||
pub struct SafeCursorMut<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>>(
|
||||
#[repr(transparent)]
|
||||
pub struct SafeCursorMut<'tree, 'bump, T: Rle, A: RleTreeTrait<T>>(
|
||||
pub(crate) UnsafeCursor<'tree, 'bump, T, A>,
|
||||
);
|
||||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> Clone for SafeCursor<'tree, 'bump, T, A> {
|
||||
fn clone(&self) -> Self {
|
||||
Self(self.0.clone())
|
||||
Self(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -152,6 +153,16 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
|
|||
pub fn as_ref_(&self) -> &'tree T {
|
||||
unsafe { self.0.as_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn leaf(&self) -> &'tree LeafNode<'bump, T, A> {
|
||||
unsafe { self.0.leaf.as_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn child_index(&self) -> usize {
|
||||
self.0.index
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump, T, A> {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use crate::Rle;
|
||||
|
||||
use super::{
|
||||
|
@ -8,7 +6,7 @@ use super::{
|
|||
SafeCursor,
|
||||
};
|
||||
|
||||
pub struct Iter<'some, 'bump, T: Rle, A: RleTreeTrait<T>> {
|
||||
pub struct Iter<'some, 'bump: 'some, T: Rle, A: RleTreeTrait<T>> {
|
||||
node: Option<&'some LeafNode<'bump, T, A>>,
|
||||
child_index: usize,
|
||||
end_node: Option<&'some LeafNode<'bump, T, A>>,
|
||||
|
|
|
@ -64,12 +64,20 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
|
|||
match self {
|
||||
Self::Internal(node) => node
|
||||
.children
|
||||
.get(0)
|
||||
.first()
|
||||
.and_then(|child| child.get_first_leaf()),
|
||||
Self::Leaf(node) => Some(node),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_last_leaf(&self) -> Option<&LeafNode<'a, T, A>> {
|
||||
match self {
|
||||
Self::Internal(node) => node.children.last().and_then(|child| child.get_last_leaf()),
|
||||
Self::Leaf(node) => Some(node),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn children_num(&self) -> usize {
|
||||
match self {
|
||||
|
|
|
@ -109,9 +109,10 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
from.map_or((0, None), |x| self._delete_start(x));
|
||||
let (direct_delete_end, to_del_end_offset) =
|
||||
to.map_or((self.children.len(), None), |x| self._delete_end(x));
|
||||
let mut result = Ok(());
|
||||
let deleted_len = direct_delete_end as isize - direct_delete_start as isize;
|
||||
let mut insertions = vec![];
|
||||
{
|
||||
// handle edge removing
|
||||
// handle removing at the end point
|
||||
let mut handled = false;
|
||||
if let (Some(del_from), Some(del_to)) = (to_del_start_offset, to_del_end_offset) {
|
||||
if direct_delete_start - 1 == direct_delete_end {
|
||||
|
@ -128,12 +129,12 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
depth + 1,
|
||||
notify,
|
||||
) {
|
||||
result = self._insert_with_split(direct_delete_end + 1, new);
|
||||
insertions.push((direct_delete_end + 1, new));
|
||||
}
|
||||
}
|
||||
Node::Leaf(node) => {
|
||||
if let Err(new) = node.delete(Some(del_from), Some(del_to), notify) {
|
||||
result = self._insert_with_split(direct_delete_end + 1, new);
|
||||
insertions.push((direct_delete_end + 1, new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -152,12 +153,12 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
if let Err(new) =
|
||||
node._delete(Some(del_from), None, visited, depth + 1, notify)
|
||||
{
|
||||
result = self._insert_with_split(direct_delete_start, new);
|
||||
insertions.push((direct_delete_start, new));
|
||||
}
|
||||
}
|
||||
Node::Leaf(node) => {
|
||||
if let Err(new) = node.delete(Some(del_from), None, notify) {
|
||||
result = self._insert_with_split(direct_delete_start, new)
|
||||
insertions.push((direct_delete_start, new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -172,14 +173,12 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
if let Err(new) =
|
||||
node._delete(None, Some(del_to), visited, depth + 1, notify)
|
||||
{
|
||||
debug_assert!(result.is_ok());
|
||||
result = self._insert_with_split(direct_delete_end + 1, new);
|
||||
insertions.push((direct_delete_end + 1, new));
|
||||
}
|
||||
}
|
||||
Node::Leaf(node) => {
|
||||
if let Err(new) = node.delete(None, Some(del_to), notify) {
|
||||
debug_assert!(result.is_ok());
|
||||
result = self._insert_with_split(direct_delete_end + 1, new);
|
||||
insertions.push((direct_delete_end + 1, new));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -187,10 +186,24 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
if direct_delete_start < direct_delete_end {
|
||||
if deleted_len > 0 {
|
||||
self.connect_leaf(direct_delete_start, direct_delete_end - 1);
|
||||
self.children.drain(direct_delete_start..direct_delete_end);
|
||||
}
|
||||
|
||||
insertions.sort_by_key(|x| -(x.0 as isize));
|
||||
let mut result = Ok(());
|
||||
for mut insertion in insertions {
|
||||
if insertion.0 >= direct_delete_end && deleted_len > 0 {
|
||||
insertion.0 -= deleted_len as usize;
|
||||
}
|
||||
|
||||
if let Err(data) = self._insert_with_split(insertion.0, insertion.1) {
|
||||
assert!(result.is_ok());
|
||||
result = Err(data);
|
||||
}
|
||||
}
|
||||
|
||||
A::update_cache_internal(self);
|
||||
if let Err(new) = &mut result {
|
||||
A::update_cache_internal(new.as_internal_mut().unwrap());
|
||||
|
@ -199,6 +212,24 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
result
|
||||
}
|
||||
|
||||
/// connect [prev leaf of left] with [next leaf of right]
|
||||
fn connect_leaf(&mut self, left_index: usize, right_index: usize) {
|
||||
let prev = self.children[left_index]
|
||||
.get_first_leaf()
|
||||
.and_then(|x| x.prev);
|
||||
let next = self.children[right_index]
|
||||
.get_last_leaf()
|
||||
.and_then(|x| x.next);
|
||||
if let Some(mut prev) = prev {
|
||||
let prev = unsafe { prev.as_mut() };
|
||||
prev.next = next;
|
||||
}
|
||||
if let Some(mut next) = next {
|
||||
let next = unsafe { next.as_mut() };
|
||||
next.prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
fn _delete_start(&mut self, from: A::Int) -> (usize, Option<A::Int>) {
|
||||
let from = A::find_pos_internal(self, from);
|
||||
if from.pos == Position::Start || from.pos == Position::Before {
|
||||
|
|
|
@ -41,6 +41,9 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
|
||||
ans_inner.next = self.next;
|
||||
ans_inner.prev = Some(NonNull::new(self).unwrap());
|
||||
if let Some(mut next) = self.next {
|
||||
unsafe { next.as_mut().prev = Some(NonNull::new_unchecked(ans_inner)) };
|
||||
}
|
||||
self.next = Some(NonNull::new(&mut *ans_inner).unwrap());
|
||||
ans
|
||||
}
|
||||
|
@ -94,6 +97,14 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
pub(crate) fn check(&mut self) {
|
||||
assert!(self.children.len() <= A::MAX_CHILDREN_NUM);
|
||||
A::check_cache_leaf(self);
|
||||
if let Some(next) = self.next {
|
||||
let self_ptr = unsafe { next.as_ref().prev.unwrap().as_ptr() };
|
||||
assert!(std::ptr::eq(self, self_ptr));
|
||||
}
|
||||
if let Some(prev) = self.prev {
|
||||
let self_ptr = unsafe { prev.as_ref().next.unwrap().as_ptr() };
|
||||
assert!(std::ptr::eq(self, self_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
fn _delete_start(&mut self, from: A::Int) -> (usize, Option<usize>) {
|
||||
|
|
|
@ -90,6 +90,9 @@ fn test(interactions: &[Interaction]) {
|
|||
let out = out.unwrap();
|
||||
//println!("vs \nindexMap: {:#?}", &out);
|
||||
assert_eq!(v.as_ref().value, out.start);
|
||||
let leaf = v.0.leaf.as_ptr() as usize;
|
||||
let out_ptr = out.value.as_ptr() as usize;
|
||||
assert_eq!(out_ptr, leaf);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -111,8 +114,8 @@ fn test(interactions: &[Interaction]) {
|
|||
|
||||
prop_compose! {
|
||||
fn gen_interaction()(
|
||||
_type in 0..1,
|
||||
from in 0..10000,
|
||||
_type in 0..2,
|
||||
from in 0..100,
|
||||
len in 1..10,
|
||||
) -> Interaction {
|
||||
if _type == 0 {
|
||||
|
@ -157,59 +160,12 @@ fn issue_2() {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn issue_3() {
|
||||
fn issue_4() {
|
||||
test(&[
|
||||
Insert { from: 0, len: 7 },
|
||||
Insert { from: 0, len: 4 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 4, len: 2 },
|
||||
Insert { from: 0, len: 3 },
|
||||
Insert { from: 120, len: 1 },
|
||||
Insert { from: 1785, len: 1 },
|
||||
Insert { from: 6137, len: 1 },
|
||||
Insert { from: 2970, len: 5 },
|
||||
Insert { from: 2424, len: 5 },
|
||||
Insert { from: 2246, len: 4 },
|
||||
Insert { from: 104, len: 1 },
|
||||
Insert { from: 447, len: 1 },
|
||||
Insert { from: 5394, len: 3 },
|
||||
Insert { from: 113, len: 6 },
|
||||
Insert { from: 6563, len: 7 },
|
||||
Insert { from: 8964, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
Insert { from: 0, len: 1 },
|
||||
]);
|
||||
Insert { from: 0, len: 5 },
|
||||
Insert { from: 12, len: 2 },
|
||||
Insert { from: 5, len: 1 },
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(not(no_proptest))]
|
||||
|
|
|
@ -125,7 +125,9 @@ impl Interaction {
|
|||
pub fn test_assert(&self, s: &mut String, tree: &mut RleTree<CustomString, StringTreeTrait>) {
|
||||
self.apply_to_str(s);
|
||||
self.apply_to_tree(tree);
|
||||
// println!("{:#?}", tree);
|
||||
assert_eq!(&tree.to_string(), s);
|
||||
// println!("{}", s);
|
||||
}
|
||||
|
||||
fn apply_to_str(&self, s: &mut String) {
|
||||
|
@ -135,8 +137,12 @@ impl Interaction {
|
|||
s.insert_str(insert_at, content.as_str())
|
||||
}
|
||||
Interaction::Delete { from, len } => {
|
||||
if s.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let from = *from % s.len();
|
||||
let mut to = from + (*len % s.len());
|
||||
let mut to = from + *len;
|
||||
if to > s.len() {
|
||||
to = s.len();
|
||||
}
|
||||
|
@ -156,8 +162,12 @@ impl Interaction {
|
|||
}
|
||||
Interaction::Delete { from, len } => {
|
||||
tree.with_tree_mut(|tree| {
|
||||
if tree.len() == 0 {
|
||||
return;
|
||||
}
|
||||
|
||||
let from = *from % tree.len();
|
||||
let mut to = from + (*len % tree.len());
|
||||
let mut to = from + *len;
|
||||
if to > tree.len() {
|
||||
to = tree.len();
|
||||
}
|
||||
|
@ -169,6 +179,78 @@ impl Interaction {
|
|||
}
|
||||
}
|
||||
|
||||
fn run_test(interactions: Vec<Interaction>) {
|
||||
let mut s = String::new();
|
||||
let mut tree = RleTree::default();
|
||||
for interaction in interactions {
|
||||
interaction.test_assert(&mut s, &mut tree);
|
||||
tree.with_tree_mut(|tree| {
|
||||
tree.debug_check();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
use Interaction::*;
|
||||
|
||||
#[test]
|
||||
fn issue_delete() {
|
||||
run_test(vec![
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "0000000000000".into(),
|
||||
},
|
||||
Delete { from: 7, len: 1 },
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "11111111111111111".into(),
|
||||
},
|
||||
Delete { from: 0, len: 1 },
|
||||
Insert {
|
||||
insert_at: 1,
|
||||
content: "2222222".into(),
|
||||
},
|
||||
Delete { from: 0, len: 3 },
|
||||
Delete { from: 0, len: 1 },
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "333333333".into(),
|
||||
},
|
||||
Insert {
|
||||
insert_at: 8,
|
||||
content: "44".into(),
|
||||
},
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "55555555555555555555555555555555".into(),
|
||||
},
|
||||
Delete { from: 0, len: 1 },
|
||||
Delete { from: 2, len: 4 },
|
||||
Delete { from: 3, len: 9 },
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "6".into(),
|
||||
},
|
||||
Delete { from: 5, len: 6 },
|
||||
Delete { from: 4, len: 6 },
|
||||
Insert {
|
||||
insert_at: 9,
|
||||
content: "7".into(),
|
||||
},
|
||||
Insert {
|
||||
insert_at: 8,
|
||||
content: "88".into(),
|
||||
},
|
||||
Insert {
|
||||
insert_at: 0,
|
||||
content: "9".into(),
|
||||
},
|
||||
// 96555555388373333334432222111111111111111000000000000
|
||||
Delete { from: 6, len: 6 },
|
||||
// Expected: 96555573333334432222111111111111111000000000000
|
||||
// Out: 965573333334432222111111111111111000000000000
|
||||
])
|
||||
}
|
||||
|
||||
#[cfg(not(no_proptest))]
|
||||
mod string_prop_test {
|
||||
use super::*;
|
||||
|
@ -176,10 +258,10 @@ mod string_prop_test {
|
|||
|
||||
prop_compose! {
|
||||
fn gen_interaction()(
|
||||
_type in 0..1,
|
||||
_type in 0..2,
|
||||
from in 0..10000000,
|
||||
len in 0..10,
|
||||
content in "[a-z]*"
|
||||
len in 1..10,
|
||||
content in "[a-z]+"
|
||||
) -> Interaction {
|
||||
if _type == 0 {
|
||||
Interaction::Insert {
|
||||
|
@ -200,12 +282,7 @@ mod string_prop_test {
|
|||
fn test_tree_string_op_the_same(
|
||||
interactions in prop::collection::vec(gen_interaction(), 1..100),
|
||||
) {
|
||||
let mut s = String::new();
|
||||
let mut tree = RleTree::default();
|
||||
for interaction in interactions {
|
||||
interaction.test_assert(&mut s, &mut tree);
|
||||
tree.with_tree_mut(|tree|tree.debug_check());
|
||||
}
|
||||
run_test(interactions);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue