fix: text tracker error #211 (#212)

* fix: text tracker error #211

* chore: rm only

* fix: refine
This commit is contained in:
Zixuan Chen 2023-12-06 23:36:21 +08:00 committed by GitHub
parent 3c735390e9
commit ab64881029
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 117 additions and 108 deletions

View file

@ -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"

View file

@ -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();

View file

@ -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 => {

View file

@ -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
} }

View 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(";");
}