diff --git a/crates/fuzz/tests/test.rs b/crates/fuzz/tests/test.rs index 781b57e7..71fec2e5 100644 --- a/crates/fuzz/tests/test.rs +++ b/crates/fuzz/tests/test.rs @@ -8240,6 +8240,239 @@ fn diff_calc_fuzz_err_2() { ) } +#[test] +fn diff_calc_fuzz_err_3() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331137), + bool: true, + key: 286331153, + pos: 1229782938247303443, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 243, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332389), + bool: true, + key: 4294967057, + pos: 0, + length: 1229782938247303461, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: false, + key: 0, + pos: 2676586395008827392, + length: 2676586395008836901, + prop: 17160162796632352037, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(623191333), + bool: true, + key: 623191333, + pos: 40841467208997, + length: 1290863008193515793, + prop: 2676586395008836881, + }), + }, + Handle { + site: 17, + target: 17, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(238)), + bool: false, + key: 286331153, + pos: 1229782938247303441, + length: 1230021532270531537, + prop: 2676586395008836901, + }), + }, + Handle { + site: 17, + target: 0, + container: 37, + action: Generic(GenericAction { + value: I32(286386705), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1229782941116208017, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229775241665909130, + length: 1229782938247303441, + prop: 2676586395008836901, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(285212709), + bool: true, + key: 286331153, + pos: 1229782938247303658, + length: 1229782938247303441, + prop: 1229782938247303953, + }), + }, + Sync { from: 17, to: 17 }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 725379779989737745, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1277915159264825617, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 287904017, + pos: 18764998447377, + length: 2676586395008827392, + prop: 2676586395008836901, + }), + }, + Handle { + site: 37, + target: 37, + container: 238, + action: Generic(GenericAction { + value: I32(623191333), + bool: true, + key: 623191333, + pos: 2676586395008836901, + length: 17160162796632352037, + prop: 2676586395008836901, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286265617, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 138, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303658, + length: 1229782938247303441, + prop: 1229782938247303953, + }), + }, + Sync { from: 17, to: 17 }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1229782938280857873, + prop: 1277915159264825617, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1236538337688359185, + prop: 18764998447377, + }), + }, + ], + ) +} + #[test] fn minify() { minify_error( diff --git a/crates/loro-internal/benches/tree.rs b/crates/loro-internal/benches/tree.rs index 9cf7336d..a0e94f35 100644 --- a/crates/loro-internal/benches/tree.rs +++ b/crates/loro-internal/benches/tree.rs @@ -56,6 +56,7 @@ mod tree { group.bench_function("1000 node checkout 10^3", |b| { let loro = LoroDoc::default(); + loro.start_auto_commit(); let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; @@ -85,6 +86,7 @@ mod tree { group.bench_function("300 deep node random checkout 10^3", |b| { let depth = 300; let loro = LoroDoc::default(); + loro.start_auto_commit(); let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; @@ -109,6 +111,8 @@ mod tree { group.bench_function("realtime tree move", |b| { let doc_a = LoroDoc::default(); let doc_b = LoroDoc::default(); + doc_a.start_auto_commit(); + doc_b.start_auto_commit(); let tree_a = doc_a.get_tree("tree"); let tree_b = doc_b.get_tree("tree"); let mut ids = vec![]; diff --git a/crates/loro-internal/src/state/tree_state.rs b/crates/loro-internal/src/state/tree_state.rs index 77c26e75..a7edc34f 100644 --- a/crates/loro-internal/src/state/tree_state.rs +++ b/crates/loro-internal/src/state/tree_state.rs @@ -882,16 +882,37 @@ impl ContainerState for TreeState { }); } TreeInternalDiff::Move { parent, position } => { - let ok = if need_check { - self.mov(target, *parent, last_move_op, Some(position.clone()), true) + if need_check { + let was_alive = !self.is_node_deleted(&target); + if self + .mov(target, *parent, last_move_op, Some(position.clone()), true) .is_ok() + { + if self.is_node_deleted(&target) { + if was_alive { + // delete event + ans.push(TreeDiffItem { + target, + action: TreeExternalDiff::Delete, + }); + } + // Otherwise, it's a normal move inside deleted nodes, no event is needed + } else { + // normal move + ans.push(TreeDiffItem { + target, + action: TreeExternalDiff::Move { + parent: parent.into_node().ok(), + index: self.get_index_by_tree_id(&target).unwrap(), + position: position.clone(), + }, + }); + } + } } else { self.mov(target, *parent, last_move_op, Some(position.clone()), false) .unwrap(); - true - }; - if ok { let index = self.get_index_by_tree_id(&target).unwrap(); ans.push(TreeDiffItem { target, @@ -901,15 +922,22 @@ impl ContainerState for TreeState { position: position.clone(), }, }); - } + }; } TreeInternalDiff::Delete { parent, position } => { + let mut send_event = true; + if need_check && self.is_node_deleted(&target) { + send_event = false; + } + self.mov(target, *parent, last_move_op, position.clone(), false) .unwrap(); - ans.push(TreeDiffItem { - target, - action: TreeExternalDiff::Delete, - }); + if send_event { + ans.push(TreeDiffItem { + target, + action: TreeExternalDiff::Delete, + }); + } } TreeInternalDiff::MoveInDelete { parent, position } => { self.mov(target, *parent, last_move_op, position.clone(), false)