From 90470658435ec4c62b5af59ebb82fe9e1f5aa761 Mon Sep 17 00:00:00 2001 From: Zixuan Chen Date: Thu, 4 Jul 2024 18:15:44 +0800 Subject: [PATCH] Fix undo with checkout (#375) * fix: should transform checkout event * chore: update fuzz dep * chore: add pos to error info * fix: clear undo/redo stack when checkingout * test: update fuzz dep * test: a new failed test case * fix: tree transform * chore: fuzz * chore: add log * chore: add more logs * fix: compose err * chore: fuzz test dep * test: a failed tree case * fix: undo tree event * fix: do not compare tree position in fuzz * fix: fuzz rev * test: a failed tree case * fix: add tree compose * chore: add comment * chore: fuzz * fix: test * fix: tree transform * fix: tree transform index * fix: sort tree index * chore: fuzz * fix: undo/redo remote change effect compose * bk * fix: tree undo redo (#385) * fix: event hint none * chore: fuzz version * ci: fuzz * bk: weird err * fix: type err * fix: fractional index between * fix: wasm counter feature * test: a new failed case * fix: recursively create child nodes * fix: filter empty event * bk * bk * fix: tree undo redo remap * chore: clean * bk * fix: tree need remap first * fix: tree undo effect * fix: tree diff calc * fix: tree fuzz check eq func * fix: remove EventHint None * chore: cargo fix * fix: tree uncreate * fix: fuzz tree assert only structure * refactor: rename methods * fix: movable tree apply delta * fix: another movable list issue * chore: fuzz only check 1 actor's history --------- Co-authored-by: Leon Zhao --- Cargo.lock | 29 +- crates/fractional_index/src/lib.rs | 2 +- crates/fuzz/Cargo.toml | 15 +- crates/fuzz/fuzz/Cargo.lock | 30 +- crates/fuzz/src/actor.rs | 228 +- crates/fuzz/src/container/tree.rs | 6 +- crates/fuzz/src/crdt_fuzzer.rs | 175 +- crates/fuzz/tests/test.rs | 2065 +++++++- crates/fuzz/tests/undo.rs | 4325 ++++++++++++++++- crates/loro-common/src/error.rs | 9 +- crates/loro-internal/benches/tree.rs | 34 +- crates/loro-internal/examples/tree.rs | 13 +- crates/loro-internal/src/container.rs | 9 +- crates/loro-internal/src/delta/tree.rs | 155 +- crates/loro-internal/src/diff_calc/tree.rs | 4 +- crates/loro-internal/src/event.rs | 8 + crates/loro-internal/src/handler.rs | 236 +- crates/loro-internal/src/handler/tree.rs | 238 +- crates/loro-internal/src/loro.rs | 8 +- crates/loro-internal/src/obs.rs | 1 - crates/loro-internal/src/state.rs | 82 +- crates/loro-internal/src/state/tree_state.rs | 110 +- crates/loro-internal/src/txn.rs | 15 +- crates/loro-internal/src/undo.rs | 62 +- crates/loro-internal/tests/test.rs | 25 +- crates/loro-wasm/Cargo.toml | 3 +- crates/loro-wasm/scripts/build.ts | 2 +- crates/loro-wasm/src/convert.rs | 9 +- crates/loro-wasm/src/lib.rs | 29 +- crates/loro/src/lib.rs | 2 + .../loro/tests/integration_test/undo_test.rs | 3 +- loro-js/src/index.ts | 2 +- loro-js/tests/tree.test.ts | 10 +- 33 files changed, 7453 insertions(+), 491 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 74d59705..396cfa40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,8 +661,7 @@ dependencies = [ "fxhash", "itertools 0.12.1", "loro 0.16.2", - "loro 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", - "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", + "loro 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "rand", "serde_json", "tabled 0.10.0", @@ -999,13 +998,13 @@ dependencies = [ [[package]] name = "loro" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "either", "enum-as-inner 0.6.0", "generic-btree", - "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", - "loro-internal 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", + "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-internal 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "tracing", ] @@ -1029,12 +1028,12 @@ dependencies = [ [[package]] name = "loro-common" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "arbitrary", "enum-as-inner 0.6.0", "fxhash", - "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", + "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "nonmax", "serde", "serde_columnar", @@ -1061,7 +1060,7 @@ dependencies = [ [[package]] name = "loro-delta" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "arrayvec", "enum-as-inner 0.5.1", @@ -1124,7 +1123,7 @@ dependencies = [ [[package]] name = "loro-internal" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "append-only-bytes", "arref", @@ -1137,10 +1136,10 @@ dependencies = [ "im", "itertools 0.12.1", "leb128", - "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", - "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", - "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", - "loro_fractional_index 0.16.2 (git+https://github.com/loro-dev/loro.git?branch=main)", + "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro_fractional_index 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "md5", "num", "num-derive", @@ -1176,7 +1175,7 @@ dependencies = [ [[package]] name = "loro-rle" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "append-only-bytes", "arref", @@ -1225,7 +1224,7 @@ dependencies = [ [[package]] name = "loro_fractional_index" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?branch=main#afac34755f3f9a099c2985ff22dc9f2534d72290" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "imbl", "rand", diff --git a/crates/fractional_index/src/lib.rs b/crates/fractional_index/src/lib.rs index 84cc525d..984ca550 100644 --- a/crates/fractional_index/src/lib.rs +++ b/crates/fractional_index/src/lib.rs @@ -74,7 +74,7 @@ pub(crate) fn new_after(bytes: &[u8]) -> Vec { } pub(crate) fn new_between(left: &[u8], right: &[u8], extra_capacity: usize) -> Option> { - let shorter_len = left.len().min(right.len()); + let shorter_len = left.len().min(right.len()) - 1; for i in 0..shorter_len { if left[i] < right[i] - 1 { let mut ans: Vec = left[0..=i].into(); diff --git a/crates/fuzz/Cargo.toml b/crates/fuzz/Cargo.toml index 8221f58f..ebaa88b1 100644 --- a/crates/fuzz/Cargo.toml +++ b/crates/fuzz/Cargo.toml @@ -7,18 +7,8 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -loro-without-counter = { path = "../loro", package = "loro" } -loro = { git = "https://github.com/loro-dev/loro.git", features = [ - "counter", -], branch = "main" } -loro-common = { git = "https://github.com/loro-dev/loro.git", features = [ - "counter", -], branch = "main" } -# loro = { path = "../loro", package = "loro", features = ["counter"] } -# loro-common = { path = "../loro-common", package = "loro-common", features = [ -# "counter", -# ] } -# loro-without-counter = { git = "https://github.com/loro-dev/loro.git", branch = "main", package = "loro" } +loro = { path = "../loro", features = ["counter"], package = "loro" } +loro-without-counter = { git = "https://github.com/loro-dev/loro.git", rev = "39caa6cedf66d8f6c77f0886e70ec1270a96bd1a", package = "loro", default-features = false } fxhash = { workspace = true } enum_dispatch = { workspace = true } enum-as-inner = { workspace = true } @@ -27,6 +17,7 @@ itertools = { workspace = true } arbitrary = "1" tabled = "0.10" rand = "0.8.5" +serde_json = "1" [dev-dependencies] ctor = "0.2" diff --git a/crates/fuzz/fuzz/Cargo.lock b/crates/fuzz/fuzz/Cargo.lock index 7f514917..cdc222b2 100644 --- a/crates/fuzz/fuzz/Cargo.lock +++ b/crates/fuzz/fuzz/Cargo.lock @@ -216,9 +216,9 @@ dependencies = [ "fxhash", "itertools 0.12.1", "loro 0.16.2", - "loro 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", - "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", + "loro 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "rand", + "serde_json", "tabled", "tracing", ] @@ -436,13 +436,13 @@ dependencies = [ [[package]] name = "loro" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "either", "enum-as-inner 0.6.0", "generic-btree", - "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", - "loro-internal 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", + "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-internal 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "tracing", ] @@ -464,12 +464,12 @@ dependencies = [ [[package]] name = "loro-common" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "arbitrary", "enum-as-inner 0.6.0", "fxhash", - "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", + "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "nonmax", "serde", "serde_columnar", @@ -491,7 +491,7 @@ dependencies = [ [[package]] name = "loro-delta" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "arrayvec", "enum-as-inner 0.5.1", @@ -537,7 +537,7 @@ dependencies = [ [[package]] name = "loro-internal" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "append-only-bytes", "arref", @@ -550,10 +550,10 @@ dependencies = [ "im", "itertools 0.12.1", "leb128", - "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", - "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", - "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", - "loro_fractional_index 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9)", + "loro-common 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-delta 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro-rle 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", + "loro_fractional_index 0.16.2 (git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a)", "md5", "num", "num-derive", @@ -584,7 +584,7 @@ dependencies = [ [[package]] name = "loro-rle" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "append-only-bytes", "arref", @@ -613,7 +613,7 @@ dependencies = [ [[package]] name = "loro_fractional_index" version = "0.16.2" -source = "git+https://github.com/loro-dev/loro.git?rev=83938290ab2666d85c0c72169127611585a05cf9#83938290ab2666d85c0c72169127611585a05cf9" +source = "git+https://github.com/loro-dev/loro.git?rev=39caa6cedf66d8f6c77f0886e70ec1270a96bd1a#39caa6cedf66d8f6c77f0886e70ec1270a96bd1a" dependencies = [ "imbl", "rand", diff --git a/crates/fuzz/src/actor.rs b/crates/fuzz/src/actor.rs index 8a620bab..f7f49644 100644 --- a/crates/fuzz/src/actor.rs +++ b/crates/fuzz/src/actor.rs @@ -1,11 +1,13 @@ use std::{ + collections::VecDeque, fmt::{Debug, Formatter}, sync::{Arc, Mutex}, }; use enum_as_inner::EnumAsInner; use enum_dispatch::enum_dispatch; -use fxhash::FxHashMap; +use fxhash::{FxHashMap, FxHashSet}; +use itertools::Itertools; use loro::{ Container, ContainerID, ContainerType, Frontiers, LoroDoc, LoroValue, PeerID, UndoManager, ID, }; @@ -52,7 +54,7 @@ impl Actor { info_span!("ApplyDiff", id = id).in_scope(|| { let mut tracker = cb_tracker.lock().unwrap(); tracker.apply_diff(e) - }) + }); })); let mut default_history = FxHashMap::default(); default_history.insert(Vec::new(), loro.get_deep_value()); @@ -123,28 +125,20 @@ impl Actor { pub fn undo(&mut self, undo_length: u32) { self.loro.attach(); - let mut before_undo = self.loro.get_deep_value(); + let before_undo = self.loro.get_deep_value(); + + // println!("\n\nstart undo\n"); for _ in 0..undo_length { self.undo_manager.undo.undo(&self.loro).unwrap(); } + // println!("\n\nstart redo\n"); for _ in 0..undo_length { self.undo_manager.undo.redo(&self.loro).unwrap(); } - let mut after_undo = self.loro.get_deep_value(); - Self::patch_tree_undo_position(&mut before_undo); - Self::patch_tree_undo_position(&mut after_undo); - assert_value_eq(&before_undo, &after_undo); - } - fn patch_tree_undo_position(a: &mut LoroValue) { - let root = Arc::make_mut(a.as_map_mut().unwrap()); - let tree = root.get_mut("tree").unwrap(); - let nodes = Arc::make_mut(tree.as_list_mut().unwrap()); - for node in nodes.iter_mut() { - let node = Arc::make_mut(node.as_map_mut().unwrap()); - node.remove("position"); - } + let after_undo = self.loro.get_deep_value(); + assert_value_eq(&before_undo, &after_undo); } pub fn check_tracker(&self) { @@ -347,6 +341,47 @@ pub trait ActorTrait { } pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) { + fn eq_without_position(a: &LoroValue, b: &LoroValue) -> bool { + match (a, b) { + (LoroValue::Map(a), LoroValue::Map(b)) => { + for (k, v) in a.iter() { + if k == "position" { + continue; + } + + if !eq_without_position(v, b.get(k).unwrap_or(&LoroValue::I64(0))) { + return false; + } + } + + for (k, v) in b.iter() { + if k == "position" { + continue; + } + if !eq_without_position(v, a.get(k).unwrap_or(&LoroValue::I64(0))) { + return false; + } + } + true + } + (LoroValue::List(a), LoroValue::List(b)) => { + if a.len() != b.len() { + return false; + } + + if is_tree_values(a.as_ref()) { + assert_tree_value_eq(a, b); + true + } else { + a.iter() + .zip(b.iter()) + .all(|(a, b)| eq_without_position(a, b)) + } + } + (a, b) => a == b, + } + } + #[must_use] fn eq(a: &LoroValue, b: &LoroValue) -> bool { match (a, b) { @@ -385,7 +420,15 @@ pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) { true } - (a, b) => a == b, + (LoroValue::List(a_list), LoroValue::List(b_list)) => { + if is_tree_values(a_list.as_ref()) { + assert_tree_value_eq(a_list, b_list); + true + } else { + eq_without_position(a, b) + } + } + (a, b) => eq_without_position(a, b), } } assert!( @@ -395,3 +438,154 @@ pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) { b ); } + +pub fn is_tree_values(value: &[LoroValue]) -> bool { + if let Some(LoroValue::Map(map)) = value.first() { + let map_keys = map.as_ref().keys().cloned().collect::>(); + return map_keys.contains("id") + && map_keys.contains("parent") + && map_keys.contains("meta") + && map_keys.contains("position"); + } + false +} +#[derive(Debug, Clone)] +struct Node { + children: Vec, + meta: FxHashMap, + position: String, +} + +struct FlatNode { + id: String, + parent: Option, + meta: FxHashMap, + index: usize, + position: String, +} + +impl FlatNode { + fn from_loro_value(value: &LoroValue) -> Self { + let map = value.as_map().unwrap(); + let id = map.get("id").unwrap().as_string().unwrap().to_string(); + let parent = map + .get("parent") + .unwrap() + .as_string() + .map_or(None, |x| Some(x.to_string())); + + let meta = map.get("meta").unwrap().as_map().unwrap().as_ref().clone(); + let index = *map.get("index").unwrap().as_i64().unwrap() as usize; + let position = map + .get("position") + .unwrap() + .as_string() + .unwrap() + .to_string(); + FlatNode { + id, + parent, + meta, + index, + position, + } + } +} + +impl Node { + fn from_loro_value(value: &[LoroValue]) -> Vec { + let mut node_map = FxHashMap::default(); + let mut parent_child_map = FxHashMap::default(); + + // 首先,将所有扁平节点转换为TreeNode,并存储在HashMap中以便快速查找 + for flat_node in value.iter() { + let flat_node = FlatNode::from_loro_value(flat_node); + let tree_node = Node { + // id: flat_node.id.clone(), + // parent: flat_node.parent.clone(), + children: vec![], + meta: flat_node.meta, + // index: flat_node.index, + position: flat_node.position, + }; + + node_map.insert(flat_node.id.clone(), tree_node); + + parent_child_map + .entry(flat_node.parent) + .or_insert_with(Vec::new) + .push((flat_node.index, flat_node.id)); + } + let mut node_map_clone = node_map.clone(); + for (parent_id, child_ids) in parent_child_map.iter() { + if let Some(parent_id) = parent_id { + if let Some(parent_node) = node_map.get_mut(parent_id) { + for (_, child_id) in child_ids.into_iter().sorted_by_key(|x| x.0) { + if let Some(child_node) = node_map_clone.remove(child_id) { + parent_node.children.push(child_node); + } + } + } + } + } + + parent_child_map.get(&None).map_or(vec![], |root_ids| { + root_ids + .iter() + .filter_map(|(_i, id)| node_map.remove(id)) + .collect::>() + }) + } +} + +pub fn assert_tree_value_eq(a: &[LoroValue], b: &[LoroValue]) { + let a_tree = Node::from_loro_value(a); + let b_tree = Node::from_loro_value(b); + let mut a_q = VecDeque::from_iter([a_tree]); + let mut b_q = VecDeque::from_iter([b_tree]); + while let (Some(a_node), Some(b_node)) = (a_q.pop_front(), b_q.pop_front()) { + let mut children_a = vec![]; + let mut children_b = vec![]; + let a_meta = a_node + .into_iter() + .map(|x| { + children_a.extend(x.children); + let mut meta = x + .meta + .into_iter() + .sorted_by_key(|(k, _)| k.clone()) + .map(|(mut k, v)| { + k.push_str(v.as_string().map_or("", |f| f.as_str())); + k + }) + .collect::(); + meta.push_str(&x.position); + meta + }) + .collect::>(); + let b_meta = b_node + .into_iter() + .map(|x| { + children_b.extend(x.children); + let mut meta = x + .meta + .into_iter() + .sorted_by_key(|(k, _)| k.clone()) + .map(|(mut k, v)| { + k.push_str(v.as_string().map_or("", |f| f.as_str())); + k + }) + .collect::(); + meta.push_str(&x.position); + meta + }) + .collect::>(); + assert!(a_meta.difference(&b_meta).count() == 0); + assert_eq!(children_a.len(), children_b.len()); + if children_a.is_empty() { + continue; + } + a_q.push_back(children_a); + b_q.push_back(children_b); + } +} diff --git a/crates/fuzz/src/container/tree.rs b/crates/fuzz/src/container/tree.rs index 724bde03..cf91417d 100644 --- a/crates/fuzz/src/container/tree.rs +++ b/crates/fuzz/src/container/tree.rs @@ -168,11 +168,11 @@ impl Actionable for TreeAction { *target = (id.peer, id.counter); } TreeActionInner::Delete => { - let target_index = target.1 as usize % node_num; + let target_index = target.0 as usize % node_num; *target = (nodes[target_index].peer, nodes[target_index].counter); } TreeActionInner::Move { parent, index } => { - let target_index = target.1 as usize % node_num; + let target_index = target.0 as usize % node_num; *target = (nodes[target_index].peer, nodes[target_index].counter); let mut parent_idx = parent.0 as usize % node_num; while target_index == parent_idx { @@ -202,7 +202,7 @@ impl Actionable for TreeAction { *c = nodes[other_idx].counter; } TreeActionInner::Meta { meta: (_, v) } => { - let target_index = target.1 as usize % node_num; + let target_index = target.0 as usize % node_num; *target = (nodes[target_index].peer, nodes[target_index].counter); if matches!(v, FuzzValue::Container(_)) { *v = FuzzValue::I32(0); diff --git a/crates/fuzz/src/crdt_fuzzer.rs b/crates/fuzz/src/crdt_fuzzer.rs index 38c14fb2..22a0d8dd 100644 --- a/crates/fuzz/src/crdt_fuzzer.rs +++ b/crates/fuzz/src/crdt_fuzzer.rs @@ -1,4 +1,7 @@ -use std::fmt::{Debug, Display}; +use std::{ + fmt::{Debug, Display}, + time::Instant, +}; use arbitrary::Arbitrary; use fxhash::FxHashSet; @@ -241,9 +244,10 @@ impl CRDTFuzzer { } fn check_history(&mut self) { - for actor in self.actors.iter_mut() { - actor.check_history(); - } + self.actors[0].check_history(); + // for actor in self.actors.iter_mut() { + // actor.check_history(); + // } } fn site_num(&self) -> usize { @@ -302,16 +306,177 @@ pub fn test_multi_sites(site_num: u8, fuzz_targets: Vec, actions: &m let mut applied = Vec::new(); for action in actions.iter_mut() { fuzzer.pre_process(action); - info_span!("ApplyAction", ?action).in_scope(|| { applied.push(action.clone()); info!("OptionsTable \n{}", (&applied).table()); fuzzer.apply_action(action); }); } + let span = &info_span!("check synced"); let _g = span.enter(); fuzzer.check_equal(); fuzzer.check_tracker(); fuzzer.check_history(); } + +pub fn minify_error(site_num: u8, f: F, normalize: N, actions: Vec) +where + F: Fn(u8, &mut [T]), + N: Fn(u8, &mut [T]) -> Vec, + T: Clone + Debug, +{ + std::panic::set_hook(Box::new(|_info| { + // ignore panic output + // println!("{:?}", _info); + })); + + let f_ref: *const _ = &f; + let f_ref: usize = f_ref as usize; + #[allow(clippy::redundant_clone)] + let mut actions_clone = actions.clone(); + let action_ref: usize = (&mut actions_clone) as *mut _ as usize; + #[allow(clippy::blocks_in_conditions)] + if std::panic::catch_unwind(|| { + // SAFETY: test + let f = unsafe { &*(f_ref as *const F) }; + // SAFETY: test + let actions_ref = unsafe { &mut *(action_ref as *mut Vec) }; + f(site_num, actions_ref); + }) + .is_ok() + { + println!("No Error Found"); + return; + } + + let mut minified = actions.clone(); + let mut candidates = Vec::new(); + println!("Setup candidates..."); + for i in 0..actions.len() { + let mut new = actions.clone(); + new.remove(i); + candidates.push(new); + } + + println!("Minifying..."); + let start = Instant::now(); + while let Some(candidate) = candidates.pop() { + let f_ref: *const _ = &f; + let f_ref: usize = f_ref as usize; + let mut actions_clone = candidate.clone(); + let action_ref: usize = (&mut actions_clone) as *mut _ as usize; + #[allow(clippy::blocks_in_conditions)] + if std::panic::catch_unwind(|| { + // SAFETY: test + let f = unsafe { &*(f_ref as *const F) }; + // SAFETY: test + let actions_ref = unsafe { &mut *(action_ref as *mut Vec) }; + f(site_num, actions_ref); + }) + .is_err() + { + for i in 0..candidate.len() { + let mut new = candidate.clone(); + new.remove(i); + candidates.push(new); + } + if candidate.len() < minified.len() { + minified = candidate; + println!("New min len={}", minified.len()); + } + if candidates.len() > 40 { + candidates.drain(0..30); + } + } + if start.elapsed().as_secs() > 10 && minified.len() <= 4 { + break; + } + if start.elapsed().as_secs() > 60 { + break; + } + } + + let minified = normalize(site_num, &mut minified); + println!( + "Old Length {}, New Length {}", + actions.len(), + minified.len() + ); + dbg!(&minified); + if actions.len() > minified.len() { + minify_error(site_num, f, normalize, minified); + } +} + +pub fn minify_simple(site_num: u8, f: F, normalize: N, actions: Vec) +where + F: Fn(u8, &mut [T]), + N: Fn(u8, &mut [T]) -> Vec, + T: Clone + Debug, +{ + std::panic::set_hook(Box::new(|_info| { + // ignore panic output + // println!("{:?}", _info); + })); + let f_ref: *const _ = &f; + let f_ref: usize = f_ref as usize; + #[allow(clippy::redundant_clone)] + let mut actions_clone = actions.clone(); + let action_ref: usize = (&mut actions_clone) as *mut _ as usize; + #[allow(clippy::blocks_in_conditions)] + if std::panic::catch_unwind(|| { + // SAFETY: test + let f = unsafe { &*(f_ref as *const F) }; + // SAFETY: test + let actions_ref = unsafe { &mut *(action_ref as *mut Vec) }; + f(site_num, actions_ref); + }) + .is_ok() + { + println!("No Error Found"); + return; + } + let mut minified = actions.clone(); + let mut current_index = minified.len() as i64 - 1; + while current_index > 0 { + let a = minified.remove(current_index as usize); + let f_ref: *const _ = &f; + let f_ref: usize = f_ref as usize; + let mut actions_clone = minified.clone(); + let action_ref: usize = (&mut actions_clone) as *mut _ as usize; + let mut re = false; + #[allow(clippy::blocks_in_conditions)] + if std::panic::catch_unwind(|| { + // SAFETY: test + let f = unsafe { &*(f_ref as *const F) }; + // SAFETY: test + let actions_ref = unsafe { &mut *(action_ref as *mut Vec) }; + f(site_num, actions_ref); + }) + .is_err() + { + re = true; + } else { + minified.insert(current_index as usize, a); + } + println!( + "{}/{} {}", + actions.len() as i64 - current_index, + actions.len(), + re + ); + current_index -= 1; + } + let minified = normalize(site_num, &mut minified); + + println!("{:?}", &minified); + println!( + "Old Length {}, New Length {}", + actions.len(), + minified.len() + ); + if actions.len() > minified.len() { + minify_simple(site_num, f, normalize, minified); + } +} diff --git a/crates/fuzz/tests/test.rs b/crates/fuzz/tests/test.rs index 73739092..4a4c85a8 100644 --- a/crates/fuzz/tests/test.rs +++ b/crates/fuzz/tests/test.rs @@ -6,7 +6,7 @@ use fuzz::{ GenericAction, }, container::{TreeAction, TreeActionInner}, - crdt_fuzzer::{test_multi_sites, Action::*, FuzzTarget, FuzzValue::*}, + crdt_fuzzer::{minify_error, test_multi_sites, Action::*, FuzzTarget, FuzzValue::*}, }; use loro::{ContainerType::*, LoroCounter, LoroDoc}; @@ -5644,3 +5644,2066 @@ fn undo_tree() { ], ) } + +#[test] +fn unknown_test() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 33, + target: 33, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819305, + pos: 1378419387125539105, + length: 7143833951692390400, + prop: 18158512740587612165, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 2165440511, + pos: 18446713166840815823, + length: 18446744073695002623, + prop: 9300496180473495551, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 88, + container: 33, + action: Generic(GenericAction { + value: I32(555819297), + bool: true, + key: 603922721, + pos: 18384011962329072995, + length: 2387470752459784191, + prop: 18157383382342902049, + }), + }, + SyncAll, + Handle { + site: 129, + target: 207, + container: 96, + action: Generic(GenericAction { + value: Container(Unknown(227)), + bool: true, + key: 570425343, + pos: 18446744073709551615, + length: 1252228849668718591, + prop: 18446744073707709423, + }), + }, + Undo { + site: 33, + op_len: 557850913, + }, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: I32(553656616), + bool: true, + key: 4294967295, + pos: 2387225703671136255, + length: 18157383382357244923, + prop: 9300496180473495547, + }), + }, + SyncAll, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: I32(-81714911), + bool: true, + key: 4227595259, + pos: 9222240656569400315, + length: 18157383382357508095, + prop: 18157383382357244923, + }), + }, + Handle { + site: 33, + target: 33, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4213252369, + pos: 18157383382357244923, + length: 1229782942188567547, + prop: 1229764366808715537, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 2846468521, + pos: 1808504320692955561, + length: 1808504320951916825, + prop: 1805689571184810265, + }), + }, + Checkout { + site: 59, + to: 421533977, + }, + SyncAllUndo { + site: 9, + op_len: 421075225, + }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(420419865), + bool: true, + key: 993737531, + pos: 1808504320951916859, + length: 1808504320951916825, + prop: 1808504320531568921, + }), + }, + Handle { + site: 25, + target: 126, + container: 25, + action: Generic(GenericAction { + value: I32(421075225), + bool: true, + key: 186194201, + pos: 3537886577862187264, + length: 4268070197444284185, + prop: 1808504320951916859, + }), + }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: Container(Unknown(251)), + bool: true, + key: 4227595259, + pos: 2387466337166031867, + length: 18446744069971452193, + prop: 1297036692682702847, + }), + }, + SyncAll, + SyncAllUndo { + site: 207, + op_len: 3824095584, + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: I32(-81714911), + bool: true, + key: 4227595259, + pos: 18157110720653425659, + length: 18157383382424616831, + prop: 18157383382357244923, + }), + }, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 555815423, + pos: 18157351368541544737, + length: 18158513695410093051, + prop: 18157383382357244923, + }), + }, + SyncAll, + Handle { + site: 33, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 291557213, + pos: 18384256508149097455, + length: 2449927290233290751, + prop: 2387226647993516031, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 301989887, + pos: 18157383382357188897, + length: 9222241721654180859, + prop: 2387226643631439871, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 3823363055, + pos: 18157383399589937123, + length: 18157383382357244923, + prop: 2387225703656586235, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 223, + container: 47, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4227596287, + pos: 18157383382357244923, + length: 2387225707328306171, + prop: 18446744073709494561, + }), + }, + SyncAll, + Handle { + site: 239, + target: 227, + container: 227, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 16776961, + length: 0, + prop: 0, + }), + }, + ], + ) +} + +#[test] +fn unknown_test_1() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 171, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 555876351, + pos: 1873497441278722081, + length: 18446680051950142745, + prop: 14251013405919093505, + }), + }, + Sync { from: 197, to: 197 }, + SyncAll, + Handle { + site: 25, + target: 25, + container: 197, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 589496831, + pos: 14251014049101066245, + length: 14251014049101104581, + prop: 1808504321235615743, + }), + }, + Handle { + site: 0, + target: 255, + container: 35, + action: Generic(GenericAction { + value: Container(Unknown(0)), + bool: false, + key: 93, + pos: 18388478750059857152, + length: 3540954258656722723, + prop: 14251014049104920573, + }), + }, + Sync { from: 197, to: 197 }, + Handle { + site: 25, + target: 231, + container: 230, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294952389, + pos: 14251013405919093505, + length: 18446680051950142917, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: I32(555819297), + bool: true, + key: 555819297, + pos: 2387225703656530209, + length: 18097157671754073472, + prop: 18446743085883913466, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Sync { from: 197, to: 255 }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(23325), + bool: false, + key: 587595548, + pos: 71875463170644529, + length: 102254863057045, + prop: 3602878813425696768, + }), + }, + Checkout { + site: 35, + to: 4281410559, + }, + Handle { + site: 35, + target: 49, + container: 255, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 427, + pos: 3535659958131563009, + length: 9259681213650633203, + prop: 2387225703656530209, + }), + }, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: Container(List), + bool: false, + key: 4278283372, + pos: 2387226295835536299, + length: 388193571007627297, + prop: 2387225703656544040, + }), + }, + Handle { + site: 33, + target: 33, + container: 51, + action: Generic(GenericAction { + value: I32(892744248), + bool: true, + key: 16055035, + pos: 2387242053152086529, + length: 2387225703656530209, + prop: 2387225703656530209, + }), + }, + SyncAllUndo { + site: 5, + op_len: 84215075, + }, + ], + ) +} + +#[test] +fn unknown_test_2() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 4280621851, + pos: 1953184669377757183, + length: 1953184666628070171, + prop: 71829045943205915, + }), + }, + Handle { + site: 251, + target: 197, + container: 255, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 16710579922159737627, + prop: 288230380914862055, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(387661595), + bool: false, + key: 454761243, + pos: 1953184666628070171, + length: 71829045943205915, + prop: 18430413027502194837, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 16710579922159737627, + length: 288230380914862055, + prop: 1953184666628070171, + }), + }, + Handle { + site: 63, + target: 27, + container: 23, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 1953184666628070171, + prop: 1953184666627808027, + }), + }, + // SyncAll, + // Handle { + // site: 27, + // target: 27, + // container: 27, + // action: Generic(GenericAction { + // value: I32(454761243), + // bool: true, + // key: 587202560, + // pos: 2499870512, + // length: 1955426454418212347, + // prop: 1953184666628070171, + // }), + // }, + // Handle { + // site: 27, + // target: 27, + // container: 27, + // action: Generic(GenericAction { + // value: I32(-404232217), + // bool: true, + // key: 18606055, + // pos: 1953184666626555904, + // length: 1953184666628070171, + // prop: 1953184666627808063, + // }), + // }, + // Handle { + // site: 27, + // target: 27, + // container: 27, + // action: Generic(GenericAction { + // value: I32(454761243), + // bool: false, + // key: 807600128, + // pos: 29802787832063, + // length: 163831513883392, + // prop: 2527082340907941888, + // }), + // }, + // Handle { + // site: 27, + // target: 27, + // container: 27, + // action: Generic(GenericAction { + // value: I32(-1920103141), + // bool: true, + // key: 2374864269, + // pos: 10199964370168810893, + // length: 10199964370168810893, + // prop: 10199964370168810893, + // }), + // }, + SyncAllUndo { + site: 141, + op_len: 2374864269, + }, + ], + ) +} + +#[test] +fn unknown_test_3() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 0, + target: 255, + container: 203, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 1, + prop: 506381209866536711, + }), + }, + Handle { + site: 7, + target: 7, + container: 255, + action: Generic(GenericAction { + value: I32(117901063), + bool: true, + key: 2332033031, + pos: 506381364485359499, + length: 17268955636171736839, + prop: 506381949047603367, + }), + }, + SyncAll, + Handle { + site: 3, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(117901063), + bool: true, + key: 4219143943, + pos: 539160350683790203, + length: 18446744073693102591, + prop: 506381188273799167, + }), + }, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 79017397, + pos: 18446744073558687743, + length: 18446471394817474559, + prop: 2821266740699193087, + }), + }, + Handle { + site: 0, + target: 49, + container: 54, + action: Generic(GenericAction { + value: I32(939524701), + bool: false, + key: 3419143984, + pos: 18388267869089287115, + length: 16492674416639, + prop: 18446744073709551360, + }), + }, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: I32(-75823353), + bool: true, + key: 2071690107, + pos: 18446468104864627579, + length: 47269824462061567, + prop: 18446743004391874983, + }), + }, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 656877351, + pos: 13053445094070757159, + length: 2199006786997, + prop: 576459871835127808, + }), + }, + SyncAll, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 656877351, + pos: 18385707052877424423, + length: 506381209866536193, + prop: 543681012144998151, + }), + }, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: I32(-1953824768), + bool: true, + key: 4281009927, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 7, + target: 7, + container: 255, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 117901235, + pos: 18376096049401366279, + length: 576460752303423487, + prop: 506381209866536706, + }), + }, + Undo { + site: 0, + op_len: 125533051, + }, + ], + ) +} + +#[test] +fn b_delete_parent_tree_undo() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 4, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 1953184666628070171, + prop: 1953184666560961307, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 5, + pos: 163831513883392, + length: 2527082340907941888, + prop: 1953184666628070171, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(-404232421), + bool: true, + key: 3890735079, + pos: 16710579925595711463, + length: 16710579925595711463, + prop: 16710579925595711463, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(741092379), + bool: false, + key: 875901996, + pos: 56299273401911, + length: 2527082340907941888, + prop: 1953184666643014442, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331155), + bool: true, + key: 286331153, + pos: 18446481363731288337, + length: 18446744073709551615, + prop: 18446743047498699025, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 1, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 18446744073709551615, + length: 1227230898458525695, + prop: 1229787336293814545, + }), + }, + SyncAllUndo { + site: 17, + op_len: 291557249, + }, + ], + ) +} + +#[test] +fn unknown_undo_err() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 48, + target: 255, + container: 203, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 1, + prop: 506381209866536711, + }), + }, + Handle { + site: 7, + target: 7, + container: 255, + action: Generic(GenericAction { + value: I32(117901063), + bool: true, + key: 2332033031, + pos: 506381364485359499, + length: 17268955636171736839, + prop: 506381949047603367, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(117901063), + bool: true, + key: 4219143943, + pos: 539160350683790203, + length: 18446744073693102591, + prop: 506381188273799167, + }), + }, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 79017397, + pos: 18446744073558687743, + length: 18446471394817474559, + prop: 2821266740699193087, + }), + }, + Handle { + site: 48, + target: 49, + container: 54, + action: Generic(GenericAction { + value: I32(939524701), + bool: false, + key: 3419143984, + pos: 18388267869089287115, + length: 16492674416639, + prop: 18446744073709551360, + }), + }, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 656877351, + pos: 13053445094070757159, + length: 2199017665973, + prop: 576459871835127808, + }), + }, + SyncAll, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(656877351), + bool: true, + key: 656877351, + pos: 18385905728160868135, + length: 9826112617566373121, + prop: 506381211308916736, + }), + }, + Handle { + site: 0, + target: 139, + container: 139, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 2071690107, + pos: 506378989234256763, + length: 1978051601041159, + prop: 18386797630442932992, + }), + }, + SyncAll, + Undo { + site: 123, + op_len: 4278680443, + }, + SyncAll, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(35733558), + bool: false, + key: 939524701, + pos: 14685055086132932610, + length: 18446744070224098211, + prop: 18446462598733823999, + }), + }, + SyncAll, + Undo { + site: 123, + op_len: 4278680443, + }, + ], + ) +} + +#[test] +fn unknown_undo_err_1() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(1600085855), + bool: true, + key: 6250319, + pos: 18374686479540944896, + length: 8589934592, + prop: 18446508778221142016, + }), + }, + SyncAll, + Handle { + site: 2, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: false, + key: 0, + pos: 1374464301510623240, + length: 2676586395008832275, + prop: 18446744073587533093, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(-476635), + bool: true, + key: 4294967295, + pos: 18446742974197926143, + length: 1157443315285966847, + prop: 10923210422339659366, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(-122018523), + bool: true, + key: 4294967295, + pos: 18446462598733430783, + length: 4400175265561444351, + prop: 18417301791145594896, + }), + }, + // SyncAll, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(623191333), + bool: true, + key: 623192869, + pos: 2676586395008836901, + length: 10634005407190033701, + prop: 10634005407197270931, + }), + }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(-8472027), + bool: true, + key: 4294967295, + pos: 18446742974197926143, + length: 1157443315285966847, + prop: 10883513199263901286, + }), + }, + // SyncAllUndo { + // site: 151, + // op_len: 2475923351, + // }, + // SyncAll, + // Handle { + // site: 37, + // target: 37, + // container: 37, + // action: Generic(GenericAction { + // value: I32(623191333), + // bool: true, + // key: 623192869, + // pos: 2676304920032126245, + // length: 10634005407190034213, + // prop: 4846998633281983379, + // }), + // }, + // Checkout { + // site: 67, + // to: 1128481603, + // }, + // SyncAllUndo { + // site: 146, + // op_len: 630428563, + // }, + // Handle { + // site: 37, + // target: 37, + // container: 37, + // action: Generic(GenericAction { + // value: Container(Unknown(255)), + // bool: true, + // key: 2303, + // pos: 1676816805009555200, + // length: 7378697628035322064, + // prop: 10923267142493798807, + // }), + // }, + // SyncAll, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 589823, + pos: 4989988387126444032, + length: 4564488215, + prop: 18446629064793286160, + }), + }, + // SyncAll, + // Handle { + // site: 37, + // target: 37, + // container: 37, + // action: Generic(GenericAction { + // value: I32(623191333), + // bool: true, + // key: 623191339, + // pos: 2676586395008836901, + // length: 10634005407197242661, + // prop: 10634005407197270931, + // }), + // }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(-1862), + bool: true, + key: 4294967295, + pos: 18446744069414584328, + length: 7354395854818985279, + prop: 10923210423161742950, + }), + }, + // SyncAll, + Handle { + site: 37, + target: 37, + container: 33, + action: Generic(GenericAction { + value: I32(-1862), + bool: true, + key: 4294967295, + pos: 18446744069414584328, + length: 1157443315285963327, + prop: 10883513199263901286, + }), + }, + // SyncAllUndo { + // site: 151, + // op_len: 2475923351, + // }, + // Handle { + // site: 37, + // target: 37, + // container: 37, + // action: Generic(GenericAction { + // value: I32(623191333), + // bool: true, + // key: 623191339, + // pos: 2676586395008836901, + // length: 10634005407197242661, + // prop: 10634005407197270931, + // }), + // }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: I32(-33094), + bool: true, + key: 4294967295, + pos: 18446744069414584328, + length: 7354395854818985279, + prop: 10923210423161742950, + }), + }, + // SyncAllUndo { + // site: 151, + // op_len: 2475922327, + // }, + // SyncAll, + // Handle { + // site: 37, + // target: 37, + // container: 37, + // action: Generic(GenericAction { + // value: I32(623191333), + // bool: true, + // key: 623191339, + // pos: 2676585295497209125, + // length: 10634005407197242663, + // prop: 4846792388952429459, + // }), + // }, + Handle { + site: 37, + target: 37, + container: 37, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 8, + pos: 14994529625533579263, + length: 10909519737336631312, + prop: 10923365712002484737, + }), + }, + SyncAllUndo { + site: 147, + op_len: 630428563, + }, + ], + ) +} + +#[test] +fn tree_delete_parent_and_delete_child() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(67108864), + bool: false, + key: 5120, + pos: 18374967954648273920, + length: 2244797026329624582, + prop: 18434758041542467359, + }), + }, + Handle { + site: 4, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 0, + pos: 0, + length: 0, + prop: 18446521976655708160, + }), + }, + Handle { + site: 126, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 3520188881, + pos: 6872316421537386961, + length: 6872316419617283935, + prop: 6872316419617283935, + }), + }, + Undo { + site: 95, + op_len: 1600085855, + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(47), + bool: false, + key: 0, + pos: 0, + length: 4107282860161957883, + prop: 18390450177879048246, + }), + }, + Handle { + site: 48, + target: 0, + container: 31, + action: Generic(GenericAction { + value: I32(520093696), + bool: false, + key: 0, + pos: 72349003438748113, + length: 72340172853149953, + prop: 6872316418034041089, + }), + }, + SyncAll, + Handle { + site: 48, + target: 0, + container: 31, + action: Generic(GenericAction { + value: I32(520093696), + bool: false, + key: 0, + pos: 72349003438748113, + length: 72340172853149953, + prop: 6872316418034041089, + }), + }, + Undo { + site: 209, + op_len: 4291920003, + }, + ], + ) +} + +#[test] +fn undo_movable_list_0() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 255, + target: 49, + container: 25, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 421075225, + pos: 2096734697103628569, + length: 14952233337343252761, + prop: 1808504324185078624, + }), + }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(421085465), + bool: true, + key: 4284514577, + pos: 18375812379578413347, + length: 1, + prop: 4553463601266163552, + }), + }, + Handle { + site: 255, + target: 49, + container: 25, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 421075225, + pos: 2096734697103628569, + length: 14952233337343252761, + prop: 1808504324185078624, + }), + }, + Handle { + site: 5, + target: 39, + container: 49, + action: Generic(GenericAction { + value: I32(-16646145), + bool: true, + key: 33554431, + pos: 4294967295, + length: 18446743369335635968, + prop: 288229276640083998, + }), + }, + Undo { + site: 255, + op_len: 654648099, + }, + ], + ) +} + +#[test] +fn undo_movable_list_1() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 4294908185, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551614, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 32, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-209), + bool: true, + key: 4294902219, + pos: 7036874838900735, + length: 1801721325924909056, + prop: 1808504320951916825, + }), + }, + Sync { from: 25, to: 25 }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(85530905), + bool: false, + key: 0, + pos: 1808617462854123520, + length: 1808504381081458969, + prop: 1808504320951916825, + }), + }, + Handle { + site: 25, + target: 25, + container: 185, + action: Generic(GenericAction { + value: I32(421075225), + bool: true, + key: 1305, + pos: 7064362208460800, + length: 57362, + prop: 1808504320951910656, + }), + }, + Handle { + site: 25, + target: 25, + container: 179, + action: Generic(GenericAction { + value: I32(421075225), + bool: true, + key: 421075225, + pos: 21895911705, + length: 1808504320530841600, + prop: 1808504320951916825, + }), + }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(421075385), + bool: true, + key: 421075225, + pos: 2965947086361139481, + length: 2965947086361143617, + prop: 2965947047706437929, + }), + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(858335529), + bool: true, + key: 858993459, + pos: 10633899440147215155, + length: 10634005407197270931, + prop: 10634005407197270931, + }), + }, + SyncAllUndo { + site: 147, + op_len: 4287861651, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + Checkout { + site: 51, + to: 858993459, + }, + Handle { + site: 41, + target: 32, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 1563240745, + pos: 2965947086361143593, + length: 2965946948922190121, + prop: 2965947086361143593, + }), + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 660104077147447337, + length: 2965947086361143593, + prop: 18446744070105147741, + }), + }, + SyncAll, + Checkout { + site: 255, + to: 4294967295, + }, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 4294901760, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 2965948009088548863, + }), + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 689973545, + pos: 18386272210477721897, + length: 18446744073709551369, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(909127984), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947086360553769, + prop: 2965947086361143561, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967109, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 52, + container: 0, + action: Generic(GenericAction { + value: I32(690555177), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947086361143593, + prop: 18446507855493802281, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690561833), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947086361143593, + prop: 660104193111566624, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 64, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2963413811570747689, + prop: 2965946948922190121, + }), + }, + Handle { + site: 93, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(858993459), + bool: true, + key: 858993459, + pos: 10634005407197270931, + length: 10634005407197270931, + prop: 10634005407197270931, + }), + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 697537427, + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947047706437929, + prop: 3101290370670602537, + }), + }, + Checkout { + site: 43, + to: 723921707, + }, + Checkout { + site: 43, + to: 724249387, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2963413811570747689, + prop: 18386272210477721897, + }), + }, + SyncAll, + Checkout { + site: 41, + to: 690563369, + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 589900073, + pos: 2965947086361143593, + length: 2965947047706437929, + prop: 18446507855491705129, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 64, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2963413811570747689, + prop: 2965946948922190121, + }), + }, + Handle { + site: 93, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(858993459), + bool: true, + key: 858993459, + pos: 10634005407197270931, + length: 10634005407197270931, + prop: 10634005407197270931, + }), + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 690590611, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 18386272210477721897, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690561833), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947086361143593, + prop: 660104193111566624, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 64, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2963413811570747689, + prop: 2965946948922190121, + }), + }, + Handle { + site: 93, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(858993459), + bool: true, + key: 858993459, + pos: 10634005407197270931, + length: 10634005407197270931, + prop: 10634005407197270931, + }), + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 697537427, + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947047706437929, + prop: 3101290370670602537, + }), + }, + Checkout { + site: 43, + to: 724249387, + }, + Checkout { + site: 43, + to: 724249387, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965937190756493609, + prop: 18446507855493802281, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690618367, + pos: 2965947086361143593, + length: 2965947086361143587, + prop: 2965937190756493609, + }), + }, + Handle { + site: 41, + target: 41, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 4623507967449235455, + length: 2965947086361143593, + prop: 2965947086361143593, + }), + }, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690561065), + bool: true, + key: 688466217, + pos: 6712941976333396265, + length: 3689348814741252393, + prop: 10606877842382992179, + }), + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + SyncAllUndo { + site: 147, + op_len: 2475922323, + }, + Handle { + site: 41, + target: 9, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2963413811570747689, + prop: 18386272210477721897, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(808529977), + bool: true, + key: 690563369, + pos: 2963413811570747689, + length: 18386272210477721897, + prop: 18446744073709551369, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(809056049), + bool: true, + key: 691222064, + pos: 2965947086361143593, + length: 2965937190756493609, + prop: 2965947085824272681, + }), + }, + Undo { + site: 41, + op_len: 4294912297, + }, + SyncAll, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(421075379), + bool: true, + key: 421101081, + pos: 1808604818470600729, + length: 1808504320951916825, + prop: 15384779033, + }), + }, + ], + ) +} + +#[test] +fn minify() { + minify_error( + 5, + |n, actions| test_multi_sites(n, vec![FuzzTarget::All], actions), + |_, actions| actions.to_vec(), + vec![], + ) +} diff --git a/crates/fuzz/tests/undo.rs b/crates/fuzz/tests/undo.rs index 4caeeb18..43b70bbd 100644 --- a/crates/fuzz/tests/undo.rs +++ b/crates/fuzz/tests/undo.rs @@ -1,8 +1,8 @@ use fuzz::{ actions::{ActionWrapper::*, GenericAction}, - crdt_fuzzer::{test_multi_sites, Action::*, FuzzTarget, FuzzValue::*}, + crdt_fuzzer::{minify_simple, test_multi_sites, Action::*, FuzzTarget, FuzzValue::*}, }; -use loro_common::ContainerType::*; +use loro::ContainerType::*; #[ctor::ctor] fn init() { @@ -164,3 +164,4324 @@ fn tree_delete() { ], ) } + +#[test] +fn tree_undo_delete_with_diff_old_index() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 4280621851, + pos: 1953184669377757183, + length: 1953184666628070171, + prop: 71829045943205915, + }), + }, + Handle { + site: 251, + target: 197, + container: 255, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 16710579922159737627, + prop: 288230380914862055, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(387661595), + bool: false, + key: 454761243, + pos: 1953184666628070171, + length: 71829045943205915, + prop: 18430413027502194837, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 16710579922159737627, + length: 288230380914862055, + prop: 1953184666628070171, + }), + }, + Handle { + site: 63, + target: 27, + container: 23, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 1953184666628070171, + prop: 1953184666627808027, + }), + }, + SyncAll, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: false, + key: 807600128, + pos: 29802787832063, + length: 163831513883392, + prop: 2527082340907941888, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(-1920103141), + bool: true, + key: 2374864269, + pos: 10199964370168810893, + length: 10199964370168810893, + prop: 10199964370168810893, + }), + }, + SyncAllUndo { + site: 141, + op_len: 2374864269, + }, + ], + ) +} + +#[test] +fn tree_undo_delete_parent_in_b() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 129, + target: 207, + container: 96, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 1478566177, + pos: 2387225703656530209, + length: 388195770586702113, + prop: 18446743116485501224, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331665, + pos: 17216961135462248175, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331137), + bool: true, + key: 286331153, + pos: 4256201887840276755, + length: 1229782946837238033, + prop: 1229782938247303441, + }), + }, + SyncAll, + Handle { + site: 0, + target: 2, + container: 5, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 2145059327, + pos: 4050480110299788081, + length: 18157383382424616754, + prop: 18157383382357244923, + }), + }, + Undo { + site: 255, + op_len: 4227596287, + }, + Handle { + site: 223, + target: 47, + container: 184, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4227595259, + pos: 18157383382357244923, + length: 2387225703656586235, + prop: 18446744073709551615, + }), + }, + SyncAll, + Undo { + site: 17, + op_len: 3823363055, + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 243, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782942240280849, + length: 1229782869527826705, + prop: 1229785137270558993, + }), + }, + Checkout { + site: 17, + to: 319885585, + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 16501207799683944947, + length: 2676586395008832811, + prop: 40841467208997, + }), + }, + Handle { + site: 243, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332177), + bool: true, + key: 286327027, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303658, + }), + }, + SyncAllUndo { + site: 135, + op_len: 2273806215, + }, + ], + ) +} + +#[test] +fn tree_undo_move_parent_deleted_in_b() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 129, + target: 207, + container: 96, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 1478566177, + pos: 2387225703656530209, + length: 388195770586702113, + prop: 18446743116485501224, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331665, + pos: 17216961135462248175, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331137), + bool: true, + key: 286331153, + pos: 4256201887840276755, + length: 1229782946837238033, + prop: 1229782938247303441, + }), + }, + SyncAll, + Handle { + site: 0, + target: 2, + container: 5, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 2145059327, + pos: 4050480110299788081, + length: 18157383382424616754, + prop: 18157383382357244923, + }), + }, + // create + Handle { + site: 0, + target: 2, + container: 5, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 2145059327, + pos: 4050480110299788081, + length: 18157383382424616754, + prop: 18157383382357244923, + }), + }, + Handle { + site: 0, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332177), + bool: true, + key: 286327027, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303658, + }), + }, + Handle { + site: 223, + target: 47, + container: 184, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4227595259, + pos: 18157383382357244923, + length: 2387225703656586235, + prop: 18446744073709551615, + }), + }, + SyncAll, + Undo { + site: 17, + op_len: 3823363055, + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 243, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782942240280849, + length: 1229782869527826705, + prop: 1229785137270558993, + }), + }, + Checkout { + site: 17, + to: 319885585, + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 16501207799683944947, + length: 2676586395008832811, + prop: 40841467208997, + }), + }, + Handle { + site: 243, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332177), + bool: true, + key: 286327027, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303658, + }), + }, + SyncAllUndo { + site: 135, + op_len: 2273806215, + }, + ], + ) +} + +#[test] +fn tree_undo_move_deleted_in_b() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 129, + target: 207, + container: 96, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 1478566177, + pos: 2387225703656530209, + length: 388195770586702113, + prop: 18446743116485501224, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331665, + pos: 17216961135462248175, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331137), + bool: true, + key: 286331153, + pos: 4256201887840276755, + length: 1229782946837238033, + prop: 1229782938247303441, + }), + }, + SyncAll, + Handle { + site: 0, + target: 2, + container: 5, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 2145059327, + pos: 4050480110299788081, + length: 18157383382424616754, + prop: 18157383382357244923, + }), + }, + // create + Handle { + site: 0, + target: 2, + container: 5, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 2145059327, + pos: 4050480110299788081, + length: 18157383382424616754, + prop: 18157383382357244923, + }), + }, + Handle { + site: 0, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332177), + bool: true, + key: 286327027, + pos: 0, + length: 1, + prop: 2, + }), + }, + Handle { + site: 223, + target: 47, + container: 184, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4227595259, + pos: 18157383382357244923, + length: 2387225703656586235, + prop: 18446744073709551615, + }), + }, + SyncAll, + Undo { + site: 17, + op_len: 3823363055, + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 243, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 0, + length: 1229782869527826705, + prop: 1229785137270558993, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 0, + length: 2676586395008832811, + prop: 1, + }), + }, + Handle { + site: 243, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286332177), + bool: true, + key: 286327027, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303658, + }), + }, + SyncAllUndo { + site: 135, + op_len: 2273806215, + }, + ], + ) +} + +#[test] +fn tree_diff_position() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(117901063), + bool: true, + key: 4294903559, + pos: 18446744073709551615, + length: 18446744070186336255, + prop: 506381209882853375, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 18446744069566171401, + length: 18446744073709027327, + prop: 15355022929519706111, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151650303, + pos: 18446744073709488393, + length: 18446744073709551607, + prop: 2242546323825885183, + }), + }, + Handle { + site: 31, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294904073, + pos: 18446744039349813247, + length: 18446744073709551615, + prop: 18446744073709540631, + }), + }, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587327, + pos: 17870283321406127881, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAllUndo { + site: 255, + op_len: 522190847, + }, + SyncAllUndo { + site: 255, + op_len: 2543162669, + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-225), + bool: true, + key: 4294967295, + pos: 3746995852044863999, + length: 3689348814751478831, + prop: 3689349566361187123, + }), + }, + Handle { + site: 0, + target: 0, + container: 203, + action: Generic(GenericAction { + value: I32(-67160012), + bool: false, + key: 14354243, + pos: 18446512270787554605, + length: 132529457269757440, + prop: 3689631393508504599, + }), + }, + Sync { from: 154, to: 57 }, + Handle { + site: 52, + target: 52, + container: 56, + action: Generic(GenericAction { + value: I32(755030791), + bool: true, + key: 757935405, + pos: 18446744073558622207, + length: 18446642734358855679, + prop: 10798258750077476863, + }), + }, + SyncAllUndo { + site: 149, + op_len: 1101628309, + }, + Checkout { + site: 255, + to: 797179861, + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-52481), + bool: true, + key: 4294967295, + pos: 2242545361753210879, + length: 18446743107869875999, + prop: 651333096108457983, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Checkout { + site: 255, + to: 797179861, + }, + SyncAll, + SyncAll, + SyncAllUndo { + site: 169, + op_len: 4294913857, + }, + SyncAll, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 522190847, + pos: 3977020664822308639, + length: 18446744070240221241, + prop: 2242545361753210879, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 18446744073709551615, + length: 651333096108457983, + prop: 18446744073709488393, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 213, + target: 163, + container: 255, + action: Generic(GenericAction { + value: I32(-618717409), + bool: true, + key: 2509581823, + pos: 18387987999889921431, + length: 3423861436305875967, + prop: 18446744073694871551, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 2267596630907625247, + length: 18446744073709551391, + prop: 18446744073693429759, + }), + }, + SyncAll, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587081, + pos: 18444492273895866367, + length: 18446744073709551615, + prop: 18446744072989704191, + }), + }, + Handle { + site: 37, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 18446744069566171401, + length: 18446744073709027327, + prop: 2305843009213693951, + }), + }, + Handle { + site: 120, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4278782217, + pos: 18446735277616529407, + length: 18446744073709551615, + prop: 18446744073706739711, + }), + }, + Checkout { + site: 37, + to: 2015305503, + }, + SyncAll, + SyncAll, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614424466711, + prop: 10749528904694701855, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294904319, + pos: 18446744073709551615, + length: 2267596630907625247, + prop: 18446744073709551391, + }), + }, + Handle { + site: 9, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744072989704191, + length: 2242551955064881151, + prop: 18446744069936740383, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 213, + target: 163, + container: 255, + action: Generic(GenericAction { + value: I32(-618717409), + bool: true, + key: 2509581823, + pos: 18387987999889921431, + length: 3423861436305875967, + prop: 18446744073694871551, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 95, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 2267596630907625247, + length: 18446744073709551391, + prop: 18446472533143846911, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAllUndo { + site: 43, + op_len: 2214581759, + }, + SyncAll, + SyncAll, + SyncAllUndo { + site: 131, + op_len: 4281287081, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 55, + container: 57, + action: Generic(GenericAction { + value: I32(-13552072), + bool: true, + key: 4294967295, + pos: 2242545357980434431, + length: 18400582177514266488, + prop: 651061559686070271, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Sync { from: 163, to: 255 }, + SyncAll, + SyncAll, + Sync { from: 163, to: 255 }, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 8655671227355963391, + length: 18446744073709494047, + prop: 18446744069582356479, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587327, + pos: 17870283321406127881, + length: 18446744073709551615, + prop: 18446708705056522239, + }), + }, + Handle { + site: 31, + target: 37, + container: 31, + action: Generic(GenericAction { + value: I32(-225), + bool: true, + key: 4294967295, + pos: 18446743013004216831, + length: 18446744073575333887, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 522190847, + pos: 18383446805802065695, + length: 18446744073709551414, + prop: 2242545361753210879, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 18446744073575333887, + length: 1729382256910270463, + prop: 11817199131615536085, + }), + }, + SyncAllUndo { + site: 47, + op_len: 4280287231, + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 225, + target: 224, + container: 135, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151650303, + pos: 18446744073709488393, + length: 18446744073709551607, + prop: 18420801199931391999, + }), + }, + SyncAllUndo { + site: 43, + op_len: 2214581759, + }, + SyncAll, + SyncAll, + SyncAll, + Checkout { + site: 55, + to: 825636921, + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 18446744069566171401, + length: 18446743133111189503, + prop: 1729382256910270463, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 10922800496058040319, + length: 18446514557796193685, + prop: 18388060938407193507, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 95, + action: Generic(GenericAction { + value: I32(-51457), + bool: true, + key: 4294967295, + pos: 2242545361753210879, + length: 18446743107869875999, + prop: 651333096108457983, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Checkout { + site: 255, + to: 797179861, + }, + SyncAll, + SyncAll, + SyncAllUndo { + site: 169, + op_len: 4294913857, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 354361208, + pos: 18374686479671623680, + length: 10798258750077476863, + prop: 18446743616657790357, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 255, + action: Generic(GenericAction { + value: I32(522133279), + bool: false, + key: 4294909727, + pos: 720575940379279359, + length: 18446744073693366537, + prop: 18446744073709549567, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 37, + container: 31, + action: Generic(GenericAction { + value: I32(-225), + bool: true, + key: 4294967295, + pos: 18446743013004216831, + length: 18446744073575333887, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587081, + pos: 18444492273895866367, + length: 18446744073709551615, + prop: 18446744072989704191, + }), + }, + Handle { + site: 37, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 9223372032711395593, + length: 18446744073709027327, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 219, + container: 149, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 2267596630907625247, + length: 18446744073709551391, + prop: 18446744073693429759, + }), + }, + SyncAll, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587081, + pos: 18444492273895866367, + length: 18446744069649465343, + prop: 18446744072989704191, + }), + }, + Handle { + site: 37, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 18446744069566171401, + length: 18446744073709027327, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 219, + container: 149, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294913857, + pos: 18388060938407193507, + length: 18446744073709494271, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 95, + container: 120, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967295, + pos: 2242545357980434431, + length: 18446744073694814072, + prop: 651061559686070271, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Sync { from: 163, to: 255 }, + SyncAllUndo { + site: 43, + op_len: 2214581759, + }, + SyncAll, + SyncAll, + SyncAllUndo { + site: 131, + op_len: 4281287081, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 55, + container: 57, + action: Generic(GenericAction { + value: I32(-13552072), + bool: true, + key: 4294967295, + pos: 2242545357980434431, + length: 18400582177514266488, + prop: 651061559686070271, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Sync { from: 163, to: 255 }, + SyncAll, + SyncAll, + Sync { from: 163, to: 255 }, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 3078831103, + pos: 2242545357980434305, + length: 14974415777474989855, + prop: 14974415777481871311, + }), + }, + Sync { from: 207, to: 207 }, + Sync { from: 207, to: 207 }, + Sync { from: 207, to: 207 }, + Sync { from: 31, to: 31 }, + SyncAllUndo { + site: 151, + op_len: 1099142549, + }, + Checkout { + site: 255, + to: 797179861, + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(891232031), + bool: true, + key: 522156063, + pos: 18446744073709549567, + length: 144114526650892287, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 732168191, + pos: 2305842113780110847, + length: 18434921212611133231, + prop: 18446743111636823939, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294913535, + pos: 18446744073709551615, + length: 2242545357995114271, + prop: 1729135000460140319, + }), + }, + SyncAll, + Handle { + site: 219, + target: 149, + container: 149, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4292214571, + pos: 18446743111636823939, + length: 18446744073709551615, + prop: 18446744073695789055, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(892673823), + bool: true, + key: 4280229752, + pos: 18446744073709551607, + length: 18375249427041353727, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 219, + container: 149, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 4281050111, + pos: 3395714115539566549, + length: 9511556229955321855, + prop: 18446744069951455023, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709497855, + length: 2305842047141019647, + prop: 2242545357980376863, + }), + }, + Sync { from: 163, to: 255 }, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 732168191, + pos: 2305842113780110847, + length: 18446744073709551615, + prop: 18385945478740049919, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(-1780771553), + bool: true, + key: 2508428695, + pos: 15420091632514445121, + length: 18446497783091266559, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 37, + action: Generic(GenericAction { + value: I32(2015311157), + bool: true, + key: 3250700575, + pos: 13961653357748797889, + length: 13961653357748797889, + prop: 13961653357748797889, + }), + }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 792827267, + pos: 9511556229955321855, + length: 18446744069951455023, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(891232031), + bool: true, + key: 522156063, + pos: 18446744073709549567, + length: 13961721794801958911, + prop: 13961653357748797889, + }), + }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + Sync { from: 193, to: 193 }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 219, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 732168191, + pos: 2305842113780110847, + length: 18446744073709551615, + prop: 18446743171766419455, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 14, + action: Generic(GenericAction { + value: I32(-16777216), + bool: true, + key: 522133503, + pos: 10779248702831402783, + length: 18446744070207407032, + prop: 2242545357980434431, + }), + }, + ], + ) +} + +#[test] +fn tree_undo_unknown() { + // 0: create 13@0 create 0@0 -> 13@0 + // 1: meta 0@0 delete 13@0 + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4281330307, + pos: 3423861436305875967, + length: 18446744073694871551, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(2015305503), + bool: true, + key: 4294967071, + pos: 18446743798831644671, + length: 18446744039349813247, + prop: 18446744073709551615, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 3040456650767990783, + prop: 18446744073709551607, + }), + }, + Handle { + site: 0, + target: 0, + container: 133, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 2039775, + pos: 18446744071620984832, + length: 9476418040919695327, + prop: 18410674826839588863, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(2015305503), + bool: true, + key: 4294967071, + pos: 651333096108457983, + length: 1441151880758495497, + prop: 18374686479671623680, + }), + }, + SyncAll, + Checkout { + site: 131, + to: 536838583, + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18388060938407193507, + length: 18446744073709494271, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4280229752, + pos: 18446744073709551615, + length: 18446744069566171401, + prop: 18446744073709027327, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10779248702831402783, + length: 9485706711646962581, + prop: 18446743254173297663, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4294967160, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 2242545357980377087, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(191)), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 707911479, + pos: 18446744073709551607, + length: 9583660007048690651, + prop: 18446744073564528789, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 2305843009213693951, + length: 10778687951896697631, + prop: 18386970223563456899, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 553975807, + pos: 18446744073560727841, + length: 18446744073709551615, + prop: 11805368386500689919, + }), + }, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10922800942115921695, + length: 11817444525671159189, + prop: 18446743179637817219, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4280229752, + pos: 18428729675200069631, + length: 18444492273895866367, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 872415231, + pos: 18446744073561321951, + length: 71725349863423, + prop: 18444310994424758272, + }), + }, + SyncAll, + Handle { + site: 0, + target: 131, + container: 131, + action: Generic(GenericAction { + value: I32(-8398026), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242545361753210879, + prop: 2242545357980376863, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 9, + target: 9, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: false, + key: 4278190080, + pos: 18446744073709551607, + length: 18420801199931391999, + prop: 2267596630907682815, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4281287043, + pos: 3423861436305875967, + length: 18446744073694871551, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522156063), + bool: true, + key: 4294967295, + pos: 651061559686070271, + length: 18444492273895866367, + prop: 18446744073709551615, + }), + }, + Checkout { + site: 131, + to: 536838583, + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18446515191345546147, + length: 18446744073709494271, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4294967160, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 2242545357980377087, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(191)), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 707911479, + pos: 18446744073709551607, + length: 9583660007048690651, + prop: 18446744073564528789, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614430507007, + prop: 2242545357980376863, + }), + }, + Handle { + site: 31, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294904073, + pos: 335544319, + length: 18446744039333036032, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 120, + target: 31, + container: 59, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10778687951896697631, + length: 18386970223563456899, + prop: 18383693675428577237, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(2015305503), + bool: true, + key: 5407, + pos: 2305841909702066176, + length: 10736644025422389023, + prop: 18446743616657790357, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(527965983), + bool: true, + key: 4294967295, + pos: 10778763175739260927, + length: 18387987836983154581, + prop: 2267596630907625247, + }), + }, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-2049), + bool: true, + key: 4294967295, + pos: 18420801199931391999, + length: 2267596630907682815, + prop: 4, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4281287043, + pos: 3423861436305875967, + length: 18446744073694871551, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522156063), + bool: true, + key: 33554432, + pos: 2242546323809107968, + length: 10778685111367573279, + prop: 18446744073702577559, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4280229752, + pos: 18446744073709551615, + length: 9481649068780656091, + prop: 15420091632514445121, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522156063), + bool: true, + key: 4294967295, + pos: 651062616234196991, + length: 17870283321406127881, + prop: 18446744073709551615, + }), + }, + SyncAll, + // 0@0 meta + Handle { + site: 31, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 4294967167, + pos: 18446744073709551615, + length: 2305843009213693951, + prop: 2242545332210573087, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 939524095, + pos: 18446744073561321951, + length: 71725349863423, + prop: 18444310994424758272, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819297, + pos: 18446744035610665249, + length: 18446744073709551615, + prop: 15355022929519706111, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1785358849), + bool: true, + key: 4294967259, + pos: 18446744035762757428, + length: 18361689565036543, + prop: 17823875776802455552, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: I32(555819297), + bool: true, + key: 555819297, + pos: 2387225703656530209, + length: 2387225703656530209, + prop: 2387225703656530209, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151650303, + pos: 1441151880758495497, + length: 18374686479671623680, + prop: 18446744073709551607, + }), + }, + SyncAll, + Handle { + site: 31, + target: 0, + container: 49, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819297, + pos: 2387225703656530209, + length: 2387225703656530209, + prop: 2387225703656530209, + }), + }, + Handle { + site: 0, + target: 0, + container: 133, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 2039775, + pos: 159580160, + length: 648518344244199424, + prop: 18446744073701153590, + }), + }, + SyncAll, + Undo { + site: 31, + op_len: 2, + }, + ], + ) +} + +#[test] +fn undo_tree_index() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(188430649), + bool: true, + key: 185273099, + pos: 18446744070374634251, + length: 795741901218843451, + prop: 795741901218843403, + }), + }, + Handle { + site: 1, + target: 0, + container: 11, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 3654932953, + pos: 15697817505862638041, + length: 4035108562632563161, + prop: 3399988123389603733, + }), + }, + SyncAll, + Handle { + site: 41, + target: 41, + container: 41, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 188430649, + pos: 795741901218843403, + length: 795741901218843403, + prop: 2970615681721645323, + }), + }, + Handle { + site: 128, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(185280777), + bool: true, + key: 185273099, + pos: 795741901218843403, + length: 15697590118234390529, + prop: 15697817505862638041, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(690563369), + bool: true, + key: 690563369, + pos: 2965947086361143593, + length: 2965947086361143593, + prop: 2965947086361143593, + }), + }, + SyncAllUndo { + site: 43, + op_len: 2214581759, + }, + ], + ) +} + +#[test] +fn undo_tree_delete_delete() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18388060938407193507, + length: 9952409283403775, + prop: 18446744070941246465, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819297, + pos: 18446744073560727841, + length: 18446744073709551615, + prop: 2242545357995114495, + }), + }, + Handle { + site: 120, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 707911479, + pos: 18446744073709551607, + length: 9583660007048690651, + prop: 18446744073564528789, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614430507007, + prop: 2242545357980376863, + }), + }, + Handle { + site: 0, + target: 174, + container: 1, + action: Generic(GenericAction { + value: I32(-65536), + bool: true, + key: 4294967295, + pos: 15355022929519706111, + length: 2242545361753210787, + prop: 2305704159417671544, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4146737631, + pos: 15852670688344145919, + length: 10774017683553796411, + prop: 18446744073708985120, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614430507007, + prop: 2242545357980376863, + }), + }, + Handle { + site: 0, + target: 0, + container: 133, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 2039775, + pos: 648518344252784640, + length: 18446744073701153590, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522156063), + bool: true, + key: 4294967295, + pos: 651061559686070271, + length: 21990232555519, + prop: 18444491174384238592, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1785341153), + bool: true, + key: 2207618455, + pos: 15420091632514445121, + length: 15852424397725860863, + prop: 6556963984818527145, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819297, + pos: 18446744073560727841, + length: 18446627525477007359, + prop: 18446462667452317695, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(527965983), + bool: true, + key: 4294967295, + pos: 651062616248025087, + length: 17870283321406127881, + prop: 18386970223563456899, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 892679477, + pos: 3834029160418063669, + length: 3834029160418063669, + prop: 3834029160418063669, + }), + }, + SyncAllUndo { + site: 255, + op_len: 3, + }, + ], + ) +} + +#[test] +fn tree_undo_nested_map_tree_tree_meta() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 872415231, + pos: 18446744073561321951, + length: 71725349863423, + prop: 18444310994424758272, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4281287043, + pos: 3423861436305875967, + length: 18446744073694871551, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18446515191345546147, + length: 18446744073709494271, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(191)), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 707911479, + pos: 18446744073709551607, + length: 9583660007048690651, + prop: 18446744073564528789, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614430507007, + prop: 10778762209893752607, + }), + }, + Handle { + site: 1, + target: 4, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 255, + pos: 18446743004262694912, + length: 2387225703656530431, + prop: 18446744035610665249, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4146737631, + pos: 15852670688344145919, + length: 10774017683553796411, + prop: 18446744073708985120, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 1310719, + length: 18446744073575268352, + prop: 1729382256910270463, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 792822677, + pos: 9511556229955321855, + length: 18446744069951455023, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4160749567, + pos: 18446744073709551615, + length: 18446642734358855679, + prop: 18446744073709551615, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 939524095, + pos: 18446744073561321951, + length: 71725349863423, + prop: 18444310994424758272, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10778686051533659935, + length: 18446514557159839127, + prop: 18446515191345546147, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1785358849), + bool: true, + key: 4294967259, + pos: 18446744035762757427, + length: 18361689565036543, + prop: 17823875776802455552, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242546323825885183, + prop: 2242545357980376863, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 1310719, + length: 18446744073575268352, + prop: 1729382256910270463, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10779248702831402783, + length: 9485706711646962581, + prop: 2305843005721226239, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 522133279, + pos: 2242545357980376863, + length: 18446744073694814072, + prop: 18446744073705357311, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 4313322543114092543, + length: 2347929015790075969, + prop: 18446744073709549403, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242546323825885183, + prop: 2242545357980376863, + }), + }, + Handle { + site: 213, + target: 163, + container: 255, + action: Generic(GenericAction { + value: I32(527965983), + bool: true, + key: 4286691203, + pos: 2242545357980376863, + length: 10779248702831402783, + prop: 9485706711646962581, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242546323825885183, + prop: 2242545357980376863, + }), + }, + SyncAllUndo { + site: 31, + op_len: 1, + }, + ], + ) +} + +#[test] +fn tree_undo_delete_and_create_exist_node() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(67108864), + bool: false, + key: 5120, + pos: 18374967954648273920, + length: 2244797026329624582, + prop: 18434758041542467359, + }), + }, + Handle { + site: 4, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 0, + pos: 0, + length: 0, + prop: 18446521976655708160, + }), + }, + Handle { + site: 126, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 3520188881, + pos: 6872316421537386961, + length: 6872316419617283935, + prop: 6872316419617283935, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(262144), + bool: false, + key: 20, + pos: 504122782800412436, + length: 2242554153559866112, + prop: 9511555592568334879, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(47), + bool: false, + key: 0, + pos: 0, + length: 4107282860161892352, + prop: 18390450177879048246, + }), + }, + Handle { + site: 48, + target: 0, + container: 31, + action: Generic(GenericAction { + value: I32(520093696), + bool: false, + key: 0, + pos: 72349003438748113, + length: 72340172853149953, + prop: 6872316418034041089, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(47), + bool: false, + key: 0, + pos: 0, + length: 4107282860161892352, + prop: 18390450177879048246, + }), + }, + Handle { + site: 48, + target: 0, + container: 31, + action: Generic(GenericAction { + value: I32(-256), + bool: true, + key: 335544319, + pos: 2115960832, + length: 72349003438748113, + prop: 72340172853149953, + }), + }, + Undo { + site: 95, + op_len: 1600085855, + }, + SyncAllUndo { + site: 128, + op_len: 4294967249, + }, + Handle { + site: 131, + target: 31, + container: 39, + action: Generic(GenericAction { + value: I32(-714423189), + bool: true, + key: 1364283729, + pos: 18446744073709551441, + length: 14430449448537641246, + prop: 15132094744467078979, + }), + }, + ], + ) +} + +#[test] +fn tree_move_child_whose_parent_deleted() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(67108864), + bool: false, + key: 5120, + pos: 18374967954648273920, + length: 2244797026329624582, + prop: 18434758041542467359, + }), + }, + SyncAll, + Handle { + site: 4, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 0, + pos: 0, + length: 0, + prop: 18446524175678963712, + }), + }, + Handle { + site: 126, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 3520188881, + pos: 6872316421537386961, + length: 6872316419617283935, + prop: 6872316419617283935, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(262144), + bool: false, + key: 20, + pos: 504122782800412436, + length: 2242554153559866112, + prop: 9511555592568334879, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 0, + pos: 0, + length: 4107282860161892352, + prop: 18390450177879048246, + }), + }, + Handle { + site: 49, + target: 0, + container: 31, + action: Generic(GenericAction { + value: I32(520093696), + bool: false, + key: 0, + pos: 15119096123158032849, + length: 15119095435963257297, + prop: 6872316420712079525, + }), + }, + SyncAllUndo { + site: 95, + op_len: 1600085855, + }, + SyncAll, + ], + ) +} + +#[test] +fn tree_meta_unknown() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18446515191345546147, + length: 18446744073709494271, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587327, + pos: 1441151086189608713, + length: 18374686479671623680, + prop: 18446744073709551607, + }), + }, + // Handle { + // site: 213, + // target: 163, + // container: 255, + // action: Generic(GenericAction { + // value: I32(527965983), + // bool: true, + // key: 4286691203, + // pos: 2242545357980376863, + // length: 10779248702831402783, + // prop: 3144638436309304213, + // }), + // }, + // Handle { + // site: 31, + // target: 31, + // container: 31, + // action: Generic(GenericAction { + // value: I32(527965983), + // bool: true, + // key: 4294967295, + // pos: 651062616248025087, + // length: 17870283321406127881, + // prop: 18446744073709551615, + // }), + // }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10779248702831402783, + length: 9485706711646962581, + prop: 18446743254173297663, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 4294967160, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 2242545357980377087, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(191)), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + Handle { + site: 31, + target: 219, + container: 149, + action: Generic(GenericAction { + value: Container(Map), + bool: true, + key: 4281050111, + pos: 18383693675428577237, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 120, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709027327, + length: 15355022929519706111, + prop: 18446744073709551523, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(31)), + bool: true, + key: 3676249887, + pos: 4720819787047212437, + length: 18446744069448138543, + prop: 18387634328600313855, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 522133503, + pos: 2242545357980415007, + length: 18446496818752593695, + prop: 720575940379279359, + }), + }, + Handle { + site: 31, + target: 234, + container: 31, + action: Generic(GenericAction { + value: I32(522140447), + bool: true, + key: 522133279, + pos: 18446496818752593695, + length: 18446744073709551615, + prop: 18446743017147596799, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4281287043, + pos: 18388150188524151807, + length: 18446744073694871551, + prop: 18446744073709551615, + }), + }, + SyncAll, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 792822677, + pos: 9511556229955321855, + length: 18446744069951455023, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 31, + container: 31, + action: Generic(GenericAction { + value: I32(527965983), + bool: true, + key: 4294967295, + pos: 651062616248025087, + length: 17870283321406127881, + prop: 18446744073709551615, + }), + }, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 31, + pos: 15795822638653211523, + length: 18446744073709551487, + prop: 18446744073709551615, + }), + }, + Undo { + site: 31, + op_len: 7, + }, + ], + ) +} + +#[test] +fn tree_small_issue() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 63, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1761484928), + bool: false, + key: 513, + pos: 2341377969152, + length: 18380315979205849600, + prop: 4251405740540952575, + }), + }, + Handle { + site: 10, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(40528415), + bool: false, + key: 3238038528, + pos: 0, + length: 0, + prop: 18446744073692774400, + }), + }, + SyncAll, + Handle { + site: 33, + target: 2, + container: 0, + action: Generic(GenericAction { + value: I32(335577214), + bool: true, + key: 16777215, + pos: 2836986853897275135, + length: 7667975533558178817, + prop: 10746995183846424578, + }), + }, + SyncAllUndo { + site: 155, + op_len: 2610666395, + }, + SyncAllUndo { + site: 155, + op_len: 2610666395, + }, + Checkout { + site: 155, + to: 2610666296, + }, + SyncAll, + ], + ) +} + +#[test] +fn tree_remap() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 0, + target: 1, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 4294913857, + pos: 18388060938407193507, + length: 9952409283403775, + prop: 18446744070941246465, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1785358849), + bool: true, + key: 4294967259, + pos: 18446744035762757431, + length: 18361689565036543, + prop: 17823875776802455552, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(Counter), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614430507007, + prop: 2242545357980376863, + }), + }, + Handle { + site: 120, + target: 31, + container: 59, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10778687951896697631, + length: 18386970223563456899, + prop: 18383693675428577237, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: false, + key: 4146737631, + pos: 15852670688344145919, + length: 10774017683553796411, + prop: 18446744073708985120, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242546323825885183, + prop: 2242545357980376863, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 15263775559043514367, + length: 15263776468834178003, + prop: 15263776468834178003, + }), + }, + Handle { + site: 0, + target: 0, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 3575119871, + pos: 2242545361753210787, + length: 2305704159417671544, + prop: 2242545357980376863, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 892679477, + pos: 3834029160418063669, + length: 3834029160418063669, + prop: 3834029160418063669, + }), + }, + Handle { + site: 120, + target: 31, + container: 59, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 522133279, + pos: 10779248702819588895, + length: 3144638436309304213, + prop: 2305842113780110847, + }), + }, + // SyncAll, + Handle { + site: 33, + target: 33, + container: 33, + action: Generic(GenericAction { + value: I32(555819297), + bool: true, + key: 555819297, + pos: 2387225703656530209, + length: 2387225703656530209, + prop: 2387225703656530209, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4146737631, + pos: 15852670688344145919, + length: 10774017683553796411, + prop: 18446744073708985120, + }), + }, + Handle { + site: 0, + target: 0, + container: 131, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242546323825885183, + prop: 2242545357846159135, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 4294967049, + pos: 1310719, + length: 18446744073575268352, + prop: 1729382256910270463, + }), + }, + Handle { + site: 255, + target: 255, + container: 31, + action: Generic(GenericAction { + value: I32(522133279), + bool: true, + key: 2015305503, + pos: 18446744073709494047, + length: 18446744073709551615, + prop: 18446744073709549567, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 792822677, + pos: 9511556229955321855, + length: 18446735273858432815, + prop: 18446744073709551615, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 555819297, + pos: 18446744073560727841, + length: 18446744073709551615, + prop: 15355022929519705906, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4146737631, + pos: 15852670688344145919, + length: 10774017683553796411, + prop: 18446744073708985120, + }), + }, + SyncAll, + Handle { + site: 213, + target: 6, + container: 163, + action: Generic(GenericAction { + value: I32(2015305503), + bool: true, + key: 2176287547, + pos: 2242545357980377087, + length: 10922800942115921695, + prop: 11817444525671159189, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 707911475, + pos: 18446744073709551607, + length: 9583660007048690651, + prop: 18446744073564528789, + }), + }, + SyncAllUndo { + site: 153, + op_len: 1, + }, + ], + ) +} + +#[test] +fn tree_metadata() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 219, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Text), + bool: true, + key: 12395099, + pos: 3298534883477, + length: 3834868070660322304, + prop: 504403158252466996, + }), + }, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Tree), + bool: true, + key: 2913840557, + pos: 3765062388551930802, + length: 12514849900987264429, + prop: 12514849905282231725, + }), + }, + SyncAll, + Handle { + site: 31, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151587081, + pos: 18444492273895866367, + length: 18446744073709551615, + prop: 2242545357995114495, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 2242792614424466711, + prop: 10749528904694701855, + }), + }, + Handle { + site: 31, + target: 31, + container: 120, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294904319, + pos: 18446744073709551615, + length: 2267596630907625247, + prop: 18446744073709551391, + }), + }, + SyncAll, + Handle { + site: 95, + target: 120, + container: 31, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 3423861436305055519, + length: 18410858213187518463, + prop: 12214771541103083519, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073169868799, + length: 18446496843029282815, + prop: 4035224870267125759, + }), + }, + SyncAllUndo { + site: 65, + op_len: 2751463215, + }, + ], + ) +} + +#[test] +fn tree_metadata2() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 171, + target: 255, + container: 255, + action: Generic(GenericAction { + value: I32(50529161), + bool: true, + key: 2769155, + pos: 416717214419337, + length: 4412750447665283201, + prop: 3544668469065809725, + }), + }, + SyncAll, + Handle { + site: 161, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 4294967067, + pos: 3544677320168046591, + length: 4412750542920560945, + prop: 4268729913046809901, + }), + }, + Handle { + site: 3, + target: 3, + container: 3, + action: Generic(GenericAction { + value: I32(707430793), + bool: true, + key: 4278387587, + pos: 1099511627775, + length: 9354488261646483456, + prop: 13114482111674842904, + }), + }, + Handle { + site: 3, + target: 7, + container: 255, + action: Generic(GenericAction { + value: I32(-85), + bool: true, + key: 59310721, + pos: 9871936841907897091, + length: 9295431258694322569, + prop: 4412750542749796608, + }), + }, + SyncAll, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: I32(2071690235), + bool: true, + key: 125533051, + pos: 506381209866404351, + length: 10055130593152665351, + prop: 506381210470516487, + }), + }, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: I32(511), + bool: false, + key: 16842752, + pos: 506381209866536706, + length: 8897841259083463547, + prop: 18446742995706251131, + }), + }, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: I32(84016903), + bool: true, + key: 4294967295, + pos: 144680349937371135, + length: 4702111234470772735, + prop: 2821266740684990247, + }), + }, + Handle { + site: 7, + target: 7, + container: 7, + action: Generic(GenericAction { + value: Container(MovableList), + bool: true, + key: 4278680443, + pos: 18446470325496907009, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + SyncAll, + Undo { + site: 123, + op_len: 125533051, + }, + ], + ) +} + +#[test] +fn tree_unknown2() { + test_multi_sites( + 5, + vec![FuzzTarget::Tree], + &mut [ + Handle { + site: 16, + target: 16, + container: 16, + action: Generic(GenericAction { + value: I32(1406210064), + bool: true, + key: 2036949345, + pos: 7017023257055951225, + length: 18446689516373762401, + prop: 14073748835532799, + }), + }, + Handle { + site: 127, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(858993414), + bool: true, + key: 858989363, + pos: 18445899653105791795, + length: 7161677112984928256, + prop: 7161677110969590627, + }), + }, + Handle { + site: 39, + target: 39, + container: 39, + action: Generic(GenericAction { + value: I32(1736337255), + bool: true, + key: 1734829927, + pos: 7451037802321897319, + length: 7451037802321897319, + prop: 7593457517697918823, + }), + }, + Handle { + site: 255, + target: 255, + container: 255, + action: Generic(GenericAction { + value: Container(List), + bool: true, + key: 1734829927, + pos: 7451037802321897319, + length: 7451037802321897319, + prop: 7451037802321897319, + }), + }, + Handle { + site: 253, + target: 90, + container: 255, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 151584859, + pos: 18377384225446139657, + length: 18446744073709551615, + prop: 651061518279901183, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1), + bool: true, + key: 4294967073, + pos: 18446744073709551615, + length: 12515980216187859455, + prop: 12803080277138976173, + }), + }, + Handle { + site: 33, + target: 0, + container: 0, + action: Generic(GenericAction { + value: Container(Unknown(149)), + bool: true, + key: 906007957, + pos: 18446740938383457883, + length: 651216092122841087, + prop: 10778686051163116296, + }), + }, + SyncAll, + Handle { + site: 126, + target: 0, + container: 57, + action: Generic(GenericAction { + value: Container(Unknown(31)), + bool: true, + key: 2913840557, + pos: 18446744073709530541, + length: 10779094167544945663, + prop: 18446744069575382498, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 0, + action: Generic(GenericAction { + value: I32(-1780744198), + bool: true, + key: 906007957, + pos: 18446740938383457883, + length: 12514849901059768319, + prop: 12804210592272199085, + }), + }, + SyncAll, + Handle { + site: 0, + target: 0, + container: 255, + action: Generic(GenericAction { + value: Container(Tree), + bool: false, + key: 8280886, + pos: 18446744073709364736, + length: 653875205807379938, + prop: 10922803139972553481, + }), + }, + Handle { + site: 0, + target: 255, + container: 23, + action: Generic(GenericAction { + value: Container(Text), + bool: false, + key: 32347, + pos: 18446744073709550886, + length: 18446499982128185343, + prop: 18446744073709551615, + }), + }, + Handle { + site: 0, + target: 0, + container: 255, + action: Generic(GenericAction { + value: Container(Tree), + bool: false, + key: 8280886, + pos: 18446744073709364736, + length: 6201284396160482, + prop: 653866370898853888, + }), + }, + Handle { + site: 35, + target: 35, + container: 35, + action: Generic(GenericAction { + value: I32(589505315), + bool: true, + key: 2516450303, + pos: 9456393277067466133, + length: 18377229688873023266, + prop: 18446744073709551615, + }), + }, + Undo { + site: 103, + op_len: 1734829927, + }, + Undo { + site: 0, + op_len: 151587081, + }, + ], + ) +} + +#[test] +fn tree_parent_remap() { + test_multi_sites( + 5, + vec![FuzzTarget::All], + &mut [ + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 1953184666628070171, + prop: 1953184666627808027, + }), + }, + Handle { + site: 65, + target: 17, + container: 255, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 286326784, + pos: 1229782938247303679, + length: 1229782938247303441, + prop: 1085667750171447569, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + Handle { + site: 21, + target: 17, + container: 9, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286338065, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 21, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 85004561, + pos: 1229782938247303434, + length: 4398046449985, + prop: 1229764173248856064, + }), + }, + SyncAll, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(-286624495), + bool: false, + key: 4008636142, + pos: 1229782938247303441, + length: 4369, + prop: 1229782938247303424, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(355537169), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 17654171012985520128, + prop: 1229782938346782730, + }), + }, + SyncAllUndo { + site: 25, + op_len: 421112089, + }, + ], + ) +} + +#[test] +fn minify() { + minify_simple( + 5, + |n, actions| test_multi_sites(n, vec![FuzzTarget::All], actions), + |_, actions| actions.to_vec(), + vec![ + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 1953184666628070171, + length: 1953184666628070171, + prop: 1953184666627808027, + }), + }, + Handle { + site: 27, + target: 27, + container: 27, + action: Generic(GenericAction { + value: I32(689642267), + bool: true, + key: 587202560, + pos: 1225290376852602672, + length: 1229782938247303441, + prop: 1229775190126301457, + }), + }, + Handle { + site: 65, + target: 17, + container: 255, + action: Generic(GenericAction { + value: I32(0), + bool: false, + key: 286326784, + pos: 1229782938247303679, + length: 1229782938247303441, + prop: 1085667750171447569, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: Container(Unknown(255)), + bool: true, + key: 4294967295, + pos: 18446744073709551615, + length: 18446744073709551615, + prop: 18446744073709551615, + }), + }, + Sync { from: 17, to: 17 }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(-286600175), + bool: false, + key: 4008636142, + pos: 1229782938247303441, + length: 4369, + prop: 1229782938247303424, + }), + }, + Handle { + site: 21, + target: 17, + container: 9, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286338065, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(355537169), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 17654171012985520128, + prop: 1229782938346782730, + }), + }, + Handle { + site: 21, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 85004561, + pos: 1229782938247303434, + length: 4398046449985, + prop: 1229764173248856064, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286331153, + pos: 1229782938247172369, + length: 18446744073693892881, + prop: 18446744073709551615, + }), + }, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + SyncAll, + Sync { from: 17, to: 17 }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(-286624495), + bool: false, + key: 4008636142, + pos: 1229782938247303441, + length: 4369, + prop: 1229782938247303424, + }), + }, + Handle { + site: 21, + target: 17, + container: 9, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 286338065, + pos: 1229782938247303441, + length: 1229782938247303441, + prop: 1229782938247303441, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(355537169), + bool: true, + key: 286331153, + pos: 1229782938247303441, + length: 17654171012985520128, + prop: 1229782938346782730, + }), + }, + Handle { + site: 21, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(286331153), + bool: true, + key: 85004561, + pos: 1229782938247303434, + length: 4398046449985, + prop: 1229764173248856064, + }), + }, + SyncAllUndo { + site: 25, + op_len: 421112089, + }, + Handle { + site: 25, + target: 25, + container: 25, + action: Generic(GenericAction { + value: I32(421075225), + bool: true, + key: 14566, + pos: 281479254966016, + length: 1809634637929030161, + prop: 1229782938247305497, + }), + }, + Handle { + site: 17, + target: 17, + container: 17, + action: Generic(GenericAction { + value: I32(454761243), + bool: true, + key: 454761243, + pos: 3038287259199220251, + length: 3038287259199220266, + prop: 3038287259199220266, + }), + }, + ], + ) +} diff --git a/crates/loro-common/src/error.rs b/crates/loro-common/src/error.rs index 622ca73e..1fb53b5c 100644 --- a/crates/loro-common/src/error.rs +++ b/crates/loro-common/src/error.rs @@ -1,3 +1,4 @@ + use serde_columnar::ColumnarError; use thiserror::Error; @@ -32,8 +33,12 @@ pub enum LoroError { NotFoundError(Box), #[error("Transaction error ({0})")] TransactionError(Box), - #[error("Index out of bound. The given pos is {pos}, but the length is {len}")] - OutOfBound { pos: usize, len: usize }, + #[error("Index out of bound. The given pos is {pos}, but the length is {len}. {info}")] + OutOfBound { + pos: usize, + len: usize, + info: Box, + }, #[error("Every op id should be unique. ID {id} has been used. You should use a new PeerID to edit the content. ")] UsedOpID { id: ID }, #[error("Movable Tree Error: {0}")] diff --git a/crates/loro-internal/benches/tree.rs b/crates/loro-internal/benches/tree.rs index def86df7..9cf7336d 100644 --- a/crates/loro-internal/benches/tree.rs +++ b/crates/loro-internal/benches/tree.rs @@ -61,20 +61,14 @@ mod tree { let mut versions = vec![]; let size = 1000; for _ in 0..size { - ids.push( - loro.with_txn(|txn| tree.create_with_txn(txn, None, 0)) - .unwrap(), - ) + ids.push(tree.create(None).unwrap()) } let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); let mut n = 1000; while n > 0 { let i = rng.gen::() % size; let j = rng.gen::() % size; - if loro - .with_txn(|txn| tree.mov_with_txn(txn, ids[i], ids[j], 0)) - .is_ok() - { + if tree.mov(ids[i], ids[j]).is_ok() { versions.push(loro.oplog_frontiers()); n -= 1; }; @@ -94,15 +88,11 @@ mod tree { let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; - let id1 = loro - .with_txn(|txn| tree.create_with_txn(txn, None, 0)) - .unwrap(); + let id1 = tree.create(None).unwrap(); ids.push(id1); versions.push(loro.oplog_frontiers()); for _ in 1..depth { - let id = loro - .with_txn(|txn| tree.create_with_txn(txn, *ids.last().unwrap(), 0)) - .unwrap(); + let id = tree.create(*ids.last().unwrap()).unwrap(); ids.push(id); versions.push(loro.oplog_frontiers()); } @@ -124,11 +114,7 @@ mod tree { let mut ids = vec![]; let size = 1000; for _ in 0..size { - ids.push( - doc_a - .with_txn(|txn| tree_a.create_with_txn(txn, None, 0)) - .unwrap(), - ) + ids.push(tree_a.create(None).unwrap()) } doc_b.import(&doc_a.export_snapshot()).unwrap(); let mut rng: StdRng = rand::SeedableRng::seed_from_u64(0); @@ -138,16 +124,10 @@ mod tree { let i = rng.gen::() % size; let j = rng.gen::() % size; if t % 2 == 0 { - let mut txn = doc_a.txn().unwrap(); - tree_a - .mov_with_txn(&mut txn, ids[i], ids[j], 0) - .unwrap_or_default(); + tree_a.mov(ids[i], ids[j]).unwrap_or_default(); doc_b.import(&doc_a.export_from(&doc_b.oplog_vv())).unwrap(); } else { - let mut txn = doc_b.txn().unwrap(); - tree_b - .mov_with_txn(&mut txn, ids[i], ids[j], 0) - .unwrap_or_default(); + tree_b.mov(ids[i], ids[j]).unwrap_or_default(); doc_a.import(&doc_b.export_from(&doc_a.oplog_vv())).unwrap(); } } diff --git a/crates/loro-internal/examples/tree.rs b/crates/loro-internal/examples/tree.rs index 4ca71de3..309ef7d3 100644 --- a/crates/loro-internal/examples/tree.rs +++ b/crates/loro-internal/examples/tree.rs @@ -6,19 +6,15 @@ use rand::{rngs::StdRng, Rng}; #[allow(unused)] fn checkout() { let depth = 300; - let loro = LoroDoc::default(); + let loro = LoroDoc::new_auto_commit(); let tree = loro.get_tree("tree"); let mut ids = vec![]; let mut versions = vec![]; - let id1 = loro - .with_txn(|txn| tree.create_with_txn(txn, None, 0)) - .unwrap(); + let id1 = tree.create_at(None, 0).unwrap(); ids.push(id1); versions.push(loro.oplog_frontiers()); for _ in 1..depth { - let id = loro - .with_txn(|txn| tree.create_with_txn(txn, *ids.last().unwrap(), 0)) - .unwrap(); + let id = tree.create_at(*ids.last().unwrap(), 0).unwrap(); ids.push(id); versions.push(loro.oplog_frontiers()); } @@ -63,8 +59,7 @@ fn create() { let loro = LoroDoc::default(); let tree = loro.get_tree("tree"); for _ in 0..size { - loro.with_txn(|txn| tree.create_with_txn(txn, None, 0)) - .unwrap(); + tree.create_at(None, 0).unwrap(); } println!("encode snapshot size {:?}\n", loro.export_snapshot().len()); println!( diff --git a/crates/loro-internal/src/container.rs b/crates/loro-internal/src/container.rs index 72c22d16..4b75c396 100644 --- a/crates/loro-internal/src/container.rs +++ b/crates/loro-internal/src/container.rs @@ -6,6 +6,10 @@ //! use crate::{arena::SharedArena, InternalString, ID}; +pub mod list; +pub mod map; +pub mod richtext; +pub mod tree; pub mod idx { use super::super::ContainerType; @@ -86,11 +90,6 @@ pub mod idx { } } } - -pub mod list; -pub mod map; -pub mod richtext; -pub mod tree; use idx::ContainerIdx; pub use loro_common::ContainerType; diff --git a/crates/loro-internal/src/delta/tree.rs b/crates/loro-internal/src/delta/tree.rs index af78f54c..82974aa5 100644 --- a/crates/loro-internal/src/delta/tree.rs +++ b/crates/loro-internal/src/delta/tree.rs @@ -1,5 +1,5 @@ use fractional_index::FractionalIndex; -use fxhash::FxHashMap; +use fxhash::{FxHashMap, FxHashSet}; use itertools::Itertools; use loro_common::{IdFull, TreeID}; use std::fmt::Debug; @@ -29,19 +29,14 @@ pub enum TreeExternalDiff { parent: Option, index: usize, position: FractionalIndex, - old_parent: TreeParentId, - old_index: usize, - }, - Delete { - old_parent: TreeParentId, - old_index: usize, }, + Delete, } impl TreeDiff { pub(crate) fn compose(mut self, other: Self) -> Self { - // TODO: better compose self.diff.extend(other.diff); + // self = compose_tree_diff(&self); self } @@ -50,125 +45,38 @@ impl TreeDiff { self } + fn to_hash_map_mut(&mut self) -> FxHashMap { + let mut ans = FxHashSet::default(); + for index in (0..self.diff.len()).rev() { + let target = self.diff[index].target; + if ans.contains(&target) { + self.diff.remove(index); + continue; + } + ans.insert(target); + } + self.iter() + .map(|x| x.target) + .enumerate() + .map(|(i, x)| (x, i)) + .collect() + } + pub(crate) fn transform(&mut self, b: &TreeDiff, left_prior: bool) { + // println!("\ntransform prior {:?} {:?} \nb {:?}", left_prior, self, b); if b.is_empty() || self.is_empty() { return; } - - let b_update: FxHashMap<_, _> = b.diff.iter().map(|d| (d.target, &d.action)).collect(); - let mut self_update: FxHashMap<_, _> = self - .diff - .iter() - .enumerate() - .map(|(i, d)| (d.target, (&d.action, i))) - .collect(); - - let mut removes = Vec::new(); - for (target, diff) in b_update { - if self_update.contains_key(&target) && diff == self_update.get(&target).unwrap().0 { - let (_, i) = self_update.remove(&target).unwrap(); - removes.push(i); - continue; - } - if !left_prior { - if let Some((_, i)) = self_update.remove(&target) { - removes.push(i); - } - } - } - for i in removes.into_iter().sorted().rev() { - self.diff.remove(i); - } - let mut b_parent = FxHashMap::default(); - - fn reset_index( - b_parent: &FxHashMap>, - index: &mut usize, - parent: &TreeParentId, - left_priority: bool, - ) { - if let Some(b_indices) = b_parent.get(parent) { - for i in b_indices.iter() { - if (i.unsigned_abs() as usize) < *index - || (i.unsigned_abs() as usize == *index && !left_priority) - { - if i > &0 { - *index += 1; - } else if *index > (i.unsigned_abs() as usize) { - *index = index.saturating_sub(1); - } - } else { - break; - } - } - } - } - - for diff in b.diff.iter() { - match &diff.action { - TreeExternalDiff::Create { - parent, - index, - position: _, - } => { - b_parent - .entry(TreeParentId::from(*parent)) - .or_insert_with(Vec::new) - .push(*index as i32); - } - TreeExternalDiff::Move { - parent, - index, - position: _, - old_parent, - old_index, - } => { - b_parent - .entry(*old_parent) - .or_insert_with(Vec::new) - .push(-(*old_index as i32)); - b_parent - .entry(TreeParentId::from(*parent)) - .or_insert_with(Vec::new) - .push(*index as i32); - } - TreeExternalDiff::Delete { - old_index, - old_parent, - } => { - b_parent - .entry(*old_parent) - .or_insert_with(Vec::new) - .push(-(*old_index as i32)); - } - } - } - b_parent - .iter_mut() - .for_each(|(_, v)| v.sort_unstable_by_key(|i| i.abs())); - for diff in self.iter_mut() { - match &mut diff.action { - TreeExternalDiff::Create { - parent, - index, - position: _, - } => reset_index(&b_parent, index, &TreeParentId::from(*parent), left_prior), - TreeExternalDiff::Move { - parent, - index, - position: _, - old_parent, - old_index, - } => { - reset_index(&b_parent, index, &TreeParentId::from(*parent), left_prior); - reset_index(&b_parent, old_index, old_parent, left_prior); - } - TreeExternalDiff::Delete { - old_index, - old_parent, - } => { - reset_index(&b_parent, old_index, old_parent, left_prior); - } + if !left_prior { + let mut self_update = self.to_hash_map_mut(); + for i in b + .iter() + .map(|x| x.target) + .filter_map(|x| self_update.remove(&x)) + .sorted() + .rev() + { + self.remove(i); } } } @@ -239,7 +147,6 @@ impl TreeDeltaItem { is_old_parent_deleted: bool, position: Option, ) -> Self { - // TODO: check op id let action = if matches!(parent, TreeParentId::Unexist) { TreeInternalDiff::UnCreate } else { diff --git a/crates/loro-internal/src/diff_calc/tree.rs b/crates/loro-internal/src/diff_calc/tree.rs index 2f5b8440..ebc4d8d2 100644 --- a/crates/loro-internal/src/diff_calc/tree.rs +++ b/crates/loro-internal/src/diff_calc/tree.rs @@ -50,7 +50,6 @@ impl DiffCalculatorTrait for TreeDiffCalculator { on_new_container(&d.target.associated_meta_container()) } }); - tracing::info!("\ndiff {:?}", diff); InternalDiff::Tree(diff) @@ -70,7 +69,6 @@ impl TreeDiffCalculator { fn checkout(&mut self, to: &VersionVector, oplog: &OpLog) { let tree_ops = oplog.op_groups.get_tree(&self.container).unwrap(); let mut tree_cache = tree_ops.tree_for_diff.lock().unwrap(); - let s = format!("checkout current {:?} to {:?}", &tree_cache.current_vv, &to); let s = tracing::span!(tracing::Level::INFO, "checkout", s = s); let _e = s.enter(); @@ -451,7 +449,7 @@ impl TreeCacheForDiff { ans.push((*tree_id, op.position.clone(), op.id_full())); } } - + ans.sort_by(|a, b| a.1.cmp(&b.1)); ans } } diff --git a/crates/loro-internal/src/event.rs b/crates/loro-internal/src/event.rs index 3fd1ea46..556408e3 100644 --- a/crates/loro-internal/src/event.rs +++ b/crates/loro-internal/src/event.rs @@ -216,6 +216,14 @@ impl DiffVariant { (a, _) => Err(a), } } + + pub fn is_empty(&self) -> bool { + match self { + DiffVariant::Internal(diff) => diff.is_empty(), + DiffVariant::External(diff) => diff.is_empty(), + DiffVariant::None => true, + } + } } #[non_exhaustive] diff --git a/crates/loro-internal/src/handler.rs b/crates/loro-internal/src/handler.rs index 2ab70c3b..7687415b 100644 --- a/crates/loro-internal/src/handler.rs +++ b/crates/loro-internal/src/handler.rs @@ -16,19 +16,22 @@ use crate::{ }; use append_only_bytes::BytesSlice; use enum_as_inner::EnumAsInner; -use fxhash::FxHashMap; +use fxhash::{FxHashMap, FxHashSet}; use generic_btree::rle::HasLength; use loro_common::{ - ContainerID, ContainerType, IdFull, InternalString, LoroError, LoroResult, LoroValue, ID, + ContainerID, ContainerType, IdFull, InternalString, LoroError, LoroResult, LoroValue, TreeID, + ID, }; use serde::{Deserialize, Serialize}; use std::{ borrow::Cow, + cmp::Reverse, + collections::BinaryHeap, fmt::Debug, ops::Deref, sync::{Arc, Mutex, Weak}, }; -use tracing::{error, info, instrument}; +use tracing::{debug, error, info, instrument, trace}; mod tree; pub use tree::TreeHandler; @@ -1060,8 +1063,11 @@ impl Handler { pub(crate) fn apply_diff( &self, diff: Diff, - on_container_remap: &mut dyn FnMut(ContainerID, ContainerID), + container_remap: &mut FxHashMap, ) -> LoroResult<()> { + let on_container_remap = &mut |old_id, new_id| { + container_remap.insert(old_id, new_id); + }; match self { Self::Map(x) => { let diff = diff.into_map().unwrap(); @@ -1098,20 +1104,60 @@ impl Handler { x.apply_delta(delta, on_container_remap)?; } Self::Tree(x) => { + fn remap_tree_id( + id: &mut TreeID, + container_remap: &FxHashMap, + ) { + let mut remapped = false; + let mut map_id = id.associated_meta_container(); + while let Some(rid) = container_remap.get(&map_id) { + remapped = true; + map_id = rid.clone(); + } + if remapped { + *id = TreeID::new( + *map_id.as_normal().unwrap().0, + *map_id.as_normal().unwrap().1, + ) + } + } for diff in diff.into_tree().unwrap().diff { - let target = diff.target; + let mut target = diff.target; match diff.action { TreeExternalDiff::Create { - parent, - index, - position: _, + mut parent, + index: _, + position, } => { - x.create_at_with_target(parent, index, target)?; - // create map event + let new_target = x.__internal__next_tree_id(); + if let Some(p) = parent.as_mut() { + remap_tree_id(p, container_remap) + } + if x.create_at_with_target_for_apply_diff(parent, position, new_target)? + { + container_remap.insert( + target.associated_meta_container(), + new_target.associated_meta_container(), + ); + } } - TreeExternalDiff::Delete { .. } => x.delete(target)?, - TreeExternalDiff::Move { parent, index, .. } => { - x.move_to(target, parent, index)? + TreeExternalDiff::Move { + mut parent, + index: _, + position, + } => { + if let Some(p) = parent.as_mut() { + remap_tree_id(p, container_remap) + } + remap_tree_id(&mut target, container_remap); + x.move_at_with_target_for_apply_diff(parent, position, target)?; + } + TreeExternalDiff::Delete => { + remap_tree_id(&mut target, container_remap); + // println!("delete {:?}", target); + if x.contains(target) { + x.delete(target)? + } } } } @@ -1329,6 +1375,7 @@ impl TextHandler { return Err(LoroError::OutOfBound { pos, len: self.len_event(), + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), }); } @@ -1426,6 +1473,7 @@ impl TextHandler { return Err(LoroError::OutOfBound { pos: pos + len, len: self.len_event(), + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), }); } @@ -1503,7 +1551,11 @@ impl TextHandler { )); } if end > len { - return Err(LoroError::OutOfBound { pos: end, len }); + return Err(LoroError::OutOfBound { + pos: end, + len, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), + }); } let (entity_range, styles) = state.get_entity_range_and_text_styles_at_range(start..end, PosType::Event); @@ -1579,7 +1631,11 @@ impl TextHandler { let len = self.len_event(); if end > len { - return Err(LoroError::OutOfBound { pos: end, len }); + return Err(LoroError::OutOfBound { + pos: end, + len, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), + }); } let inner = self.inner.try_attached_state()?; @@ -1873,6 +1929,7 @@ impl ListHandler { if pos > self.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -1957,6 +2014,7 @@ impl ListHandler { if pos > self.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -1997,6 +2055,7 @@ impl ListHandler { if pos + len > self.len() { return Err(LoroError::OutOfBound { pos: pos + len, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2031,6 +2090,7 @@ impl ListHandler { let list = l.try_lock().unwrap(); let value = list.value.get(index).ok_or(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: list.value.len(), })?; match value { @@ -2050,6 +2110,7 @@ impl ListHandler { }) else { return Err(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: a.with_state(|state| state.as_list_state().unwrap().len()), }); }; @@ -2249,6 +2310,7 @@ impl MovableListHandler { if pos > d.value.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: d.value.len(), }); } @@ -2271,6 +2333,7 @@ impl MovableListHandler { if pos > self.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2310,12 +2373,14 @@ impl MovableListHandler { if from >= d.value.len() { return Err(LoroError::OutOfBound { pos: from, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: d.value.len(), }); } if to >= d.value.len() { return Err(LoroError::OutOfBound { pos: to, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: d.value.len(), }); } @@ -2337,6 +2402,7 @@ impl MovableListHandler { if from >= self.len() { return Err(LoroError::OutOfBound { pos: from, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2344,6 +2410,7 @@ impl MovableListHandler { if to >= self.len() { return Err(LoroError::OutOfBound { pos: to, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2439,6 +2506,7 @@ impl MovableListHandler { if pos > d.value.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: d.value.len(), }); } @@ -2461,6 +2529,7 @@ impl MovableListHandler { if pos > self.len() { return Err(LoroError::OutOfBound { pos, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2495,6 +2564,7 @@ impl MovableListHandler { if index >= d.value.len() { return Err(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: d.value.len(), }); } @@ -2516,6 +2586,7 @@ impl MovableListHandler { if index >= self.len() { return Err(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2604,6 +2675,7 @@ impl MovableListHandler { if pos + len > self.len() { return Err(LoroError::OutOfBound { pos: pos + len, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: self.len(), }); } @@ -2651,6 +2723,7 @@ impl MovableListHandler { let list = l.try_lock().unwrap(); let value = list.value.get(index).ok_or(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: list.value.len(), })?; match value { @@ -2675,6 +2748,7 @@ impl MovableListHandler { }) else { return Err(LoroError::OutOfBound { pos: index, + info: format!("Position: {}:{}", file!(), line!()).into_boxed_str(), len: a.with_state(|state| state.as_list_state().unwrap().len()), }); }; @@ -2872,7 +2946,54 @@ impl MovableListHandler { unimplemented!(); } MaybeDetached::Attached(_) => { + debug!("movable list apply_delta {:#?}", &delta); + // preprocess all deletions. They will be used to infer the move ops let mut index = 0; + let mut to_delete = FxHashMap::default(); + for d in delta.iter() { + match d { + loro_delta::DeltaItem::Retain { len, .. } => { + index += len; + } + loro_delta::DeltaItem::Replace { delete, .. } => { + if *delete > 0 { + for i in index..index + *delete { + if let Some(LoroValue::Container(c)) = self.get(i) { + to_delete.insert(c, i); + } + } + + index += *delete; + } + } + } + } + + fn update_on_insert( + d: &mut FxHashMap, + index: usize, + len: usize, + ) { + for pos in d.values_mut() { + if *pos >= index { + *pos += len; + } + } + } + + fn update_on_delete(d: &mut FxHashMap, index: usize) { + for pos in d.values_mut() { + if *pos >= index { + *pos -= 1; + } + } + } + + // process all insertions and moves + let mut index = 0; + let mut deleted = Vec::new(); + let mut next_deleted = BinaryHeap::new(); + let mut index_shift = 0; for d in delta.iter() { match d { loro_delta::DeltaItem::Retain { len, .. } => { @@ -2881,28 +3002,93 @@ impl MovableListHandler { loro_delta::DeltaItem::Replace { value, delete, - attr: _attr, + attr, } => { - // TODO: handle move error - self.delete(index, *delete)?; + if *delete > 0 { + // skip the deletion if it is already processed by moving + let mut d = *delete; + while let Some(Reverse(old_index)) = next_deleted.peek() { + if *old_index + index_shift < index + d { + assert!(index <= *old_index + index_shift); + assert!(d > 0); + next_deleted.pop(); + d -= 1; + } else { + break; + } + } + + index += d; + } + for v in value.iter() { match v { ValueOrHandler::Value(v) => { self.insert(index, v.clone())?; + update_on_insert(&mut to_delete, index, 1); + index += 1; + index_shift += 1; } ValueOrHandler::Handler(h) => { let old_id = h.id(); - let new_h = self.insert_container( - index, - Handler::new_unattached(old_id.container_type()), - )?; - let new_id = new_h.id(); - on_container_remap(old_id, new_id); + if let Some(old_index) = to_delete.remove(&old_id) { + if old_index > index { + self.mov(old_index, index)?; + next_deleted.push(Reverse(old_index)); + index += 1; + index_shift += 1; + } else { + // we need to sub 1 because old_index < index, and index means the position before the move + // but the param is the position after the move + self.mov(old_index, index - 1)?; + } + deleted.push(old_index); + update_on_delete(&mut to_delete, old_index); + update_on_insert(&mut to_delete, index, 1); + } else { + let new_h = self.insert_container( + index, + Handler::new_unattached(old_id.container_type()), + )?; + let new_id = new_h.id(); + on_container_remap(old_id, new_id); + update_on_insert(&mut to_delete, index, 1); + index += 1; + index_shift += 1; + } + } + } + } + } + } + } + + // apply the rest of the deletions + + // sort deleted indexes from large to small + deleted.sort_by_key(|x| -(*x as i32)); + let mut index = 0; + for d in delta.iter() { + match d { + loro_delta::DeltaItem::Retain { len, .. } => { + index += len; + } + loro_delta::DeltaItem::Replace { delete, value, .. } => { + if *delete > 0 { + let mut d = *delete; + while let Some(last) = deleted.last() { + if *last < index + d { + deleted.pop(); + d -= 1; + } else { + break; } } - index += 1; + self.delete(index, d)?; } + + index += value.len(); } } } diff --git a/crates/loro-internal/src/handler/tree.rs b/crates/loro-internal/src/handler/tree.rs index b520c8f0..7b9e2718 100644 --- a/crates/loro-internal/src/handler/tree.rs +++ b/crates/loro-internal/src/handler/tree.rs @@ -3,13 +3,14 @@ use std::collections::VecDeque; use fractional_index::FractionalIndex; use fxhash::FxHashMap; use loro_common::{ - ContainerID, ContainerType, Counter, LoroResult, LoroTreeError, LoroValue, PeerID, TreeID, + ContainerID, ContainerType, Counter, IdLp, LoroResult, LoroTreeError, LoroValue, PeerID, TreeID, }; +use smallvec::smallvec; use crate::{ container::tree::tree_op::TreeOp, delta::{TreeDiffItem, TreeExternalDiff}, - state::{FractionalIndexGenResult, TreeParentId}, + state::{FractionalIndexGenResult, NodePosition, TreeParentId}, txn::{EventHint, Transaction}, BasicHandler, HandlerTrait, MapHandler, }; @@ -49,19 +50,6 @@ impl TreeInner { id } - fn create_with_target( - &mut self, - parent: Option, - index: usize, - target: TreeID, - ) -> TreeID { - self.map.insert(target, MapHandler::new_detached()); - self.parent_links.insert(target, parent); - let children = self.children_links.entry(parent).or_default(); - children.insert(index, target); - target - } - fn mov(&mut self, target: TreeID, new_parent: Option, index: usize) -> LoroResult<()> { let old_parent = self .parent_links @@ -267,7 +255,7 @@ impl HandlerTrait for TreeHandler { impl std::fmt::Debug for TreeHandler { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match &self.inner { - MaybeDetached::Detached(_) => write!(f, "TreeHandler Dettached"), + MaybeDetached::Detached(_) => write!(f, "TreeHandler Detached"), MaybeDetached::Attached(a) => write!(f, "TreeHandler {}", a.id), } } @@ -296,21 +284,15 @@ impl TreeHandler { } } - pub fn delete_with_txn(&self, txn: &mut Transaction, target: TreeID) -> LoroResult<()> { + pub(crate) fn delete_with_txn(&self, txn: &mut Transaction, target: TreeID) -> LoroResult<()> { let inner = self.inner.try_attached_state()?; txn.apply_local_op( inner.container_idx, crate::op::RawOpContent::Tree(TreeOp::Delete { target }), - EventHint::Tree(TreeDiffItem { + EventHint::Tree(smallvec![TreeDiffItem { target, - action: TreeExternalDiff::Delete { - old_parent: self - .get_node_parent(&target) - .map(TreeParentId::from) - .unwrap_or(TreeParentId::Unexist), - old_index: self.get_index_by_tree_id(&target).unwrap_or(0), - }, - }), + action: TreeExternalDiff::Delete, + }]), &inner.state, ) } @@ -338,45 +320,141 @@ impl TreeHandler { } /// For undo/redo, Specify the TreeID of the created node - pub(crate) fn create_at_with_target( + pub(crate) fn create_at_with_target_for_apply_diff( &self, parent: Option, - index: usize, + position: FractionalIndex, target: TreeID, - ) -> LoroResult<()> { - if let Some(p) = parent { - if !self.contains(p) { - return Ok(()); + ) -> LoroResult { + let MaybeDetached::Attached(a) = &self.inner else { + unreachable!(); + }; + if let Some(p) = self.get_node_parent(&target) { + if p == parent { + return Ok(false); + // If parent is deleted, we need to create the node, so this op from move_apply_diff + } else if !p.is_some_and(|p| !self.contains(p)) { + return self.move_at_with_target_for_apply_diff(parent, position, target); } } - match &self.inner { - MaybeDetached::Detached(t) => { - let t = &mut t.try_lock().unwrap().value; - t.create_with_target(parent, index, target); - Ok(()) - } - MaybeDetached::Attached(a) => a.with_txn(|txn| { - let inner = self.inner.try_attached_state()?; - match self.generate_position_at(&target, parent, index) { - FractionalIndexGenResult::Ok(position) => { - self.create_with_position(inner, txn, target, parent, index, position)?; - } - FractionalIndexGenResult::Rearrange(ids) => { - for (i, (id, position)) in ids.into_iter().enumerate() { - if i == 0 { - self.create_with_position(inner, txn, id, parent, index, position)?; - continue; - } - self.mov_with_position(inner, txn, id, parent, index + i, position)?; - } - } - }; - Ok(()) - }), + + let with_event = !parent.is_some_and(|p| !self.contains(p)); + if !with_event { + return Ok(false); } + + // println!( + // "create_at_with_target_for_apply_diff: {:?} {:?}", + // target, parent + // ); + + let index = self + .get_index_by_fractional_index( + parent, + &NodePosition { + position: position.clone(), + idlp: self.next_idlp(), + }, + ) + // TODO: parent has deleted? + .unwrap_or(0); + + let children = a.with_txn(|txn| { + let inner = self.inner.try_attached_state()?; + + txn.apply_local_op( + inner.container_idx, + crate::op::RawOpContent::Tree(TreeOp::Create { + target, + parent, + position: position.clone(), + }), + EventHint::Tree(smallvec![TreeDiffItem { + target, + action: TreeExternalDiff::Create { + parent, + index, + position: position.clone(), + }, + }]), + &inner.state, + )?; + + Ok(self.children(Some(target)).unwrap_or_default()) + })?; + for child in children { + let position = self.get_position_by_tree_id(&child).unwrap(); + self.create_at_with_target_for_apply_diff(Some(target), position, child)?; + } + Ok(true) } - pub fn create_with_txn>>( + /// For undo/redo, Specify the TreeID of the created node + pub(crate) fn move_at_with_target_for_apply_diff( + &self, + parent: Option, + position: FractionalIndex, + target: TreeID, + ) -> LoroResult { + let MaybeDetached::Attached(a) = &self.inner else { + unreachable!(); + }; + + // the move node does not exist, create it + if !self.contains(target) { + return self.create_at_with_target_for_apply_diff(parent, position, target); + } + + if let Some(p) = self.get_node_parent(&target) { + if p == parent { + return Ok(false); + } + } + + let index = self + .get_index_by_fractional_index( + parent, + &NodePosition { + position: position.clone(), + idlp: self.next_idlp(), + }, + ) + .unwrap_or(0); + let with_event = !parent.is_some_and(|p| !self.contains(p)); + + if !with_event { + return Ok(false); + } + + // println!( + // "move_at_with_target_for_apply_diff: {:?} {:?}", + // target, parent + // ); + + a.with_txn(|txn| { + let inner = self.inner.try_attached_state()?; + txn.apply_local_op( + inner.container_idx, + crate::op::RawOpContent::Tree(TreeOp::Move { + target, + parent, + position: position.clone(), + }), + EventHint::Tree(smallvec![TreeDiffItem { + target, + action: TreeExternalDiff::Move { + parent, + index, + position: position.clone(), + }, + }]), + &inner.state, + ) + })?; + Ok(true) + } + + pub(crate) fn create_with_txn>>( &self, txn: &mut Transaction, parent: T, @@ -465,7 +543,7 @@ impl TreeHandler { } } - pub fn mov_with_txn>>( + pub(crate) fn mov_with_txn>>( &self, txn: &mut Transaction, target: TreeID, @@ -513,6 +591,7 @@ impl TreeHandler { } } + #[allow(clippy::too_many_arguments)] fn create_with_position( &self, inner: &BasicHandler, @@ -529,19 +608,20 @@ impl TreeHandler { parent, position: position.clone(), }), - EventHint::Tree(TreeDiffItem { + EventHint::Tree(smallvec![TreeDiffItem { target: tree_id, action: TreeExternalDiff::Create { parent, index, position, }, - }), + }]), &inner.state, )?; Ok(tree_id) } + #[allow(clippy::too_many_arguments)] fn mov_with_position( &self, inner: &BasicHandler, @@ -558,19 +638,14 @@ impl TreeHandler { parent, position: position.clone(), }), - EventHint::Tree(TreeDiffItem { + EventHint::Tree(smallvec![TreeDiffItem { target, action: TreeExternalDiff::Move { parent, index, position, - old_parent: self - .get_node_parent(&target) - .map(TreeParentId::from) - .unwrap_or(TreeParentId::Unexist), - old_index: self.get_index_by_tree_id(&target).unwrap_or(0), }, - }), + }]), &inner.state, ) } @@ -623,8 +698,7 @@ impl TreeHandler { } MaybeDetached::Attached(a) => a.with_state(|state| { let a = state.as_tree_state().unwrap(); - a.get_children(&TreeParentId::from(parent)) - .map(|x| x.collect()) + a.children(&TreeParentId::from(parent)) }), } } @@ -761,4 +835,30 @@ impl TreeHandler { a.delete_position(&TreeParentId::from(parent), target) }) } + + // use for apply diff + pub(crate) fn get_index_by_fractional_index( + &self, + parent: Option, + node_position: &NodePosition, + ) -> Option { + match &self.inner { + MaybeDetached::Detached(_) => { + unreachable!(); + } + MaybeDetached::Attached(a) => a.with_state(|state| { + let a = state.as_tree_state().unwrap(); + a.get_index_by_position(&TreeParentId::from(parent), node_position) + }), + } + } + + pub(crate) fn next_idlp(&self) -> IdLp { + match &self.inner { + MaybeDetached::Detached(_) => { + unreachable!() + } + MaybeDetached::Attached(a) => a.with_txn(|txn| Ok(txn.next_idlp())).unwrap(), + } + } } diff --git a/crates/loro-internal/src/loro.rs b/crates/loro-internal/src/loro.rs index 313f92c6..1583619f 100644 --- a/crates/loro-internal/src/loro.rs +++ b/crates/loro-internal/src/loro.rs @@ -831,6 +831,8 @@ impl LoroDoc { before_diff, ); + // println!("\nundo_internal: diff: {:?}", diff); + self.checkout_without_emitting(&latest_frontiers)?; self.detached.store(false, Release); if was_recording { @@ -927,10 +929,7 @@ impl LoroDoc { } let h = self.get_handler(id); - h.apply_diff(diff, &mut |old_id, new_id| { - container_remap.insert(old_id, new_id); - }) - .unwrap(); + h.apply_diff(diff, container_remap).unwrap(); } Ok(()) @@ -1083,7 +1082,6 @@ impl LoroDoc { format!("Cannot find the specified version {:?}", frontiers).into_boxed_str(), )); }; - let diff = calc.calc_diff_internal( &oplog, before, diff --git a/crates/loro-internal/src/obs.rs b/crates/loro-internal/src/obs.rs index 91c98726..d6c5b307 100644 --- a/crates/loro-internal/src/obs.rs +++ b/crates/loro-internal/src/obs.rs @@ -100,7 +100,6 @@ impl Observer { self.inner.lock().unwrap().event_queue.push(doc_diff); return; } - let mut inner = self.take_inner(); self.emit_inner(&doc_diff, &mut inner); self.reset_inner(inner); diff --git a/crates/loro-internal/src/state.rs b/crates/loro-internal/src/state.rs index 2dd72e0b..cea58160 100644 --- a/crates/loro-internal/src/state.rs +++ b/crates/loro-internal/src/state.rs @@ -8,12 +8,13 @@ use enum_dispatch::enum_dispatch; use fxhash::{FxHashMap, FxHashSet}; use loro_common::{ContainerID, LoroError, LoroResult}; use loro_delta::DeltaItem; -use tracing::{info, instrument}; +use tracing::instrument; use crate::{ configure::{Configure, DefaultRandom, SecureRandomGenerator}, container::{idx::ContainerIdx, richtext::config::StyleConfigMap, ContainerIdRaw}, cursor::Cursor, + delta::TreeExternalDiff, diff_calc::DiffCalculator, encoding::{StateSnapshotDecodeContext, StateSnapshotEncoder}, event::{Diff, EventTriggerKind, Index, InternalContainerDiff, InternalDiff}, @@ -39,7 +40,9 @@ pub(crate) use self::movable_list_state::{IndexType, MovableListState}; pub(crate) use list_state::ListState; pub(crate) use map_state::MapState; pub(crate) use richtext_state::RichtextState; -pub(crate) use tree_state::{get_meta_value, FractionalIndexGenResult, TreeParentId, TreeState}; +pub(crate) use tree_state::{ + get_meta_value, FractionalIndexGenResult, NodePosition, TreeParentId, TreeState, +}; use self::unknown_state::UnknownState; @@ -444,7 +447,6 @@ impl DocState { // We need to ensure diff is processed in order diffs.sort_by_cached_key(|diff| self.arena.get_depth(diff.idx).unwrap()); - let mut to_revive_in_next_layer: FxHashSet = FxHashSet::default(); let mut to_revive_in_this_layer: FxHashSet = FxHashSet::default(); let mut last_depth = 0; @@ -470,9 +472,13 @@ impl DocState { let external_diff = state.to_diff(&self.arena, &self.global_txn, &self.weak_state); - trigger_on_new_container(&external_diff, |cid| { - to_revive_in_this_layer.insert(cid); - }); + trigger_on_new_container( + &external_diff, + |cid| { + to_revive_in_this_layer.insert(cid); + }, + &self.arena, + ); diffs.push(InternalContainerDiff { idx: new, @@ -493,9 +499,13 @@ impl DocState { let state = get_or_create!(self, diff.idx); let extern_diff = state.to_diff(&self.arena, &self.global_txn, &self.weak_state); - trigger_on_new_container(&extern_diff, |cid| { - to_revive_in_next_layer.insert(cid); - }); + trigger_on_new_container( + &extern_diff, + |cid| { + to_revive_in_next_layer.insert(cid); + }, + &self.arena, + ); diff.diff = extern_diff.into(); } } @@ -523,9 +533,13 @@ impl DocState { &self.weak_state, ) }; - trigger_on_new_container(&external_diff, |cid| { - to_revive_in_next_layer.insert(cid); - }); + trigger_on_new_container( + &external_diff, + |cid| { + to_revive_in_next_layer.insert(cid); + }, + &self.arena, + ); diff.diff = external_diff.into(); } else { state.apply_diff( @@ -540,7 +554,9 @@ impl DocState { } to_revive_in_this_layer.remove(&idx); - diffs.push(diff); + if !diff.diff.is_empty() { + diffs.push(diff); + } } // Revive the last several layers @@ -559,16 +575,22 @@ impl DocState { } let external_diff = state.to_diff(&self.arena, &self.global_txn, &self.weak_state); - trigger_on_new_container(&external_diff, |cid| { - to_revive_in_next_layer.insert(cid); - }); + trigger_on_new_container( + &external_diff, + |cid| { + to_revive_in_next_layer.insert(cid); + }, + &self.arena, + ); - diffs.push(InternalContainerDiff { - idx: new, - bring_back: true, - is_container_deleted: false, - diff: external_diff.into(), - }); + if !external_diff.is_empty() { + diffs.push(InternalContainerDiff { + idx: new, + bring_back: true, + is_container_deleted: false, + diff: external_diff.into(), + }); + } } to_revive_in_this_layer = std::mem::take(&mut to_revive_in_next_layer); @@ -1054,7 +1076,7 @@ impl DocState { // this container may be deleted let Ok(prop) = id.clone().into_root() else { let id = format!("{}", &id); - info!(?id, "Missing parent - container is deleted"); + tracing::info!(?id, "Missing parent - container is deleted"); return None; }; ans.push((id, Index::Key(prop.0))); @@ -1309,7 +1331,11 @@ impl DocState { } } -fn trigger_on_new_container(state_diff: &Diff, mut listener: impl FnMut(ContainerIdx)) { +fn trigger_on_new_container( + state_diff: &Diff, + mut listener: impl FnMut(ContainerIdx), + arena: &SharedArena, +) { match state_diff { Diff::List(list) => { for delta in list.iter() { @@ -1340,6 +1366,14 @@ fn trigger_on_new_container(state_diff: &Diff, mut listener: impl FnMut(Containe } } } + Diff::Tree(tree) => { + for item in tree.iter() { + if matches!(item.action, TreeExternalDiff::Create { .. }) { + let id = item.target.associated_meta_container(); + listener(arena.id_to_idx(&id).unwrap()); + } + } + } _ => {} }; } diff --git a/crates/loro-internal/src/state/tree_state.rs b/crates/loro-internal/src/state/tree_state.rs index caa55897..67b46480 100644 --- a/crates/loro-internal/src/state/tree_state.rs +++ b/crates/loro-internal/src/state/tree_state.rs @@ -57,7 +57,7 @@ impl From> for TreeParentId { } } -#[derive(Clone)] +#[derive(Debug, Clone)] enum NodeChildren { Vec(Vec<(NodePosition, TreeID)>), BTree(btree::ChildTree), @@ -77,6 +77,16 @@ impl NodeChildren { } } + fn get_last_insert_index_by_position( + &self, + node_position: &NodePosition, + ) -> Result { + match self { + NodeChildren::Vec(v) => v.binary_search_by_key(&node_position, |x| &x.0), + NodeChildren::BTree(btree) => btree.get_index_by_node_position(node_position), + } + } + fn get_node_position_at(&self, pos: usize) -> Option<&NodePosition> { match self { NodeChildren::Vec(v) => v.get(pos).map(|x| &x.0), @@ -322,6 +332,33 @@ mod btree { Some(ans) } + + pub(super) fn get_index_by_node_position( + &self, + node_position: &NodePosition, + ) -> Result { + let Some(res) = self.tree.query::(node_position) else { + return Ok(0); + }; + let mut ans = 0; + self.tree + .visit_previous_caches(res.cursor, |prev| match prev { + generic_btree::PreviousCache::NodeCache(c) => { + ans += c.len; + } + generic_btree::PreviousCache::PrevSiblingElem(_) => { + ans += 1; + } + generic_btree::PreviousCache::ThisElemAndOffset { elem: _, offset } => { + ans += offset; + } + }); + if res.found { + Ok(ans) + } else { + Err(ans) + } + } } #[derive(Clone, Debug)] @@ -525,13 +562,13 @@ pub struct TreeState { jitter: u8, } -#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord)] -struct NodePosition { - position: FractionalIndex, +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub(crate) struct NodePosition { + pub(crate) position: FractionalIndex, // different nodes created by a peer may have the same position // when we merge updates that cause cycles. // for example [::fuzz::test::test_tree::same_peer_have_same_position()] - idlp: IdLp, + pub(crate) idlp: IdLp, } impl NodePosition { @@ -584,22 +621,10 @@ impl TreeState { self.delete_position(&old_parent, target); } - if !parent.is_deleted() { - let entry = self.children.entry(parent).or_default(); - let node_position = NodePosition::new(position.clone().unwrap(), id.idlp()); - debug_assert!(!entry.has_child(&node_position)); - entry.insert_child(node_position, target); - } else { - // clean the cache recursively, otherwise the index of event will be calculated incorrectly - let mut q = vec![target]; - while let Some(id) = q.pop() { - let parent = TreeParentId::from(Some(id)); - if let Some(children) = self.children.get(&parent) { - q.extend(children.iter().map(|x| x.1)); - } - self.children.remove(&parent); - } - } + let entry = self.children.entry(parent).or_default(); + let node_position = NodePosition::new(position.clone().unwrap_or_default(), id.idlp()); + debug_assert!(!entry.has_child(&node_position)); + entry.insert_child(node_position, target); self.trees.insert( target, @@ -725,11 +750,10 @@ impl TreeState { self.children.get(parent).map(|x| x.len()) } - pub fn children(&self, parent: &TreeParentId) -> Vec { + pub fn children(&self, parent: &TreeParentId) -> Option> { self.children .get(parent) .map(|x| x.iter().map(|x| *x.1).collect()) - .unwrap_or_default() } /// Determine whether the target is the child of the node @@ -782,6 +806,19 @@ impl TreeState { .flatten() } + pub(crate) fn get_index_by_position( + &self, + parent: &TreeParentId, + node_position: &NodePosition, + ) -> Option { + self.children.get(parent).map(|c| { + match c.get_last_insert_index_by_position(node_position) { + Ok(i) => i, + Err(i) => i, + } + }) + } + pub(crate) fn get_id_by_index(&self, parent: &TreeParentId, index: usize) -> Option { (!parent.is_deleted()) .then(|| self.children.get(parent).and_then(|x| x.get_id_at(index))) @@ -838,8 +875,6 @@ impl ContainerState for TreeState { }); } TreeInternalDiff::Move { parent, position } => { - let old_parent = self.trees.get(&target).unwrap().parent; - let old_index = self.get_index_by_tree_id(&target).unwrap(); self.mov(target, *parent, last_move_op, Some(position.clone()), false) .unwrap(); let index = self.get_index_by_tree_id(&target).unwrap(); @@ -849,22 +884,15 @@ impl ContainerState for TreeState { parent: parent.into_node().ok(), index, position: position.clone(), - old_parent, - old_index, }, }); } TreeInternalDiff::Delete { parent, position } => { - let old_parent = self.trees.get(&target).unwrap().parent; - let old_index = self.get_index_by_tree_id(&target).unwrap(); self.mov(target, *parent, last_move_op, position.clone(), false) .unwrap(); ans.push(TreeDiffItem { target, - action: TreeExternalDiff::Delete { - old_parent, - old_index, - }, + action: TreeExternalDiff::Delete, }); } TreeInternalDiff::MoveInDelete { parent, position } => { @@ -872,15 +900,13 @@ impl ContainerState for TreeState { .unwrap(); } TreeInternalDiff::UnCreate => { - let old_parent = self.trees.get(&target).unwrap().parent; - let old_index = self.get_index_by_tree_id(&target).unwrap(); - ans.push(TreeDiffItem { - target, - action: TreeExternalDiff::Delete { - old_parent, - old_index, - }, - }); + // maybe the node created and moved to the parent deleted + if !self.is_node_deleted(&target) { + ans.push(TreeDiffItem { + target, + action: TreeExternalDiff::Delete, + }); + } // delete it from state let parent = self.trees.remove(&target); if let Some(parent) = parent { diff --git a/crates/loro-internal/src/txn.rs b/crates/loro-internal/src/txn.rs index 54e9cad5..27204197 100644 --- a/crates/loro-internal/src/txn.rs +++ b/crates/loro-internal/src/txn.rs @@ -123,7 +123,8 @@ pub(super) enum EventHint { key: InternalString, value: Option, }, - Tree(TreeDiffItem), + // use vec because we could bring back some node that has children + Tree(SmallVec<[TreeDiffItem; 1]>), MarkEnd, #[cfg(feature = "counter")] Counter(f64), @@ -393,6 +394,7 @@ impl Transaction { let op = self.arena.convert_raw_op(&raw_op); state.apply_local_op(&raw_op, &op)?; drop(state); + debug_assert_eq!( event.rle_len(), op.atom_len(), @@ -400,6 +402,7 @@ impl Transaction { &event, &op ); + match self.event_hints.last_mut() { Some(last) if last.can_merge(&event) => { last.merge_right(&event); @@ -490,6 +493,13 @@ impl Transaction { } } + pub fn next_idlp(&self) -> IdLp { + IdLp { + peer: self.peer, + lamport: self.next_lamport, + } + } + pub fn is_empty(&self) -> bool { self.local_ops.is_empty() } @@ -657,7 +667,7 @@ fn change_to_diff( }), EventHint::Tree(tree_diff) => { let mut diff = TreeDiff::default(); - diff.push(tree_diff); + diff.diff.extend(tree_diff.into_iter()); ans.push(TxnContainerDiff { idx: op.container, diff: Diff::Tree(diff), @@ -716,6 +726,5 @@ fn change_to_diff( .map(|x| x.content_len() as Lamport) .sum::(); } - ans } diff --git a/crates/loro-internal/src/undo.rs b/crates/loro-internal/src/undo.rs index 26b80194..16d17536 100644 --- a/crates/loro-internal/src/undo.rs +++ b/crates/loro-internal/src/undo.rs @@ -40,9 +40,11 @@ impl DiffBatch { return; } - for (idx, diff) in self.0.iter_mut() { - if let Some(b_diff) = other.0.get(idx) { - diff.compose_ref(b_diff); + for (idx, diff) in other.0.iter() { + if let Some(this_diff) = self.0.get_mut(idx) { + this_diff.compose_ref(diff); + } else { + self.0.insert(idx.clone(), diff.clone()); } } } @@ -146,7 +148,7 @@ pub type OnPush = Box UndoItemMeta + Send + S pub type OnPop = Box; struct UndoManagerInner { - latest_counter: Counter, + latest_counter: Option, undo_stack: Stack, redo_stack: Stack, processing_undo: bool, @@ -180,7 +182,7 @@ struct Stack { size: usize, } -#[derive(Debug)] +#[derive(Debug, Clone)] struct StackItem { span: CounterSpan, meta: UndoItemMeta, @@ -210,7 +212,7 @@ impl UndoItemMeta { } } - /// It's assumed that the cursor is just acqured before the ops that + /// It's assumed that the cursor is just acquired before the ops that /// need to be undo/redo. /// /// We need to rely on the validity of the original pos value @@ -271,19 +273,14 @@ impl Stack { pub fn push_with_merge(&mut self, span: CounterSpan, meta: UndoItemMeta, can_merge: bool) { let last = self.stack.back_mut().unwrap(); - let mut last_remote_diff = last.1.try_lock().unwrap(); + let last_remote_diff = last.1.try_lock().unwrap(); if !last_remote_diff.0.is_empty() { // If the remote diff is not empty, we cannot merge - if last.0.is_empty() { - last.0.push_back(StackItem { span, meta }); - last_remote_diff.clear(); - } else { - drop(last_remote_diff); - let mut v = VecDeque::new(); - v.push_back(StackItem { span, meta }); - self.stack - .push_back((v, Arc::new(Mutex::new(DiffBatch::default())))); - } + drop(last_remote_diff); + let mut v = VecDeque::new(); + v.push_back(StackItem { span, meta }); + self.stack + .push_back((v, Arc::new(Mutex::new(DiffBatch::default())))); self.size += 1; } else { @@ -322,7 +319,6 @@ impl Stack { if self.is_empty() { return; } - let remote_diff = &mut self.stack.back_mut().unwrap().1; remote_diff.try_lock().unwrap().transform(diff, false); } @@ -365,7 +361,7 @@ impl Default for Stack { impl UndoManagerInner { fn new(last_counter: Counter) -> Self { UndoManagerInner { - latest_counter: last_counter, + latest_counter: Some(last_counter), undo_stack: Default::default(), redo_stack: Default::default(), processing_undo: false, @@ -380,13 +376,18 @@ impl UndoManagerInner { } fn record_checkpoint(&mut self, latest_counter: Counter) { - if latest_counter == self.latest_counter { + if Some(latest_counter) == self.latest_counter { return; } - assert!(self.latest_counter < latest_counter); + if self.latest_counter.is_none() { + self.latest_counter = Some(latest_counter); + return; + } + + assert!(self.latest_counter.unwrap() < latest_counter); let now = get_sys_timestamp(); - let span = CounterSpan::new(self.latest_counter, latest_counter); + let span = CounterSpan::new(self.latest_counter.unwrap(), latest_counter); let meta = self .on_push .as_ref() @@ -400,7 +401,7 @@ impl UndoManagerInner { self.undo_stack.push(span, meta); } - self.latest_counter = latest_counter; + self.latest_counter = Some(latest_counter); self.redo_stack.clear(); while self.undo_stack.len() > self.max_stack_size { self.undo_stack.pop_front(); @@ -445,7 +446,7 @@ impl UndoManager { // a remote event. inner.undo_stack.compose_remote_event(event.events); inner.redo_stack.compose_remote_event(event.events); - inner.latest_counter = id.counter + 1; + inner.latest_counter = Some(id.counter + 1); } else { inner.record_checkpoint(id.counter + 1); } @@ -456,7 +457,12 @@ impl UndoManager { inner.undo_stack.compose_remote_event(event.events); inner.redo_stack.compose_remote_event(event.events); } - EventTriggerKind::Checkout => {} + EventTriggerKind::Checkout => { + let mut inner = inner_clone.try_lock().unwrap(); + inner.undo_stack.clear(); + inner.redo_stack.clear(); + inner.latest_counter = None; + } })); UndoManager { @@ -648,7 +654,7 @@ impl UndoManager { } get_opposite(&mut inner).push(CounterSpan::new(end_counter, new_counter), meta); - inner.latest_counter = new_counter; + inner.latest_counter = Some(new_counter); executed = true; break; } else { @@ -749,7 +755,6 @@ pub(crate) fn undo( // ------------------------------------------------------------------------------ // 1.b Transform and apply Ci-1 based on Ai, call it A'i // ------------------------------------------------------------------------------ - last_ci.transform(&event_a_i, true); event_a_i.compose(&last_ci); @@ -760,13 +765,12 @@ pub(crate) fn undo( if i == spans.len() - 1 { on_last_event_a(&event_a_prime); } - // -------------------------------------------------- // 3. Transform event A'_i based on B_i, call it C_i // -------------------------------------------------- event_a_prime.transform(event_b_i, true); - let c_i = event_a_prime; + let c_i = event_a_prime; last_ci = Some(c_i); }); } diff --git a/crates/loro-internal/tests/test.rs b/crates/loro-internal/tests/test.rs index 6ca9b499..72fab027 100644 --- a/crates/loro-internal/tests/test.rs +++ b/crates/loro-internal/tests/test.rs @@ -720,26 +720,18 @@ fn map_concurrent_checkout() { #[test] fn tree_checkout() { - let doc_a = LoroDoc::new(); + let doc_a = LoroDoc::new_auto_commit(); doc_a.subscribe_root(Arc::new(|_e| {})); doc_a.set_peer_id(1).unwrap(); let tree = doc_a.get_tree("root"); - let id1 = doc_a - .with_txn(|txn| tree.create_with_txn(txn, None, 0)) - .unwrap(); - let id2 = doc_a - .with_txn(|txn| tree.create_with_txn(txn, id1, 0)) - .unwrap(); + let id1 = tree.create(None).unwrap(); + let id2 = tree.create(id1).unwrap(); let v1_state = tree.get_deep_value(); let v1 = doc_a.oplog_frontiers(); - let _id3 = doc_a - .with_txn(|txn| tree.create_with_txn(txn, id2, 0)) - .unwrap(); + let _id3 = tree.create(id2).unwrap(); let v2_state = tree.get_deep_value(); let v2 = doc_a.oplog_frontiers(); - doc_a - .with_txn(|txn| tree.delete_with_txn(txn, id2)) - .unwrap(); + tree.delete(id2).unwrap(); let v3_state = tree.get_deep_value(); let v3 = doc_a.oplog_frontiers(); doc_a.checkout(&v1).unwrap(); @@ -765,12 +757,7 @@ fn tree_checkout() { ); doc_a.attach(); - doc_a - .with_txn(|txn| { - tree.create_with_txn(txn, None, 0) - //tree.insert_meta(txn, id1, "a", 1.into()) - }) - .unwrap(); + tree.create(None).unwrap(); } #[test] diff --git a/crates/loro-wasm/Cargo.toml b/crates/loro-wasm/Cargo.toml index dc210c34..e5860907 100644 --- a/crates/loro-wasm/Cargo.toml +++ b/crates/loro-wasm/Cargo.toml @@ -10,7 +10,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] js-sys = "0.3.60" -loro-internal = { path = "../loro-internal", features = ["wasm"] } +loro-internal = { path = "../loro-internal", features = ["wasm", "counter"] } wasm-bindgen = "=0.2.92" serde-wasm-bindgen = { version = "^0.6.5" } wasm-bindgen-derive = "0.2.1" @@ -24,4 +24,3 @@ serde_json = "1" [features] default = ["console_error_panic_hook"] -counter = ["loro-internal/counter"] diff --git a/crates/loro-wasm/scripts/build.ts b/crates/loro-wasm/scripts/build.ts index 9cff1d10..ddce6f93 100644 --- a/crates/loro-wasm/scripts/build.ts +++ b/crates/loro-wasm/scripts/build.ts @@ -59,7 +59,7 @@ async function build() { async function cargoBuild() { const cmd = - `cargo build --features counter --target wasm32-unknown-unknown --profile ${profile}`; + `cargo build --target wasm32-unknown-unknown --profile ${profile}`; console.log(cmd); const status = await Deno.run({ cmd: cmd.split(" "), diff --git a/crates/loro-wasm/src/convert.rs b/crates/loro-wasm/src/convert.rs index 6f97eabd..9cb6b58a 100644 --- a/crates/loro-wasm/src/convert.rs +++ b/crates/loro-wasm/src/convert.rs @@ -9,15 +9,12 @@ use loro_internal::{ListDiffItem, LoroDoc, LoroValue}; use wasm_bindgen::JsValue; use crate::{ - frontiers_to_ids, Container, Cursor, JsContainer, JsImportBlobMetadata, LoroList, LoroMap, - LoroMovableList, LoroText, LoroTree, + frontiers_to_ids, Container, Cursor, JsContainer, JsImportBlobMetadata, LoroCounter, LoroList, + LoroMap, LoroMovableList, LoroText, LoroTree, }; use wasm_bindgen::__rt::IntoJsResult; use wasm_bindgen::convert::RefFromWasmAbi; -#[cfg(feature = "counter")] -use crate::LoroCounter; - /// Convert a `JsValue` to `T` by constructor's name. /// /// more details can be found in https://github.com/rustwasm/wasm-bindgen/issues/2231#issuecomment-656293288 @@ -137,7 +134,6 @@ pub(crate) fn resolved_diff_to_js(value: &Diff, doc: &Arc) -> JsValue { .unwrap(); } - #[cfg(feature = "counter")] Diff::Counter(v) => { js_sys::Reflect::set( &obj, @@ -345,7 +341,6 @@ pub(crate) fn handler_to_js_value(handler: Handler, doc: Option>) - Handler::List(l) => LoroList { handler: l, doc }.into(), Handler::Tree(t) => LoroTree { handler: t, doc }.into(), Handler::MovableList(m) => LoroMovableList { handler: m, doc }.into(), - #[cfg(feature = "counter")] Handler::Counter(c) => LoroCounter { handler: c, doc }.into(), Handler::Unknown(_) => unreachable!(), } diff --git a/crates/loro-wasm/src/lib.rs b/crates/loro-wasm/src/lib.rs index dd79eb33..3b9bfb2f 100644 --- a/crates/loro-wasm/src/lib.rs +++ b/crates/loro-wasm/src/lib.rs @@ -29,12 +29,9 @@ use std::{cell::RefCell, cmp::Ordering, rc::Rc, sync::Arc}; use wasm_bindgen::{__rt::IntoJsResult, prelude::*, throw_val}; use wasm_bindgen_derive::TryFromJsValue; -#[cfg(feature = "counter")] mod counter; -#[cfg(feature = "counter")] pub use counter::LoroCounter; -#[cfg(feature = "counter")] -use loro_internal::handler::counter::CounterHandler; + mod awareness; mod log; @@ -316,7 +313,7 @@ impl Loro { /// If enabled, the Unix timestamp will be recorded for each change automatically. /// /// You can also set each timestamp manually when you commit a change. - /// The timstamp manually set will override the automatic one. + /// The timestamp manually set will override the automatic one. /// /// NOTE: Timestamps are forced to be in ascending order. /// If you commit a new change with a timestamp that is less than the existing one, @@ -696,7 +693,6 @@ impl Loro { } /// Get a LoroCounter by container id - #[cfg(feature = "counter")] #[wasm_bindgen(js_name = "getCounter")] pub fn get_counter(&self, cid: &JsIntoContainerID) -> JsResult { let counter = self @@ -789,7 +785,6 @@ impl Loro { } .into() } - #[cfg(feature = "counter")] ContainerType::Counter => { let counter = self.0.get_counter(container_id); LoroCounter { @@ -1713,7 +1708,7 @@ impl LoroText { } } - /// Whether the container is attached to a docuemnt. + /// Whether the container is attached to a document. /// /// If it's detached, the operations on the container will not be persisted. #[wasm_bindgen(js_name = "isAttached")] @@ -2070,7 +2065,7 @@ impl LoroMap { } } - /// Whether the container is attached to a docuemnt. + /// Whether the container is attached to a document. /// /// If it's detached, the operations on the container will not be persisted. #[wasm_bindgen(js_name = "isAttached")] @@ -2358,7 +2353,7 @@ impl LoroList { } } - /// Whether the container is attached to a docuemnt. + /// Whether the container is attached to a document. /// /// If it's detached, the operations on the container will not be persisted. #[wasm_bindgen(js_name = "isAttached")] @@ -2685,7 +2680,7 @@ impl LoroMovableList { } } - /// Whether the container is attached to a docuemnt. + /// Whether the container is attached to a document. /// /// If it's detached, the operations on the container will not be persisted. #[wasm_bindgen(js_name = "isAttached")] @@ -2999,13 +2994,15 @@ impl LoroTreeNode { /// The objects returned are new js objects each time because they need to cross /// the WASM boundary. #[wasm_bindgen(skip_typescript)] - pub fn children(&self) -> Array { - let children = self.tree.children(Some(self.id)).unwrap_or_default(); + pub fn children(&self) -> JsValue { + let Some(children) = self.tree.children(Some(self.id)) else { + return JsValue::undefined(); + }; let children = children.into_iter().map(|c| { let node = LoroTreeNode::from_tree(c, self.tree.clone(), self.doc.clone()); JsValue::from(node) }); - Array::from_iter(children) + Array::from_iter(children).into() } } @@ -3316,7 +3313,7 @@ impl LoroTree { } } - /// Whether the container is attached to a docuemnt. + /// Whether the container is attached to a document. /// /// If it's detached, the operations on the container will not be persisted. #[wasm_bindgen(js_name = "isAttached")] @@ -3395,7 +3392,7 @@ impl Cursor { /// Get the ID that represents the position. /// - /// It can be undefined if it's not binded into a specific ID. + /// It can be undefined if it's not bind into a specific ID. pub fn pos(&self) -> Option { match self.pos.id { Some(id) => { diff --git a/crates/loro/src/lib.rs b/crates/loro/src/lib.rs index 3e073da7..ee7def81 100644 --- a/crates/loro/src/lib.rs +++ b/crates/loro/src/lib.rs @@ -1377,6 +1377,8 @@ impl LoroTree { } /// Return all children of the target node. + /// + /// If the parent node does not exist, return `None`. pub fn children(&self, parent: Option) -> Option> { self.handler.children(parent) } diff --git a/crates/loro/tests/integration_test/undo_test.rs b/crates/loro/tests/integration_test/undo_test.rs index 8d1e47dd..6b6bf4fa 100644 --- a/crates/loro/tests/integration_test/undo_test.rs +++ b/crates/loro/tests/integration_test/undo_test.rs @@ -1209,7 +1209,8 @@ fn undo_tree_concurrent_delete2() -> LoroResult<()> { .get("id") .unwrap() .to_json_value(), - json!("1@1") + // create a new node + json!("1@2") ); Ok(()) } diff --git a/loro-js/src/index.ts b/loro-js/src/index.ts index 68250317..ce3f6e64 100644 --- a/loro-js/src/index.ts +++ b/loro-js/src/index.ts @@ -638,7 +638,7 @@ declare module "loro-wasm" { * The objects returned are new js objects each time because they need to cross * the WASM boundary. */ - children(): Array>; + children(): Array> | undefined; } interface AwarenessWasm { diff --git a/loro-js/tests/tree.test.ts b/loro-js/tests/tree.test.ts index b782d2dc..54573c48 100644 --- a/loro-js/tests/tree.test.ts +++ b/loro-js/tests/tree.test.ts @@ -34,7 +34,7 @@ describe("loro tree", () => { assertEquals(child2.parent()!.id, root.id); tree.move(child2.id, child.id); assertEquals(child2.parent()!.id, child.id); - assertEquals(child.children()[0].id, child2.id); + assertEquals(child.children()![0].id, child2.id); expect(()=>tree.move(child2.id, child.id, 1)).toThrowError(); }); @@ -70,9 +70,9 @@ describe("loro tree", () => { const root = tree.createNode(); const child = tree.createNode(root.id); const child2 = tree.createNode(root.id); - assertEquals(root.children().length, 2); - assertEquals(root.children()[0].id, child.id); - assertEquals(root.children()[1].id, child2.id); + assertEquals(root.children()!.length, 2); + assertEquals(root.children()![0].id, child.id); + assertEquals(root.children()![1].id, child2.id); }); it("toArray", ()=>{ @@ -141,7 +141,7 @@ describe("loro tree node", ()=>{ assertEquals(child2.parent()!.id, root.id); child2.move(child); assertEquals(child2.parent()!.id, child.id); - assertEquals(child.children()[0].id, child2.id); + assertEquals(child.children()![0].id, child2.id); expect(()=>child2.move(child, 1)).toThrowError(); });