mirror of
https://github.com/loro-dev/loro.git
synced 2024-11-28 17:41:49 +00:00
feat: insert at cursor
This commit is contained in:
parent
a61ac5b335
commit
36c9fd7340
8 changed files with 244 additions and 51 deletions
|
@ -1,6 +1,9 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use rle::{rle_tree::node::LeafNode, HasLength};
|
||||
use rle::{
|
||||
rle_tree::{iter::Iter, node::LeafNode},
|
||||
HasLength,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
id::{Counter, ID},
|
||||
|
@ -15,12 +18,18 @@ use self::{
|
|||
y_span::{Status, YSpan},
|
||||
};
|
||||
|
||||
use super::text_content::{new_unknown_text, TextOpContent};
|
||||
use super::text_content::TextOpContent;
|
||||
|
||||
mod content_map;
|
||||
mod cursor_map;
|
||||
mod y_span;
|
||||
|
||||
/// A tracker for a single text, we can use it to calculate the effect of an operation on a text.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// - [YSpan] never gets removed in both [ContentMap] and [CursorMap]
|
||||
/// - The deleted contents are marked with deleted, but still lives on the [ContentMap] with length of 0
|
||||
struct Tracker {
|
||||
content: ContentMap,
|
||||
id_to_cursor: CursorMap,
|
||||
|
@ -76,13 +85,22 @@ impl Tracker {
|
|||
fn turn_off(&mut self, _id: IdSpan) {}
|
||||
fn checkout(&mut self, _vv: VersionVector) {}
|
||||
|
||||
/// apply an operation directly to the current tracker
|
||||
fn apply(&mut self, op: &Op) {
|
||||
match &op.content {
|
||||
crate::op::OpContent::Normal { content } => {
|
||||
if let Some(text_content) = downcast_ref::<TextOpContent>(&**content) {
|
||||
match text_content {
|
||||
TextOpContent::Insert { id, text, pos } => {
|
||||
let yspan = self.content.new_yspan_at_pos(*id, *pos, text.len());
|
||||
self.content.insert_yspan_at_pos(
|
||||
*id,
|
||||
*pos,
|
||||
text.len(),
|
||||
&mut |v, leaf| {
|
||||
|
||||
//TODO notify
|
||||
},
|
||||
);
|
||||
}
|
||||
TextOpContent::Delete { id, pos, len } => todo!(),
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use rle::{
|
||||
rle_tree::{Position, SafeCursor, SafeCursorMut, UnsafeCursor},
|
||||
rle_tree::{node::LeafNode, Position, SafeCursor, SafeCursorMut, UnsafeCursor},
|
||||
HasLength, RleTree,
|
||||
};
|
||||
|
||||
use crate::{container::text::text_content::TextPointer, id::ID};
|
||||
use crate::id::ID;
|
||||
|
||||
use super::y_span::{StatusChange, YSpan, YSpanTreeTrait};
|
||||
|
||||
/// It stores all the [YSpan] data, including the deleted/undo ones
|
||||
///
|
||||
/// Its internal state, acquired by traveling from begin to end, represents the **current** state of the tree
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug, Default)]
|
||||
pub(super) struct ContentMap(RleTree<YSpan, YSpanTreeTrait>);
|
||||
|
@ -20,17 +23,23 @@ struct CursorWithId<'tree> {
|
|||
|
||||
impl ContentMap {
|
||||
#[inline]
|
||||
pub fn new_yspan_at_pos(&mut self, id: ID, pos: usize, len: usize) -> YSpan {
|
||||
pub(super) fn insert_yspan_at_pos<F>(&mut self, id: ID, pos: usize, len: usize, notify: &mut F)
|
||||
where
|
||||
F: FnMut(&YSpan, *const LeafNode<'_, YSpan, YSpanTreeTrait>),
|
||||
{
|
||||
let (left, right) = self.get_sibling_at(pos);
|
||||
YSpan {
|
||||
origin_left: left.map(|x| x.id).unwrap_or_else(ID::null),
|
||||
origin_right: right.map(|x| x.id).unwrap_or_else(ID::null),
|
||||
let yspan = YSpan {
|
||||
origin_left: left.as_ref().map(|x| x.id).unwrap_or_else(ID::null),
|
||||
origin_right: right.as_ref().map(|x| x.id).unwrap_or_else(ID::null),
|
||||
id,
|
||||
len,
|
||||
status: Default::default(),
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: insert between left & right
|
||||
}
|
||||
|
||||
/// When we insert a new [YSpan] at given position, we need to calculate its `originLeft` and `originRight`
|
||||
fn get_sibling_at(&self, pos: usize) -> (Option<CursorWithId<'_>>, Option<CursorWithId<'_>>) {
|
||||
self.with_tree(|tree| {
|
||||
if let Some(cursor) = tree.get(pos) {
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
use crate::{
|
||||
container::text::text_content::TextPointer, id::Counter, ContentType, InsertContent, ID,
|
||||
};
|
||||
use crate::{id::Counter, ContentType, InsertContent, ID};
|
||||
use rle::{rle_tree::tree_trait::CumulateTreeTrait, HasLength, Mergable, Sliceable};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Default)]
|
||||
|
|
|
@ -89,6 +89,27 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> UnsafeCursor<'tree, 'bump,
|
|||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// we need to make sure that the cursor is still valid
|
||||
pub unsafe fn insert_notify<F>(&mut self, value: T, notify: &mut F)
|
||||
where
|
||||
F: FnMut(&T, *mut LeafNode<'_, T, A>),
|
||||
{
|
||||
let leaf = self.leaf.as_mut();
|
||||
let result = leaf.insert_at_pos(self.pos, self.index, self.offset, value, notify);
|
||||
let mut node = leaf.parent.as_mut();
|
||||
if let Err(new) = result {
|
||||
let mut result = node.insert_at_pos(leaf.get_index_in_parent().unwrap() + 1, new);
|
||||
while let Err(new) = result {
|
||||
let old_node_index = node.get_index_in_parent().unwrap();
|
||||
// result is err, so we're sure parent is valid
|
||||
node = node.parent.unwrap().as_mut();
|
||||
result = node.insert_at_pos(old_node_index + 1, new);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// we need to make sure that the cursor is still valid
|
||||
|
@ -275,6 +296,14 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
|
|||
pub fn offset(&self) -> usize {
|
||||
self.0.offset
|
||||
}
|
||||
|
||||
pub fn insert_notify<F>(&mut self, value: T, notify: &mut F)
|
||||
where
|
||||
F: FnMut(&T, *mut LeafNode<'_, T, A>),
|
||||
{
|
||||
// SAFETY: we know the cursor is a valid pointer
|
||||
unsafe { self.0.insert_notify(value, notify) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsMut<T>
|
||||
|
|
|
@ -332,6 +332,30 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn insert_at_pos(
|
||||
&mut self,
|
||||
index: usize,
|
||||
value: &'a mut Node<'a, T, A>,
|
||||
) -> Result<(), &'a mut Node<'a, T, A>> {
|
||||
let result = self._insert_with_split(index, value);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
A::update_cache_internal(self);
|
||||
Ok(())
|
||||
}
|
||||
Err(new) => {
|
||||
A::update_cache_internal(self);
|
||||
A::update_cache_internal(new.as_internal_mut().unwrap());
|
||||
if self.is_root() {
|
||||
self._create_level(new);
|
||||
Ok(())
|
||||
} else {
|
||||
Err(new)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
||||
|
@ -484,6 +508,16 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> InternalNode<'a, T, A> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_index_in_parent(&self) -> Option<usize> {
|
||||
let parent = self.parent.unwrap();
|
||||
// SAFETY: we know parent must be valid
|
||||
let parent = unsafe { parent.as_ref() };
|
||||
parent
|
||||
.children
|
||||
.iter()
|
||||
.position(|child| std::ptr::eq(child.as_internal().unwrap(), self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Rle, A: RleTreeTrait<T>> Debug for InternalNode<'a, T, A> {
|
||||
|
|
|
@ -168,7 +168,52 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
where
|
||||
F: FnMut(&T, *mut LeafNode<'_, T, A>),
|
||||
{
|
||||
match self._insert(raw_index, value, notify) {
|
||||
let result = {
|
||||
if self.children.is_empty() {
|
||||
notify(&value, self);
|
||||
self.children.push(self.bump.alloc(value));
|
||||
Ok(())
|
||||
} else {
|
||||
let FindPosResult {
|
||||
child_index,
|
||||
offset,
|
||||
pos,
|
||||
..
|
||||
} = A::find_pos_leaf(self, raw_index);
|
||||
self._insert_at_pos(pos, child_index, offset, value, notify)
|
||||
}
|
||||
};
|
||||
self.with_cache_updated(result)
|
||||
}
|
||||
|
||||
pub(crate) fn insert_at_pos<F>(
|
||||
&mut self,
|
||||
pos: Position,
|
||||
child_index: usize,
|
||||
offset: usize,
|
||||
value: T,
|
||||
notify: &mut F,
|
||||
) -> Result<(), &'bump mut Node<'bump, T, A>>
|
||||
where
|
||||
F: FnMut(&T, *mut LeafNode<'_, T, A>),
|
||||
{
|
||||
let result = {
|
||||
if self.children.is_empty() {
|
||||
notify(&value, self);
|
||||
self.children.push(self.bump.alloc(value));
|
||||
Ok(())
|
||||
} else {
|
||||
self._insert_at_pos(pos, child_index, offset, value, notify)
|
||||
}
|
||||
};
|
||||
self.with_cache_updated(result)
|
||||
}
|
||||
|
||||
fn with_cache_updated(
|
||||
&mut self,
|
||||
result: Result<(), &'bump mut Node<'bump, T, A>>,
|
||||
) -> Result<(), &'bump mut Node<'bump, T, A>> {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
A::update_cache_leaf(self);
|
||||
Ok(())
|
||||
|
@ -181,27 +226,17 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
fn _insert<F>(
|
||||
fn _insert_at_pos<F>(
|
||||
&mut self,
|
||||
raw_index: A::Int,
|
||||
mut pos: Position,
|
||||
mut child_index: usize,
|
||||
mut offset: usize,
|
||||
value: T,
|
||||
notify: &mut F,
|
||||
) -> Result<(), &'bump mut Node<'bump, T, A>>
|
||||
where
|
||||
F: FnMut(&T, *mut LeafNode<'_, T, A>),
|
||||
{
|
||||
if self.children.is_empty() {
|
||||
notify(&value, self);
|
||||
self.children.push(self.bump.alloc(value));
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let FindPosResult {
|
||||
mut child_index,
|
||||
mut offset,
|
||||
mut pos,
|
||||
..
|
||||
} = A::find_pos_leaf(self, raw_index);
|
||||
let self_ptr = self as *mut _;
|
||||
let prev = {
|
||||
if (pos == Position::Start || pos == Position::Before) && child_index > 0 {
|
||||
|
@ -215,7 +250,6 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(prev) = prev {
|
||||
// clean cut, should no split
|
||||
if prev.is_mergable(&value, &()) {
|
||||
|
@ -224,17 +258,14 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
let clean_cut = pos != Position::Middle;
|
||||
if clean_cut {
|
||||
return self._insert_with_split(child_index, value, notify);
|
||||
}
|
||||
|
||||
// need to split child
|
||||
let a = self.children[child_index].slice(0, offset);
|
||||
let b = self.children[child_index].slice(offset, self.children[child_index].len());
|
||||
self.children[child_index] = self.bump.alloc(a);
|
||||
|
||||
if self.children.len() >= A::MAX_CHILDREN_NUM - 1 {
|
||||
let next_node = self._split(notify);
|
||||
let next_leaf = next_node.as_leaf_mut().unwrap();
|
||||
|
@ -262,7 +293,6 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
|
||||
return Err(next_node);
|
||||
}
|
||||
|
||||
notify(&b, self);
|
||||
notify(&value, self);
|
||||
self.children.insert(child_index + 1, self.bump.alloc(b));
|
||||
|
@ -380,6 +410,16 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> LeafNode<'a, T, A> {
|
|||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_index_in_parent(&self) -> Option<usize> {
|
||||
let parent = self.parent;
|
||||
// SAFETY: we know parent must be valid
|
||||
let parent = unsafe { parent.as_ref() };
|
||||
parent
|
||||
.children
|
||||
.iter()
|
||||
.position(|child| std::ptr::eq(child.as_leaf().unwrap(), self))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: Rle, A: RleTreeTrait<T>> Debug for LeafNode<'a, T, A> {
|
||||
|
|
|
@ -15,8 +15,15 @@ type ValueTreeTrait = CumulateTreeTrait<Value, 4>;
|
|||
|
||||
#[derive(enum_as_inner::EnumAsInner, Debug)]
|
||||
enum Interaction {
|
||||
Insert { from: usize, len: usize },
|
||||
Delete { from: usize, len: usize },
|
||||
Insert {
|
||||
from: usize,
|
||||
len: usize,
|
||||
use_cursor: bool,
|
||||
},
|
||||
Delete {
|
||||
from: usize,
|
||||
len: usize,
|
||||
},
|
||||
}
|
||||
|
||||
macro_rules! _println {
|
||||
|
@ -38,7 +45,11 @@ impl Interaction {
|
|||
R: rand::Rng,
|
||||
{
|
||||
match self {
|
||||
Interaction::Insert { from, len } => tree.with_tree_mut(|tree| {
|
||||
Interaction::Insert {
|
||||
from,
|
||||
len,
|
||||
use_cursor,
|
||||
} => tree.with_tree_mut(|tree| {
|
||||
let mut from = *from;
|
||||
let len = *len;
|
||||
if tree.len() == 0 {
|
||||
|
@ -52,7 +63,15 @@ impl Interaction {
|
|||
value: rng.next_u64(),
|
||||
};
|
||||
_println!("Insert {{from: {}, len: {}}},", from, len);
|
||||
if *use_cursor {
|
||||
if let Some(mut cursor) = tree.get_mut(from) {
|
||||
cursor.insert_notify(value, notify)
|
||||
} else {
|
||||
tree.insert_notify(from, value, notify);
|
||||
}
|
||||
} else {
|
||||
tree.insert_notify(from, value, notify);
|
||||
}
|
||||
}),
|
||||
Interaction::Delete { from, len } => tree.with_tree_mut(|tree| {
|
||||
let mut from = *from;
|
||||
|
@ -151,11 +170,13 @@ prop_compose! {
|
|||
_type in 0..2,
|
||||
from in 0..1000,
|
||||
len in 0..10,
|
||||
use_cursor: bool,
|
||||
) -> Interaction {
|
||||
if _type == 0 {
|
||||
Interaction::Insert {
|
||||
from: from as usize,
|
||||
len: len as usize,
|
||||
use_cursor
|
||||
}
|
||||
} else {
|
||||
Interaction::Delete {
|
||||
|
@ -171,25 +192,53 @@ use Interaction::*;
|
|||
#[test]
|
||||
fn issue_0() {
|
||||
test(&[
|
||||
Interaction::Insert { from: 0, len: 1 },
|
||||
Interaction::Insert { from: 0, len: 2 },
|
||||
Interaction::Insert {
|
||||
from: 0,
|
||||
len: 1,
|
||||
use_cursor: false,
|
||||
},
|
||||
Interaction::Insert {
|
||||
from: 0,
|
||||
len: 2,
|
||||
use_cursor: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_1() {
|
||||
test(&[
|
||||
Interaction::Insert { from: 0, len: 3 },
|
||||
Interaction::Insert { from: 1, len: 1 },
|
||||
Interaction::Insert {
|
||||
from: 0,
|
||||
len: 3,
|
||||
use_cursor: false,
|
||||
},
|
||||
Interaction::Insert {
|
||||
from: 1,
|
||||
len: 1,
|
||||
use_cursor: false,
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_2() {
|
||||
test(&[
|
||||
Insert { from: 0, len: 5 },
|
||||
Insert { from: 0, len: 6 },
|
||||
Insert { from: 4, len: 3 },
|
||||
Insert {
|
||||
from: 0,
|
||||
len: 5,
|
||||
use_cursor: false,
|
||||
},
|
||||
Insert {
|
||||
from: 0,
|
||||
len: 6,
|
||||
use_cursor: false,
|
||||
},
|
||||
Insert {
|
||||
from: 4,
|
||||
len: 3,
|
||||
use_cursor: false,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
|
@ -197,18 +246,34 @@ fn issue_2() {
|
|||
fn issue_4() {
|
||||
test(&[
|
||||
// 0-5
|
||||
Insert { from: 0, len: 5 },
|
||||
Insert {
|
||||
from: 0,
|
||||
len: 5,
|
||||
use_cursor: false,
|
||||
},
|
||||
// 0-2, 2-4, 2-5
|
||||
Insert { from: 2, len: 2 },
|
||||
Insert {
|
||||
from: 2,
|
||||
len: 2,
|
||||
use_cursor: true,
|
||||
},
|
||||
// 0-2, 2-4, 2-3, 5-6, 3-5
|
||||
Insert { from: 5, len: 1 },
|
||||
Insert {
|
||||
from: 5,
|
||||
len: 1,
|
||||
use_cursor: true,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn issue_5() {
|
||||
test(&[
|
||||
Insert { from: 0, len: 0 },
|
||||
Insert {
|
||||
from: 0,
|
||||
len: 0,
|
||||
use_cursor: false,
|
||||
},
|
||||
Delete { from: 0, len: 0 },
|
||||
Delete { from: 0, len: 0 },
|
||||
])
|
||||
|
@ -222,7 +287,7 @@ mod notify_proptest {
|
|||
proptest! {
|
||||
#[test]
|
||||
fn test_notify(
|
||||
interactions in prop::collection::vec(gen_interaction(), 1..100),
|
||||
interactions in prop::collection::vec(gen_interaction(), 1..1000),
|
||||
) {
|
||||
test(&interactions);
|
||||
}
|
||||
|
|
|
@ -113,7 +113,7 @@ impl<T: Rle, const MAX_CHILD: usize> RleTreeTrait<T> for CumulateTreeTrait<T, MA
|
|||
mut index: Self::Int,
|
||||
) -> FindPosResult<usize> {
|
||||
if node.children.is_empty() {
|
||||
return FindPosResult::new(0, 0, Position::Before);
|
||||
return FindPosResult::new_not_found(0, 0, Position::Before);
|
||||
}
|
||||
|
||||
let mut last_cache = 0;
|
||||
|
@ -145,7 +145,7 @@ impl<T: Rle, const MAX_CHILD: usize> RleTreeTrait<T> for CumulateTreeTrait<T, MA
|
|||
|
||||
fn find_pos_leaf(node: &LeafNode<'_, T, Self>, mut index: Self::Int) -> FindPosResult<usize> {
|
||||
if node.children.is_empty() {
|
||||
return FindPosResult::new(0, 0, Position::Before);
|
||||
return FindPosResult::new_not_found(0, 0, Position::Before);
|
||||
}
|
||||
|
||||
for (i, child) in node.children().iter().enumerate() {
|
||||
|
|
Loading…
Reference in a new issue