fix: simplify get siblings

This commit is contained in:
Zixuan Chen 2022-10-14 00:35:40 +08:00
parent e136504f3e
commit 6da6cd2915
3 changed files with 313 additions and 269 deletions

View file

@ -18,7 +18,7 @@ crev:
cargo crev crate check cargo crev crate check
fuzz: fuzz:
cargo fuzz run yata -- -max_total_time=300 -max_len=8000 -jobs=2 cargo fuzz run yata -- -max_total_time=300 -max_len=1000 -jobs=2
flame: flame:
cargo flamegraph --example test --features=fuzzing --root cargo flamegraph --example test --features=fuzzing --root

View file

@ -1,7 +1,7 @@
use std::ops::{Deref, DerefMut}; use std::ops::{Deref, DerefMut};
use rle::{ use rle::{
rle_tree::{Position, SafeCursor, SafeCursorMut, UnsafeCursor}, rle_tree::{Position, SafeCursor, SafeCursorMut},
HasLength, RleTree, RleVec, HasLength, RleTree, RleVec,
}; };
@ -16,28 +16,20 @@ use super::y_span::{StatusChange, YSpan, YSpanTreeTrait};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub(super) struct ContentMap(RleTree<YSpan, YSpanTreeTrait>); pub(super) struct ContentMap(RleTree<YSpan, YSpanTreeTrait>);
struct CursorWithId<'tree> {
id: ID,
cursor: UnsafeCursor<'tree, YSpan, YSpanTreeTrait>,
}
impl ContentMap { impl ContentMap {
#[inline] #[inline]
pub(super) fn get_yspan_at_pos(&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); let (left, right) = self.get_sibling_at(pos);
YSpan { YSpan {
origin_left: left.as_ref().map(|x| x.id), origin_left: left,
origin_right: right.as_ref().map(|x| x.id), origin_right: right,
id, id,
len, len,
status: Default::default(), status: Default::default(),
} }
} }
fn get_sibling_at_dumb( fn get_sibling_at(&self, pos: usize) -> (Option<ID>, Option<ID>) {
&self,
pos: usize,
) -> (Option<CursorWithId<'_>>, Option<CursorWithId<'_>>) {
if let Some(cursor) = self.get(pos) { if let Some(cursor) = self.get(pos) {
let mut 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 // SAFETY: we only change the lifetime of the cursor; the returned lifetime is kinda wrong in this situation
@ -51,16 +43,7 @@ impl ContentMap {
let mut prev_offset_cursor = cursor.unwrap(); let mut prev_offset_cursor = cursor.unwrap();
prev_offset_cursor.offset -= 1; prev_offset_cursor.offset -= 1;
if cursor.as_ref().can_be_origin() { if cursor.as_ref().can_be_origin() {
return ( return (Some(id.inc(offset as i32 - 1)), Some(id.inc(offset as i32)));
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 { } else {
None None
} }
@ -70,10 +53,7 @@ impl ContentMap {
let mut prev_offset_cursor = cursor.unwrap(); let mut prev_offset_cursor = cursor.unwrap();
prev_offset_cursor.offset -= 1; prev_offset_cursor.offset -= 1;
prev_offset_cursor.pos = Position::Middle; prev_offset_cursor.pos = Position::Middle;
Some(CursorWithId { Some(cursor.as_ref().last_id())
id: cursor.as_ref().last_id(),
cursor: prev_offset_cursor,
})
} else { } else {
None None
} }
@ -93,10 +73,7 @@ impl ContentMap {
let mut cursor = cursor.unwrap(); let mut cursor = cursor.unwrap();
cursor.offset = offset; cursor.offset = offset;
cursor.pos = Position::Middle; cursor.pos = Position::Middle;
prev = Some(CursorWithId { prev = Some(prev_inner.as_ref().last_id());
id: prev_inner.as_ref().last_id(),
cursor,
});
break; break;
} }
prev_cursor = prev_inner.prev_elem(); prev_cursor = prev_inner.prev_elem();
@ -104,36 +81,18 @@ impl ContentMap {
} }
let next = if prev.is_some() { let next = if prev.is_some() {
let mut next_cursor = cursor.next_elem_start(); let next_cursor = cursor.next_elem_start();
let mut ans = None; let mut ans = None;
while let Some(next_inner) = next_cursor { if let Some(next_inner) = next_cursor {
if next_inner.as_ref().status.is_activated() { let mut cursor = next_inner.unwrap();
let mut cursor = next_inner.unwrap(); cursor.offset = 0;
cursor.offset = 0; cursor.pos = Position::Start;
cursor.pos = Position::Start; ans = Some(next_inner.as_ref().id);
ans = Some(CursorWithId {
id: next_inner.as_ref().id,
cursor,
});
break;
}
next_cursor = next_inner.next_elem_start();
} }
ans ans
} else { } else {
// if prev is none, next should be the first element in the tree Some(cursor.as_ref().id)
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) (prev, next)
@ -142,111 +101,6 @@ impl ContentMap {
} }
} }
/// 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<'_>>) {
if let Some(cursor) = self.get(pos) {
let 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 => {
if cursor.as_ref().can_be_origin() {
let id = cursor.as_ref().id;
(
None,
Some(CursorWithId {
id,
cursor: cursor.unwrap(),
}),
)
} else {
(None, None)
}
}
Position::Middle => {
if cursor.as_ref().can_be_origin() {
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(),
}),
)
} else {
(None, None)
}
}
Position::End => {
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, None)
}
}
_ => {
unreachable!()
}
};
if prev.is_none() {
let mut prev_cursor = cursor.prev_elem();
while let Some(prev_inner) = prev_cursor {
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();
while let Some(next_inner) = next_cursor {
if next_inner.as_ref().status.is_activated() {
let mut cursor = next_inner.unwrap();
cursor.offset = 0;
cursor.pos = Position::Start;
next = Some(CursorWithId {
id: next_inner.as_ref().id,
cursor,
});
break;
}
next_cursor = next_inner.next_elem_start();
}
}
(prev, next)
} else {
(None, None)
}
}
pub fn get_id_spans(&self, pos: usize, len: usize) -> RleVec<IdSpan> { pub fn get_id_spans(&self, pos: usize, len: usize) -> RleVec<IdSpan> {
let mut ans = RleVec::new(); let mut ans = RleVec::new();
for cursor in self.iter_range(pos, Some(pos + len)) { for cursor in self.iter_range(pos, Some(pos + len)) {

View file

@ -238,6 +238,8 @@ pub mod fuzz {
}; };
if aa != bb { if aa != bb {
dbg!(a.client_id);
dbg!(b.client_id);
dbg!(aa.vec()); dbg!(aa.vec());
dbg!(bb.vec()); dbg!(bb.vec());
// dbg!(&a.content); // dbg!(&a.content);
@ -306,69 +308,295 @@ pub mod fuzz {
5, 5,
100, 100,
vec![ vec![
Delete { NewOp {
client_id: 252, client_id: 177,
pos: 252, pos: 41,
len: 252,
}, },
Delete { Delete {
client_id: 39, client_id: 255,
pos: 252, pos: 255,
len: 178, len: 255,
},
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 { NewOp {
client_id: 145, client_id: 162,
pos: 252, pos: 162,
}, },
Delete { Delete {
client_id: 252, client_id: 255,
pos: 178, pos: 255,
len: 252, len: 229,
},
Delete {
client_id: 150,
pos: 227,
len: 1,
},
NewOp {
client_id: 255,
pos: 255,
},
Delete {
client_id: 255,
pos: 255,
len: 0,
},
Sync { from: 0, to: 0 },
Sync { from: 0, to: 48 },
Sync { from: 0, to: 0 },
NewOp {
client_id: 150,
pos: 150,
},
Delete {
client_id: 63,
pos: 229,
len: 229,
},
Delete {
client_id: 175,
pos: 175,
len: 255,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 229,
pos: 229,
len: 229,
},
NewOp {
client_id: 58,
pos: 255,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Sync { from: 48, to: 175 },
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 175,
pos: 175,
len: 229,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 229,
},
Delete {
client_id: 229,
pos: 114,
len: 223,
},
Delete {
client_id: 255,
pos: 229,
len: 255,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 175,
pos: 175,
len: 175,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 237,
pos: 237,
len: 237,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
},
Delete {
client_id: 196,
pos: 150,
len: 150,
},
Sync { from: 162, to: 162 },
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
Sync { from: 81, to: 162 },
Sync { from: 0, to: 0 },
Sync { from: 162, to: 162 },
Sync { from: 0, to: 0 },
Sync { from: 0, to: 0 },
Sync { from: 0, to: 162 },
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 233,
pos: 162,
},
NewOp {
client_id: 150,
pos: 150,
},
Delete {
client_id: 255,
pos: 255,
len: 255,
}, },
Sync { from: 0, to: 0 }, Sync { from: 0, to: 0 },
Delete { Delete {
client_id: 252, client_id: 175,
pos: 178, pos: 255,
len: 178, len: 255,
}, },
Delete { Delete {
client_id: 252, client_id: 229,
pos: 252, pos: 229,
len: 252, len: 196,
}, },
NewOp { NewOp {
client_id: 252, client_id: 162,
pos: 252, pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 162,
},
NewOp {
client_id: 162,
pos: 1,
},
NewOp {
client_id: 162,
pos: 255,
},
Delete {
client_id: 229,
pos: 229,
len: 229,
},
Sync { from: 255, to: 255 },
Sync { from: 255, to: 162 },
NewOp {
client_id: 162,
pos: 162,
}, },
], ],
) )
@ -377,69 +605,31 @@ pub mod fuzz {
#[test] #[test]
fn normalize() { fn normalize() {
let mut actions = vec![ let mut actions = vec![
Delete { NewOp {
client_id: 252, client_id: 129,
pos: 252, pos: 142,
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 { NewOp {
client_id: 145, client_id: 0,
pos: 252, pos: 85,
}, },
Delete { Sync { from: 85, to: 86 },
client_id: 252, NewOp {
pos: 178, client_id: 129,
len: 252, pos: 129,
}, },
Sync { from: 0, to: 0 }, Sync { from: 129, to: 129 },
Delete { NewOp {
client_id: 252, client_id: 106,
pos: 178, pos: 106,
len: 178,
},
Delete {
client_id: 252,
pos: 252,
len: 252,
}, },
NewOp { NewOp {
client_id: 252, client_id: 1,
pos: 252, pos: 0,
},
NewOp {
client_id: 129,
pos: 106,
}, },
]; ];