diff --git a/crates/loro-internal/src/container/text/tracker.rs b/crates/loro-internal/src/container/text/tracker.rs index 12dc3fd3..067d87df 100644 --- a/crates/loro-internal/src/container/text/tracker.rs +++ b/crates/loro-internal/src/container/text/tracker.rs @@ -121,7 +121,7 @@ impl Tracker { let mut len = 0; for marker in self .id_to_cursor - .get_range(id_span.min_id().into(), id_span.end_id().into()) + .get_range(id_span.norm_id_start().into(), id_span.norm_id_end().into()) { for span in marker.get_spans(id_span) { len += span.len; diff --git a/crates/loro-internal/src/container/text/tracker/cursor_map.rs b/crates/loro-internal/src/container/text/tracker/cursor_map.rs index 9f9ad046..ff029d04 100644 --- a/crates/loro-internal/src/container/text/tracker/cursor_map.rs +++ b/crates/loro-internal/src/container/text/tracker/cursor_map.rs @@ -245,7 +245,9 @@ impl CursorMap { let mut deletes: Vec<(ID, RleVecWithLen<[IdSpan; 2]>)> = Vec::with_capacity(span.atom_len() / 10); let mut inserted_set = fxhash::FxHashSet::default(); - for (id, marker) in self.get_range_with_index(span.min_id().into(), span.end_id().into()) { + for (id, marker) in + self.get_range_with_index(span.norm_id_start().into(), span.norm_id_end().into()) + { let id: ID = id.into(); match marker { Marker::Insert { .. } => { @@ -265,7 +267,8 @@ impl CursorMap { Marker::Delete(del) => { if span.intersect(&id.to_span(del.atom_len())) { let from = (span.counter.min() - id.counter).max(0); - let to = (span.counter.end() - id.counter).min(del.atom_len() as Counter); + let to = + (span.counter.norm_end() - id.counter).min(del.atom_len() as Counter); if to - from > 0 { deletes.push((id.inc(from), del.slice(from as usize, to as usize))); } @@ -278,7 +281,9 @@ impl CursorMap { } pub fn get_first_cursors_at_id_span(&self, span: IdSpan) -> Option { - for (id, marker) in self.get_range_with_index(span.min_id().into(), span.end_id().into()) { + for (id, marker) in + self.get_range_with_index(span.norm_id_start().into(), span.norm_id_end().into()) + { let start_id: u128 = id.max(span.id_start().into()); let end_id: u128 = span.id_end().into(); let from = (start_id - id) as usize; diff --git a/crates/loro-internal/src/log_store.rs b/crates/loro-internal/src/log_store.rs index 45ccfa85..7e0347c2 100644 --- a/crates/loro-internal/src/log_store.rs +++ b/crates/loro-internal/src/log_store.rs @@ -125,7 +125,7 @@ impl LogStore { let mut ans = Vec::with_capacity(id_span.atom_len() / 30); for change in changes.slice_iter( id_span.counter.min() as usize, - id_span.counter.end() as usize, + id_span.counter.norm_end() as usize, ) { let change = change.value.slice(change.start, change.end); ans.push(change); diff --git a/crates/loro-internal/src/log_store/iter.rs b/crates/loro-internal/src/log_store/iter.rs index 8465c8aa..6c10a656 100644 --- a/crates/loro-internal/src/log_store/iter.rs +++ b/crates/loro-internal/src/log_store/iter.rs @@ -94,7 +94,7 @@ impl<'a> Iterator for OpSpanIter<'a> { let op = RichOp::new_by_slice_on_change( change, self.span.counter.min() - change.id.counter, - self.span.counter.end() - change.id.counter, + self.span.counter.norm_end() - change.id.counter, op, ); if op.atom_len() == 0 { diff --git a/crates/loro-internal/src/span.rs b/crates/loro-internal/src/span.rs index d8aaee95..efcc0346 100644 --- a/crates/loro-internal/src/span.rs +++ b/crates/loro-internal/src/span.rs @@ -88,7 +88,8 @@ impl CounterSpan { } #[inline(always)] - pub fn end(&self) -> i32 { + /// This is different from end. start may be greater than end. This is the max of start+1 and end + pub fn norm_end(&self) -> i32 { if self.start < self.end { self.end } else { @@ -195,11 +196,6 @@ impl IdSpan { self.counter.end < self.counter.start } - #[inline(always)] - pub fn id_at_begin(&self) -> ID { - ID::new(self.client_id, self.counter.start) - } - #[inline(always)] pub fn reverse(&mut self) { self.counter.reverse(); @@ -210,14 +206,16 @@ impl IdSpan { self.counter.normalize_(); } + /// This is different from id_start. id_start may be greater than id_end, but this is the min of id_start and id_end-1 #[inline] - pub fn min_id(&self) -> ID { + pub fn norm_id_start(&self) -> ID { ID::new(self.client_id, self.counter.min()) } + /// This is different from id_end. id_start may be greater than id_end. This is the max of id_start+1 and id_end #[inline] - pub fn end_id(&self) -> ID { - ID::new(self.client_id, self.counter.end()) + pub fn norm_id_end(&self) -> ID { + ID::new(self.client_id, self.counter.norm_end()) } pub fn to_id_span_vec(self) -> IdSpanVector { @@ -263,10 +261,12 @@ pub trait HasCounter { } pub trait HasCounterSpan: HasCounter + HasLength { + /// end is the exclusive end, last the inclusive end. fn ctr_end(&self) -> Counter { self.ctr_start() + self.atom_len() as Counter } + /// end is the exclusive end, last the inclusive end. fn ctr_last(&self) -> Counter { self.ctr_start() + self.atom_len() as Counter - 1 } @@ -312,10 +312,12 @@ pub trait HasIdSpan: HasId + HasLength { ) } + /// end is the exclusive end, last the inclusive end. fn id_end(&self) -> ID { self.id_start().inc(self.content_len() as i32) } + /// end is the exclusive end, last the inclusive end. fn id_last(&self) -> ID { self.id_start().inc(self.content_len() as i32 - 1) } @@ -337,10 +339,12 @@ pub trait HasLamport { } pub trait HasLamportSpan: HasLamport + HasLength { + /// end is the exclusive end, last the inclusive end. fn lamport_end(&self) -> Lamport { self.lamport() + self.content_len() as Lamport } + /// end is the exclusive end, last the inclusive end. fn lamport_last(&self) -> Lamport { self.lamport() + self.content_len() as Lamport - 1 } @@ -350,7 +354,7 @@ impl HasLamportSpan for T {} impl HasId for IdSpan { #[inline] fn id_start(&self) -> ID { - self.min_id() + self.norm_id_start() } } diff --git a/crates/loro-internal/src/version.rs b/crates/loro-internal/src/version.rs index 580092b6..b03beebf 100644 --- a/crates/loro-internal/src/version.rs +++ b/crates/loro-internal/src/version.rs @@ -430,11 +430,11 @@ impl VersionVector { pub fn extend_to_include(&mut self, span: IdSpan) { if let Some(counter) = self.get_mut(&span.client_id) { - if *counter < span.counter.end() { - *counter = span.counter.end(); + if *counter < span.counter.norm_end() { + *counter = span.counter.norm_end(); } } else { - self.insert(span.client_id, span.counter.end()); + self.insert(span.client_id, span.counter.norm_end()); } } @@ -667,12 +667,12 @@ impl PatchedVersionVector { }; if let Some(counter) = self.patch.get_mut(&span.client_id) { - if *counter < span.counter.end() { - *counter = span.counter.end(); + if *counter < span.counter.norm_end() { + *counter = span.counter.norm_end(); self.omit_if_needless(span.client_id); } } else { - let target = span.counter.end(); + let target = span.counter.norm_end(); if self.base.get(&span.client_id) == Some(&target) { continue; } diff --git a/docs/Glossary.md b/docs/Glossary.md index 1a7d5dee..a97c0175 100644 --- a/docs/Glossary.md +++ b/docs/Glossary.md @@ -17,7 +17,7 @@ | Lamport | [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp) | | | Span | A series of continuous things | | | Causal Order | The order of happen-before relationship | The DAG expresses the causal order of the changes | -| VV | [Version Vector](https://en.wikipedia.org/wiki/Version_vector) | | +| VV | [Version Vector](https://en.wikipedia.org/wiki/Version_vector) | In code, it has exclusive end, `VV {0: 8}` means the last op id of client 0 is `0-7` | ### RLE @@ -32,3 +32,9 @@ This gives us a compact way to represent the mergeable elements. ### Container Each op is associated with one container. Different CRDT algorithms use different types of containers. There are hierarchical relationship between containers, but they cannot affect each other + +# Convention + +- There are methods like `id_last`, `id_end`, `lamport_last`, `lamport_end`. `last` refers to the last one, but + `end` refers to the exclusive end of a range. For example, OpSpan ranging from 0-0 till 0-8 has id_last of 0-8 + and id_end of 0-9