feat: insert at cursor

This commit is contained in:
Zixuan Chen 2022-09-23 17:45:13 +08:00
parent a61ac5b335
commit 36c9fd7340
8 changed files with 244 additions and 51 deletions

View file

@ -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!(),
}

View file

@ -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) {

View file

@ -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)]

View file

@ -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>

View file

@ -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> {

View file

@ -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> {

View file

@ -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);
}

View file

@ -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() {