diff --git a/crates/loro-core/justfile b/crates/loro-core/justfile index 056a927b..e08f3026 100644 --- a/crates/loro-core/justfile +++ b/crates/loro-core/justfile @@ -18,7 +18,7 @@ crev: cargo crev crate check 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: cargo flamegraph --example test --features=fuzzing --root diff --git a/crates/loro-core/src/container/text/tracker/content_map.rs b/crates/loro-core/src/container/text/tracker/content_map.rs index 527ca88f..c3c80bea 100644 --- a/crates/loro-core/src/container/text/tracker/content_map.rs +++ b/crates/loro-core/src/container/text/tracker/content_map.rs @@ -1,7 +1,7 @@ use std::ops::{Deref, DerefMut}; use rle::{ - rle_tree::{Position, SafeCursor, SafeCursorMut, UnsafeCursor}, + rle_tree::{Position, SafeCursor, SafeCursorMut}, HasLength, RleTree, RleVec, }; @@ -16,28 +16,20 @@ use super::y_span::{StatusChange, YSpan, YSpanTreeTrait}; #[derive(Debug, Default)] pub(super) struct ContentMap(RleTree); -struct CursorWithId<'tree> { - id: ID, - cursor: UnsafeCursor<'tree, YSpan, YSpanTreeTrait>, -} - impl ContentMap { #[inline] 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 { - origin_left: left.as_ref().map(|x| x.id), - origin_right: right.as_ref().map(|x| x.id), + origin_left: left, + origin_right: right, id, len, status: Default::default(), } } - fn get_sibling_at_dumb( - &self, - pos: usize, - ) -> (Option>, Option>) { + fn get_sibling_at(&self, pos: usize) -> (Option, Option) { if let Some(cursor) = self.get(pos) { let mut cursor: SafeCursor<'_, YSpan, YSpanTreeTrait> = // 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(); prev_offset_cursor.offset -= 1; if cursor.as_ref().can_be_origin() { - return ( - Some(CursorWithId { - id: id.inc(offset as i32 - 1), - cursor: prev_offset_cursor, - }), - Some(CursorWithId { - id: id.inc(offset as i32), - cursor: cursor.unwrap(), - }), - ); + return (Some(id.inc(offset as i32 - 1)), Some(id.inc(offset as i32))); } else { None } @@ -70,10 +53,7 @@ impl ContentMap { 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, - }) + Some(cursor.as_ref().last_id()) } else { None } @@ -93,10 +73,7 @@ impl ContentMap { let mut cursor = cursor.unwrap(); cursor.offset = offset; cursor.pos = Position::Middle; - prev = Some(CursorWithId { - id: prev_inner.as_ref().last_id(), - cursor, - }); + prev = Some(prev_inner.as_ref().last_id()); break; } prev_cursor = prev_inner.prev_elem(); @@ -104,36 +81,18 @@ impl ContentMap { } 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; - 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; - ans = Some(CursorWithId { - id: next_inner.as_ref().id, - cursor, - }); - break; - } - - next_cursor = next_inner.next_elem_start(); + if let Some(next_inner) = next_cursor { + let mut cursor = next_inner.unwrap(); + cursor.offset = 0; + cursor.pos = Position::Start; + ans = Some(next_inner.as_ref().id); } ans } else { - // if prev is none, next should be the first element in the tree - 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(), - }) + Some(cursor.as_ref().id) }; (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>, Option>) { - 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 { let mut ans = RleVec::new(); for cursor in self.iter_range(pos, Some(pos + len)) { diff --git a/crates/loro-core/src/container/text/tracker/yata.rs b/crates/loro-core/src/container/text/tracker/yata.rs index 81eb8874..ecd15a11 100644 --- a/crates/loro-core/src/container/text/tracker/yata.rs +++ b/crates/loro-core/src/container/text/tracker/yata.rs @@ -238,6 +238,8 @@ pub mod fuzz { }; if aa != bb { + dbg!(a.client_id); + dbg!(b.client_id); dbg!(aa.vec()); dbg!(bb.vec()); // dbg!(&a.content); @@ -306,69 +308,295 @@ pub mod fuzz { 5, 100, vec![ - Delete { - client_id: 252, - pos: 252, - len: 252, + NewOp { + client_id: 177, + pos: 41, }, 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, + client_id: 255, + pos: 255, + len: 255, }, NewOp { - client_id: 145, - pos: 252, + client_id: 162, + pos: 162, }, Delete { - client_id: 252, - pos: 178, - len: 252, + client_id: 255, + pos: 255, + 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 }, Delete { - client_id: 252, - pos: 178, - len: 178, + client_id: 175, + pos: 255, + len: 255, }, Delete { - client_id: 252, - pos: 252, - len: 252, + client_id: 229, + pos: 229, + len: 196, }, NewOp { - client_id: 252, - pos: 252, + client_id: 162, + 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] fn normalize() { let mut actions = vec![ - Delete { - client_id: 252, - pos: 252, - 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 { + client_id: 129, + pos: 142, }, NewOp { - client_id: 145, - pos: 252, + client_id: 0, + pos: 85, }, - Delete { - client_id: 252, - pos: 178, - len: 252, + Sync { from: 85, to: 86 }, + NewOp { + client_id: 129, + pos: 129, }, - Sync { from: 0, to: 0 }, - Delete { - client_id: 252, - pos: 178, - len: 178, - }, - Delete { - client_id: 252, - pos: 252, - len: 252, + Sync { from: 129, to: 129 }, + NewOp { + client_id: 106, + pos: 106, }, NewOp { - client_id: 252, - pos: 252, + client_id: 1, + pos: 0, + }, + NewOp { + client_id: 129, + pos: 106, }, ];