mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 05:24:51 +00:00
* fix: text tracker error #211 * chore: rm only * fix: refine
This commit is contained in:
parent
3c735390e9
commit
ab64881029
5 changed files with 117 additions and 108 deletions
99
crates/loro-internal/fuzz/Cargo.lock
generated
99
crates/loro-internal/fuzz/Cargo.lock
generated
|
@ -2,24 +2,12 @@
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "Inflector"
|
|
||||||
version = "0.11.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "adler"
|
name = "adler"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aliasable"
|
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "append-only-bytes"
|
name = "append-only-bytes"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
@ -71,12 +59,6 @@ dependencies = [
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bumpalo"
|
|
||||||
version = "3.11.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecount"
|
name = "bytecount"
|
||||||
version = "0.6.3"
|
version = "0.6.3"
|
||||||
|
@ -116,17 +98,6 @@ version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crdt-list"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e257356d86ea7330c1105eaf6041c6df627c5b1968f2338021df88b19de1875a"
|
|
||||||
dependencies = [
|
|
||||||
"arbitrary",
|
|
||||||
"arref",
|
|
||||||
"rand",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "critical-section"
|
name = "critical-section"
|
||||||
version = "1.1.1"
|
version = "1.1.1"
|
||||||
|
@ -389,7 +360,6 @@ dependencies = [
|
||||||
"append-only-bytes",
|
"append-only-bytes",
|
||||||
"arbitrary",
|
"arbitrary",
|
||||||
"arref",
|
"arref",
|
||||||
"crdt-list",
|
|
||||||
"debug-log",
|
"debug-log",
|
||||||
"enum-as-inner 0.5.1",
|
"enum-as-inner 0.5.1",
|
||||||
"enum_dispatch",
|
"enum_dispatch",
|
||||||
|
@ -413,14 +383,12 @@ dependencies = [
|
||||||
"string_cache",
|
"string_cache",
|
||||||
"tabled",
|
"tabled",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tracing",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "loro-internal-fuzz"
|
name = "loro-internal-fuzz"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crdt-list",
|
|
||||||
"libfuzzer-sys",
|
"libfuzzer-sys",
|
||||||
"loro-internal",
|
"loro-internal",
|
||||||
]
|
]
|
||||||
|
@ -441,14 +409,10 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"append-only-bytes",
|
"append-only-bytes",
|
||||||
"arref",
|
"arref",
|
||||||
"bumpalo",
|
|
||||||
"crdt-list",
|
|
||||||
"debug-log",
|
"debug-log",
|
||||||
"enum-as-inner 0.5.1",
|
"enum-as-inner 0.6.0",
|
||||||
"fxhash",
|
"fxhash",
|
||||||
"heapless",
|
|
||||||
"num",
|
"num",
|
||||||
"ouroboros",
|
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -555,29 +519,6 @@ version = "1.18.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ouroboros"
|
|
||||||
version = "0.15.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dfbb50b356159620db6ac971c6d5c9ab788c9cc38a6f49619fca2a27acb062ca"
|
|
||||||
dependencies = [
|
|
||||||
"aliasable",
|
|
||||||
"ouroboros_macro",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "ouroboros_macro"
|
|
||||||
version = "0.15.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4a0d9d1a6191c4f391f87219d1ea42b23f09ee84d64763cd05ee6ea88d9f384d"
|
|
||||||
dependencies = [
|
|
||||||
"Inflector",
|
|
||||||
"proc-macro-error",
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.105",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "papergrid"
|
name = "papergrid"
|
||||||
version = "0.7.1"
|
version = "0.7.1"
|
||||||
|
@ -621,12 +562,6 @@ dependencies = [
|
||||||
"siphasher",
|
"siphasher",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "pin-project-lite"
|
|
||||||
version = "0.2.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "postcard"
|
name = "postcard"
|
||||||
version = "1.0.2"
|
version = "1.0.2"
|
||||||
|
@ -949,38 +884,6 @@ dependencies = [
|
||||||
"syn 2.0.25",
|
"syn 2.0.25",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing"
|
|
||||||
version = "0.1.37"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ce8c33a8d48bd45d624a6e523445fd21ec13d3653cd51f681abf67418f54eb8"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tracing-attributes",
|
|
||||||
"tracing-core",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-attributes"
|
|
||||||
version = "0.1.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4017f8f45139870ca7e672686113917c71c7a6e02d4924eda67186083c03081a"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 1.0.105",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tracing-core"
|
|
||||||
version = "0.1.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "24eb03ba0eab1fd845050058ce5e616558e8f8d8fca633e6b163fe25c797213a"
|
|
||||||
dependencies = [
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use generic_btree::LeafIndex;
|
use generic_btree::{rle::Sliceable, LeafIndex};
|
||||||
use loro_common::{Counter, IdSpan, PeerID, ID};
|
use loro_common::{Counter, IdSpan, PeerID, ID};
|
||||||
use rle::HasLength;
|
use rle::HasLength;
|
||||||
|
|
||||||
|
@ -69,10 +69,11 @@ impl Tracker {
|
||||||
&self.applied_vv
|
&self.applied_vv
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn insert(&mut self, op_id: ID, pos: usize, content: RichtextChunk) {
|
pub(crate) fn insert(&mut self, mut op_id: ID, mut pos: usize, mut content: RichtextChunk) {
|
||||||
if self.applied_vv.includes_id(op_id) {
|
let last_id = op_id.inc(content.len() as Counter - 1);
|
||||||
let last_id = op_id.inc(content.len() as Counter - 1);
|
let applied_counter_end = self.applied_vv.get(&last_id.peer).copied().unwrap_or(0);
|
||||||
assert!(self.applied_vv.includes_id(last_id));
|
if applied_counter_end > last_id.counter {
|
||||||
|
// the op is included in the applied vv
|
||||||
if !self.current_vv.includes_id(last_id) {
|
if !self.current_vv.includes_id(last_id) {
|
||||||
// PERF: may be slow
|
// PERF: may be slow
|
||||||
let mut updates = Default::default();
|
let mut updates = Default::default();
|
||||||
|
@ -87,6 +88,12 @@ impl Tracker {
|
||||||
self.batch_update(updates, false);
|
self.batch_update(updates, false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else if applied_counter_end > op_id.counter {
|
||||||
|
// need to slice the content
|
||||||
|
let start = (applied_counter_end - op_id.counter) as usize;
|
||||||
|
op_id.counter = applied_counter_end;
|
||||||
|
pos += start;
|
||||||
|
content = content.slice(start..);
|
||||||
}
|
}
|
||||||
|
|
||||||
// debug_log::group!("before insert {} pos={}", op_id, pos);
|
// debug_log::group!("before insert {} pos={}", op_id, pos);
|
||||||
|
@ -127,10 +134,11 @@ impl Tracker {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// If `reverse` is true, the deletion happens from the end of the range to the start.
|
/// If `reverse` is true, the deletion happens from the end of the range to the start.
|
||||||
pub(crate) fn delete(&mut self, op_id: ID, pos: usize, len: usize, reverse: bool) {
|
pub(crate) fn delete(&mut self, mut op_id: ID, mut pos: usize, mut len: usize, reverse: bool) {
|
||||||
if self.applied_vv.includes_id(op_id) {
|
let last_id = op_id.inc(len as Counter - 1);
|
||||||
let last_id = op_id.inc(len as Counter - 1);
|
let applied_counter_end = self.applied_vv.get(&last_id.peer).copied().unwrap_or(0);
|
||||||
assert!(self.applied_vv.includes_id(last_id));
|
if applied_counter_end > last_id.counter {
|
||||||
|
// the op is included in the applied_vv
|
||||||
if !self.current_vv.includes_id(last_id) {
|
if !self.current_vv.includes_id(last_id) {
|
||||||
// PERF: may be slow
|
// PERF: may be slow
|
||||||
let mut updates = Default::default();
|
let mut updates = Default::default();
|
||||||
|
@ -141,6 +149,16 @@ impl Tracker {
|
||||||
self.batch_update(updates, false);
|
self.batch_update(updates, false);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
} else if applied_counter_end > op_id.counter {
|
||||||
|
// need to slice the op
|
||||||
|
let start = (applied_counter_end - op_id.counter) as usize;
|
||||||
|
op_id.counter = applied_counter_end;
|
||||||
|
len -= start;
|
||||||
|
if reverse {
|
||||||
|
pos -= start;
|
||||||
|
} else {
|
||||||
|
pos += start;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ans = Vec::new();
|
let mut ans = Vec::new();
|
||||||
|
|
|
@ -352,7 +352,6 @@ impl CrdtRope {
|
||||||
while let Some((index, elem)) = iter.next() {
|
while let Some((index, elem)) = iter.next() {
|
||||||
// The elements will not be changed by this method.
|
// The elements will not be changed by this method.
|
||||||
// This index is current index of the elem (calculated by `status` field rather than `diff_status` field)
|
// This index is current index of the elem (calculated by `status` field rather than `diff_status` field)
|
||||||
debug_log::debug_dbg!(&index, &elem);
|
|
||||||
match elem.diff() {
|
match elem.diff() {
|
||||||
DiffStatus::NotChanged => {}
|
DiffStatus::NotChanged => {}
|
||||||
DiffStatus::Created => {
|
DiffStatus::Created => {
|
||||||
|
|
|
@ -151,6 +151,7 @@ impl std::fmt::Debug for OpLog {
|
||||||
pub(crate) struct EnsureChangeDepsAreAtTheEnd;
|
pub(crate) struct EnsureChangeDepsAreAtTheEnd;
|
||||||
|
|
||||||
impl OpLog {
|
impl OpLog {
|
||||||
|
#[inline]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
dag: AppDag::default(),
|
dag: AppDag::default(),
|
||||||
|
@ -164,6 +165,7 @@ impl OpLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn new_with_arena(arena: SharedArena) -> Self {
|
pub fn new_with_arena(arena: SharedArena) -> Self {
|
||||||
Self {
|
Self {
|
||||||
dag: AppDag::default(),
|
dag: AppDag::default(),
|
||||||
|
@ -173,10 +175,12 @@ impl OpLog {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn latest_timestamp(&self) -> Timestamp {
|
pub fn latest_timestamp(&self) -> Timestamp {
|
||||||
self.latest_timestamp
|
self.latest_timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn dag(&self) -> &AppDag {
|
pub fn dag(&self) -> &AppDag {
|
||||||
&self.dag
|
&self.dag
|
||||||
}
|
}
|
||||||
|
@ -192,10 +196,12 @@ impl OpLog {
|
||||||
timestamp
|
timestamp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.dag.map.is_empty() && self.arena.can_import_snapshot()
|
self.dag.map.is_empty() && self.arena.can_import_snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
pub fn changes(&self) -> &ClientChanges {
|
pub fn changes(&self) -> &ClientChanges {
|
||||||
&self.changes
|
&self.changes
|
||||||
}
|
}
|
||||||
|
|
83
loro-js/tests/issue.test.ts
Normal file
83
loro-js/tests/issue.test.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
import { describe, expect, expectTypeOf, it } from "vitest";
|
||||||
|
import {
|
||||||
|
Loro,
|
||||||
|
LoroList,
|
||||||
|
LoroMap,
|
||||||
|
isContainer,
|
||||||
|
setPanicHook,
|
||||||
|
toEncodedVersion,
|
||||||
|
getType,
|
||||||
|
} from "../src";
|
||||||
|
import { Container, LoroText, OpId } from "../dist/loro";
|
||||||
|
import { setDebug } from "loro-wasm";
|
||||||
|
|
||||||
|
setPanicHook();
|
||||||
|
|
||||||
|
it("#211", () => {
|
||||||
|
const loro1 = new Loro()
|
||||||
|
loro1.setPeerId(0n)
|
||||||
|
const text1 = loro1.getText("text")
|
||||||
|
|
||||||
|
const loro2 = new Loro()
|
||||||
|
loro2.setPeerId(1n)
|
||||||
|
const text2 = loro2.getText("text")
|
||||||
|
|
||||||
|
console.log("[1] Insert T to #0")
|
||||||
|
text1.insert(0, 'T')
|
||||||
|
loro1.commit()
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
|
||||||
|
console.log("[2] Synchronize")
|
||||||
|
loro1.import(loro2.exportFrom(loro1.version()))
|
||||||
|
loro2.import(loro1.exportFrom(loro2.version()))
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
const frontiers1After2 = loro1.frontiers()
|
||||||
|
const frontiers2After2 = loro2.frontiers()
|
||||||
|
|
||||||
|
console.log("[3] Append A to #0")
|
||||||
|
text1.insert(1, 'A')
|
||||||
|
loro1.commit()
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
|
||||||
|
console.log("[4] Append B to #1")
|
||||||
|
text2.insert(1, 'B')
|
||||||
|
loro2.commit()
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
|
||||||
|
console.log("[5] Play back to the frontiers after 2")
|
||||||
|
loro1.checkout(frontiers1After2)
|
||||||
|
loro2.checkout(frontiers2After2)
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
|
||||||
|
console.log("[6] Check both to the latest")
|
||||||
|
loro1.checkoutToLatest()
|
||||||
|
loro2.checkoutToLatest()
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
const frontiers1Before7 = loro1.frontiers()
|
||||||
|
const frontiers2Before7 = loro2.frontiers()
|
||||||
|
|
||||||
|
console.log("[7] Append B to #1")
|
||||||
|
text2.insert(2, 'B')
|
||||||
|
loro2.commit()
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
|
||||||
|
console.log("[8] Play back to the frontiers before 7")
|
||||||
|
console.log("----------------------------------------------------------");
|
||||||
|
loro1.checkout(frontiers1Before7)
|
||||||
|
console.log("----------------------------------------------------------");
|
||||||
|
loro2.checkout(frontiers2Before7)
|
||||||
|
show(text1, loro1, text2, loro2)
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
function show(text1: LoroText, loro1: Loro, text2: LoroText, loro2: Loro) {
|
||||||
|
console.log(` #0 has content: ${JSON.stringify(text1.toString())}`)
|
||||||
|
console.log(` #0 has frontiers: ${showFrontiers(loro1.frontiers())}`)
|
||||||
|
console.log(` #1 has content: ${JSON.stringify(text2.toString())}`)
|
||||||
|
console.log(` #1 has frontiers: ${showFrontiers(loro2.frontiers())}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
function showFrontiers(frontiers: OpId[]) {
|
||||||
|
return frontiers.map((x) => `${x.peer}@${x.counter}`).join(";");
|
||||||
|
}
|
Loading…
Reference in a new issue