mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-22 12:57:20 +00:00
feat: introduce crdt-list
This commit is contained in:
parent
e9100f300d
commit
cd95e2276c
15 changed files with 370 additions and 33 deletions
16
Cargo.lock
generated
16
Cargo.lock
generated
|
@ -29,6 +29,12 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
||||
|
||||
[[package]]
|
||||
name = "arref"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ccd462b64c3c72f1be8305905a85d85403d768e8690c9b8bd3b9009a5761679"
|
||||
|
||||
[[package]]
|
||||
name = "atty"
|
||||
version = "0.2.14"
|
||||
|
@ -144,6 +150,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crdt-list"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4a759e0bf62cfa3fbc92c569e0ef8d133f8e3c0fc28ff743f43af9da923d068"
|
||||
dependencies = [
|
||||
"arref",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
|
@ -319,6 +334,7 @@ dependencies = [
|
|||
name = "loro-core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crdt-list",
|
||||
"enum-as-inner",
|
||||
"fxhash",
|
||||
"im",
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# Loro
|
||||
|
||||
# Dev
|
||||
|
||||
we use nightly rust due to GAT in crdt-list. we can use stable rust when GAT is
|
||||
stable
|
|
@ -19,6 +19,7 @@ thiserror = "1.0.31"
|
|||
im = "15.1.0"
|
||||
enum-as-inner = "0.5.1"
|
||||
num = "0.4.0"
|
||||
crdt-list = "0.1.1"
|
||||
|
||||
[dev-dependencies]
|
||||
proptest = "1.0.0"
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
use std::ptr::NonNull;
|
||||
|
||||
use rle::{
|
||||
rle_tree::{iter::Iter, node::LeafNode},
|
||||
HasLength,
|
||||
};
|
||||
use rle::{rle_tree::node::LeafNode, HasLength};
|
||||
|
||||
use crate::{
|
||||
id::{Counter, ID},
|
||||
|
@ -23,6 +20,7 @@ use super::text_content::TextOpContent;
|
|||
mod content_map;
|
||||
mod cursor_map;
|
||||
mod y_span;
|
||||
mod yata;
|
||||
|
||||
/// A tracker for a single text, we can use it to calculate the effect of an operation on a text.
|
||||
///
|
||||
|
@ -30,6 +28,8 @@ mod y_span;
|
|||
///
|
||||
/// - [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
|
||||
///
|
||||
#[derive(Debug)]
|
||||
struct Tracker {
|
||||
content: ContentMap,
|
||||
id_to_cursor: CursorMap,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use crdt_list::crdt::ListCrdt;
|
||||
use crdt_list::yata::Yata;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
use rle::{
|
||||
|
@ -36,7 +38,7 @@ impl ContentMap {
|
|||
status: Default::default(),
|
||||
};
|
||||
|
||||
// TODO: insert between left & right
|
||||
// TODO: integrate yjs
|
||||
}
|
||||
|
||||
/// When we insert a new [YSpan] at given position, we need to calculate its `originLeft` and `originRight`
|
||||
|
|
|
@ -2,9 +2,13 @@ use std::{fmt::Debug, ptr::NonNull};
|
|||
|
||||
use enum_as_inner::EnumAsInner;
|
||||
|
||||
use rle::{range_map::RangeMap, rle_tree::node::LeafNode, HasLength, Mergable, Sliceable};
|
||||
use rle::{
|
||||
range_map::RangeMap,
|
||||
rle_tree::{node::LeafNode, Position, SafeCursor, SafeCursorMut},
|
||||
HasLength, Mergable, Sliceable,
|
||||
};
|
||||
|
||||
use crate::span::IdSpan;
|
||||
use crate::{id::ID, span::IdSpan};
|
||||
|
||||
use super::y_span::{YSpan, YSpanTreeTrait};
|
||||
|
||||
|
@ -20,6 +24,39 @@ pub(super) enum Marker {
|
|||
// TODO: REDO, UNDO
|
||||
}
|
||||
|
||||
impl Marker {
|
||||
pub fn as_cursor(&self, id: ID) -> Option<SafeCursor<'_, 'static, YSpan, YSpanTreeTrait>> {
|
||||
match self {
|
||||
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))?;
|
||||
// SAFETY: we just checked it is valid
|
||||
Some(unsafe { SafeCursor::new(*ptr, position, 0, rle::rle_tree::Position::Start) })
|
||||
}
|
||||
Marker::Delete(_) => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_cursor_mut(
|
||||
&mut self,
|
||||
id: ID,
|
||||
) -> Option<SafeCursorMut<'_, 'static, YSpan, YSpanTreeTrait>> {
|
||||
match self {
|
||||
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))?;
|
||||
// SAFETY: we just checked it is valid
|
||||
Some(unsafe { SafeCursorMut::new(*ptr, position, 0, Position::Start) })
|
||||
}
|
||||
Marker::Delete(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Sliceable for Marker {
|
||||
fn slice(&self, from: usize, to: usize) -> Self {
|
||||
match self {
|
||||
|
|
|
@ -72,6 +72,13 @@ impl YSpan {
|
|||
debug_assert!(self.len > 0);
|
||||
self.status.is_activated()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn contain_id(&self, id: ID) -> bool {
|
||||
self.id.client_id == id.client_id
|
||||
&& self.id.counter <= id.counter
|
||||
&& self.last_id().counter > id.counter
|
||||
}
|
||||
}
|
||||
|
||||
impl Mergable for YSpan {
|
||||
|
|
98
crates/loro-core/src/container/text/tracker/yata.rs
Normal file
98
crates/loro-core/src/container/text/tracker/yata.rs
Normal file
|
@ -0,0 +1,98 @@
|
|||
use crdt_list::crdt::{ListCrdt, OpSet};
|
||||
use rle::rle_tree::{iter::IterMut, SafeCursorMut, RleTreeRaw};
|
||||
|
||||
use crate::id::ID;
|
||||
|
||||
use super::{
|
||||
content_map::ContentMap,
|
||||
y_span::{YSpan, YSpanTreeTrait},
|
||||
Tracker,
|
||||
};
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
struct OpSpanSet {}
|
||||
|
||||
impl OpSet<YSpan, ID> for OpSpanSet {
|
||||
fn insert(&mut self, value: &YSpan) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn contain(&self, id: ID) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn clear(&mut self) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
struct YataImpl;
|
||||
|
||||
impl ListCrdt for YataImpl {
|
||||
type OpUnit = YSpan;
|
||||
|
||||
type OpId = ID;
|
||||
|
||||
type Container = Tracker;
|
||||
|
||||
type Set = OpSpanSet;
|
||||
|
||||
type Cursor<'a> = SafeCursorMut<'a, 'static, YSpan, YSpanTreeTrait>;
|
||||
|
||||
type Iterator<'a> = IterMut<'a, 'static, YSpan, YSpanTreeTrait>;
|
||||
|
||||
fn iter(
|
||||
container: &mut Self::Container,
|
||||
from: Option<Self::OpId>,
|
||||
to: Option<Self::OpId>,
|
||||
) -> Self::Iterator<'_> {
|
||||
let from = from.and_then(|x| {
|
||||
container
|
||||
.id_to_cursor
|
||||
.get(x.into())
|
||||
.and_then(|m| m.as_cursor(x))
|
||||
});
|
||||
let to = to.and_then(|x| {
|
||||
container
|
||||
.id_to_cursor
|
||||
.get(x.into())
|
||||
.and_then(|m| m.as_cursor(x))
|
||||
});
|
||||
|
||||
container
|
||||
.content
|
||||
.with_tree_mut(|tree|
|
||||
// SAFETY: loosen lifetime requirement here. It's safe because the function
|
||||
// signature can limit the lifetime of the returned iterator
|
||||
unsafe {std::mem::transmute::<_, &mut &mut RleTreeRaw<_, _>>(tree)}.iter_mut_in(from, to)
|
||||
)
|
||||
}
|
||||
|
||||
fn insert_at(container: &mut Self::Container, op: Self::OpUnit, pos: usize) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn id(op: &Self::OpUnit) -> Self::OpId {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn cmp_id(op_a: &Self::OpUnit, op_b: &Self::OpUnit) -> std::cmp::Ordering {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn contains(op: &Self::OpUnit, id: Self::OpId) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn integrate(container: &mut Self::Container, op: Self::OpUnit) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn can_integrate(container: &Self::Container, op: &Self::OpUnit) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn len(container: &Self::Container) -> usize {
|
||||
todo!()
|
||||
}
|
||||
}
|
|
@ -46,6 +46,7 @@ impl<Value: Rle, Index: GlobalIndex> HasGlobalIndex for WithGlobalIndex<Value, I
|
|||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct RangeMap<Index: GlobalIndex + 'static, Value: Rle + 'static> {
|
||||
pub(crate) tree:
|
||||
RleTree<WithGlobalIndex<Value, Index>, GlobalTreeTrait<WithGlobalIndex<Value, Index>, 10>>,
|
||||
|
|
|
@ -3,6 +3,7 @@ use crate::Rle;
|
|||
pub(self) use bumpalo::collections::vec::Vec as BumpVec;
|
||||
use bumpalo::Bump;
|
||||
pub use cursor::{SafeCursor, SafeCursorMut, UnsafeCursor};
|
||||
use num::FromPrimitive;
|
||||
use ouroboros::self_referencing;
|
||||
use std::marker::{PhantomData, PhantomPinned};
|
||||
pub use tree_trait::Position;
|
||||
|
@ -93,12 +94,10 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> RleTreeRaw<'bump, T, A> {
|
|||
return None;
|
||||
}
|
||||
|
||||
return Some(SafeCursor::new(
|
||||
leaf.into(),
|
||||
result.child_index,
|
||||
result.offset,
|
||||
result.pos,
|
||||
));
|
||||
// SAFETY: result is valid
|
||||
return Some(unsafe {
|
||||
SafeCursor::new(leaf.into(), result.child_index, result.offset, result.pos)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -125,12 +124,10 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> RleTreeRaw<'bump, T, A> {
|
|||
return None;
|
||||
}
|
||||
|
||||
return Some(SafeCursor::new(
|
||||
leaf.into(),
|
||||
result.child_index,
|
||||
result.offset,
|
||||
result.pos,
|
||||
));
|
||||
// SAFETY: result is valid
|
||||
return Some(unsafe {
|
||||
SafeCursor::new(leaf.into(), result.child_index, result.offset, result.pos)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -142,10 +139,44 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> RleTreeRaw<'bump, T, A> {
|
|||
cursor.map(|x| SafeCursorMut(x.0))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter(&self) -> iter::Iter<'_, 'bump, T, A> {
|
||||
iter::Iter::new(self.node.get_first_leaf())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn iter_mut(&mut self) -> iter::IterMut<'_, 'bump, T, A> {
|
||||
iter::IterMut::new(self.node.get_first_leaf_mut())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn empty(&self) -> bool {
|
||||
self.len() == A::Int::from_usize(0).unwrap()
|
||||
}
|
||||
|
||||
pub fn iter_mut_in<'tree>(
|
||||
&'tree mut self,
|
||||
start: Option<SafeCursor<'tree, 'bump, T, A>>,
|
||||
end: Option<SafeCursor<'tree, 'bump, T, A>>,
|
||||
) -> iter::IterMut<'tree, 'bump, T, A> {
|
||||
if self.empty() || (start.is_none() && end.is_none()) {
|
||||
self.iter_mut()
|
||||
} else {
|
||||
// SAFETY: this is safe because we know there are at least one element in the tree
|
||||
let start = start.unwrap_or_else(|| unsafe {
|
||||
SafeCursor::new(
|
||||
self.node.get_first_leaf().unwrap().into(),
|
||||
0,
|
||||
0,
|
||||
Position::Start,
|
||||
)
|
||||
});
|
||||
|
||||
let start: SafeCursorMut<'tree, 'bump, T, A> = SafeCursorMut(start.0);
|
||||
iter::IterMut::from_cursor(start, end).unwrap_or_else(|| self.iter_mut())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_range(&mut self, start: Option<A::Int>, end: Option<A::Int>) {
|
||||
self.node
|
||||
.as_internal_mut()
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use std::{marker::PhantomData, ptr::NonNull};
|
||||
use std::{marker::PhantomData, ops::Deref, ptr::NonNull};
|
||||
|
||||
use crate::{Rle, RleTreeTrait};
|
||||
|
||||
|
@ -184,8 +184,11 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursor<'tree, 'bump, T
|
|||
self.0.offset
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Users should make sure aht leaf is pointing to a valid LeafNode with 'bump lifetime, and index is inbound
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
pub unsafe fn new(
|
||||
leaf: NonNull<LeafNode<'bump, T, A>>,
|
||||
index: usize,
|
||||
offset: usize,
|
||||
|
@ -223,6 +226,12 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
|
|||
unsafe { self.0.leaf.as_ref() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn leaf_mut(&mut self) -> &'tree mut LeafNode<'bump, T, A> {
|
||||
// SAFETY: SafeCursorMut is a exclusive reference to the tree
|
||||
unsafe { self.0.leaf.as_mut() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn child_index(&self) -> usize {
|
||||
self.0.index
|
||||
|
@ -231,7 +240,7 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump
|
|||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> SafeCursorMut<'tree, 'bump, T, A> {
|
||||
#[inline]
|
||||
pub(crate) fn new(
|
||||
pub unsafe fn new(
|
||||
leaf: NonNull<LeafNode<'bump, T, A>>,
|
||||
index: usize,
|
||||
offset: usize,
|
||||
|
@ -316,3 +325,19 @@ impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> AsMut<T>
|
|||
unsafe { self.0.as_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> Deref for SafeCursor<'tree, 'bump, T, A> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree, 'bump: 'tree, T: Rle, A: RleTreeTrait<T>> Deref for SafeCursorMut<'tree, 'bump, T, A> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,16 +3,61 @@ use crate::Rle;
|
|||
use super::{
|
||||
node::LeafNode,
|
||||
tree_trait::{Position, RleTreeTrait},
|
||||
SafeCursor,
|
||||
SafeCursor, SafeCursorMut,
|
||||
};
|
||||
|
||||
pub struct Iter<'some, 'bump: 'some, T: Rle, A: RleTreeTrait<T>> {
|
||||
pub struct Iter<'some, 'bump, T: Rle, A: RleTreeTrait<T>> {
|
||||
node: Option<&'some LeafNode<'bump, T, A>>,
|
||||
child_index: usize,
|
||||
end_node: Option<&'some LeafNode<'bump, T, A>>,
|
||||
end_index: Option<usize>,
|
||||
}
|
||||
|
||||
pub struct IterMut<'some, 'bump, T: Rle, A: RleTreeTrait<T>> {
|
||||
node: Option<&'some mut LeafNode<'bump, T, A>>,
|
||||
child_index: usize,
|
||||
end_node: Option<&'some LeafNode<'bump, T, A>>,
|
||||
end_index: Option<usize>,
|
||||
}
|
||||
|
||||
impl<'tree, 'bump, T: Rle, A: RleTreeTrait<T>> IterMut<'tree, 'bump, T, A> {
|
||||
#[inline]
|
||||
pub fn new(node: Option<&'tree mut LeafNode<'bump, T, A>>) -> Self {
|
||||
Self {
|
||||
node,
|
||||
child_index: 0,
|
||||
end_node: None,
|
||||
end_index: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn from_cursor(
|
||||
mut start: SafeCursorMut<'tree, 'bump, T, A>,
|
||||
mut end: Option<SafeCursor<'tree, 'bump, T, A>>,
|
||||
) -> Option<Self> {
|
||||
if start.0.pos == Position::After {
|
||||
start = start.next()?
|
||||
}
|
||||
|
||||
if let Some(end_inner) = end {
|
||||
if end_inner.0.pos == Position::Middle
|
||||
|| end_inner.0.pos == Position::End
|
||||
|| end_inner.0.pos == Position::After
|
||||
{
|
||||
end = end_inner.next();
|
||||
}
|
||||
}
|
||||
|
||||
Some(Self {
|
||||
node: Some(start.leaf_mut()),
|
||||
child_index: start.0.index,
|
||||
end_node: end.map(|end| end.leaf()),
|
||||
end_index: end.map(|end| end.index()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tree, 'bump, T: Rle, A: RleTreeTrait<T>> Iter<'tree, 'bump, T, A> {
|
||||
#[inline]
|
||||
pub fn new(node: Option<&'tree LeafNode<'bump, T, A>>) -> Self {
|
||||
|
@ -67,12 +112,10 @@ impl<'rf, 'bump, T: Rle, A: RleTreeTrait<T>> Iterator for Iter<'rf, 'bump, T, A>
|
|||
match node.children.get(self.child_index) {
|
||||
Some(_) => {
|
||||
self.child_index += 1;
|
||||
return Some(SafeCursor::new(
|
||||
node.into(),
|
||||
self.child_index - 1,
|
||||
0,
|
||||
Position::Start,
|
||||
));
|
||||
// SAFETY: we just checked that the child exists
|
||||
return Some(unsafe {
|
||||
SafeCursor::new(node.into(), self.child_index - 1, 0, Position::Start)
|
||||
});
|
||||
}
|
||||
None => match node.next() {
|
||||
Some(next) => {
|
||||
|
@ -96,3 +139,52 @@ impl<'rf, 'bump, T: Rle, A: RleTreeTrait<T>> Iterator for Iter<'rf, 'bump, T, A>
|
|||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<'rf, 'bump, T: Rle, A: RleTreeTrait<T>> Iterator for IterMut<'rf, 'bump, T, A> {
|
||||
type Item = SafeCursorMut<'rf, 'bump, T, A>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if let (Some(end_node), Some(node), Some(end_index)) = (
|
||||
self.end_node,
|
||||
self.node.as_mut().map(|x| *x as *const LeafNode<_, _>),
|
||||
self.end_index,
|
||||
) {
|
||||
if std::ptr::eq(end_node, node as *const _) && self.child_index == end_index {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(node) = std::mem::take(&mut self.node) {
|
||||
let node_ptr = node as *const _;
|
||||
match node.children.get(self.child_index) {
|
||||
Some(_) => {
|
||||
self.child_index += 1;
|
||||
let leaf = node.into();
|
||||
self.node = Some(node);
|
||||
// SAFETY: we just checked that the child exists
|
||||
return Some(unsafe {
|
||||
SafeCursorMut::new(leaf, self.child_index - 1, 0, Position::Start)
|
||||
});
|
||||
}
|
||||
None => match node.next_mut() {
|
||||
Some(next) => {
|
||||
if let Some(end_node) = self.end_node {
|
||||
// if node == end_node, should not go to next node
|
||||
// in this case end_index == node.children.len()
|
||||
if std::ptr::eq(end_node, node_ptr) {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
self.node = Some(next);
|
||||
self.child_index = 0;
|
||||
continue;
|
||||
}
|
||||
None => return None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
|
|
@ -70,6 +70,17 @@ impl<'a, T: Rle, A: RleTreeTrait<T>> Node<'a, T, A> {
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_first_leaf_mut(&mut self) -> Option<&mut LeafNode<'a, T, A>> {
|
||||
match self {
|
||||
Self::Internal(node) => node
|
||||
.children
|
||||
.first_mut()
|
||||
.and_then(|child| child.get_first_leaf_mut()),
|
||||
Self::Leaf(node) => Some(node),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn get_last_leaf(&self) -> Option<&LeafNode<'a, T, A>> {
|
||||
match self {
|
||||
|
|
|
@ -52,13 +52,17 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
#[inline]
|
||||
pub fn get_cursor<'tree>(&'tree self, pos: A::Int) -> SafeCursor<'tree, 'bump, T, A> {
|
||||
let result = A::find_pos_leaf(self, pos);
|
||||
SafeCursor::new(self.into(), result.child_index, result.offset, result.pos)
|
||||
assert!(result.found);
|
||||
// SAFETY: result.found is true
|
||||
unsafe { SafeCursor::new(self.into(), result.child_index, result.offset, result.pos) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_cursor_mut<'b>(&'b mut self, pos: A::Int) -> SafeCursorMut<'b, 'bump, T, A> {
|
||||
let result = A::find_pos_leaf(self, pos);
|
||||
SafeCursorMut::new(self.into(), result.child_index, result.offset, result.pos)
|
||||
assert!(result.found);
|
||||
// SAFETY: result.found is true
|
||||
unsafe { SafeCursorMut::new(self.into(), result.child_index, result.offset, result.pos) }
|
||||
}
|
||||
|
||||
pub fn push_child<F>(
|
||||
|
@ -307,6 +311,12 @@ impl<'bump, T: Rle, A: RleTreeTrait<T>> LeafNode<'bump, T, A> {
|
|||
unsafe { self.next.map(|p| p.as_ref()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn next_mut(&mut self) -> Option<&mut Self> {
|
||||
// SAFETY: internal variant ensure prev and next are valid reference
|
||||
unsafe { self.next.map(|mut p| p.as_mut()) }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn prev(&self) -> Option<&Self> {
|
||||
// SAFETY: internal variant ensure prev and next are valid reference
|
||||
|
|
|
@ -1 +1 @@
|
|||
stable
|
||||
nightly
|
||||
|
|
Loading…
Reference in a new issue