Sketch in type-level changes to track insertion splits

This commit is contained in:
Nathan Sobo 2021-12-08 21:04:22 -07:00
parent bd6e972d0f
commit ec54010e3c
2 changed files with 134 additions and 12 deletions

View file

@ -0,0 +1,74 @@
use smallvec::{smallvec, SmallVec};
use std::iter;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Locator(SmallVec<[u32; 4]>);
impl Locator {
pub fn min() -> Self {
Self(smallvec![u32::MIN])
}
pub fn max() -> Self {
Self(smallvec![u32::MAX])
}
pub fn between(lhs: &Self, rhs: &Self) -> Self {
let lhs = lhs.0.iter().copied().chain(iter::repeat(u32::MIN));
let rhs = rhs.0.iter().copied().chain(iter::repeat(u32::MAX));
let mut location = SmallVec::new();
for (lhs, rhs) in lhs.zip(rhs) {
let mid = lhs + (rhs.saturating_sub(lhs)) / 2;
location.push(mid);
if mid > lhs {
break;
}
}
Self(location)
}
}
impl Default for Locator {
fn default() -> Self {
Self::min()
}
}
#[cfg(test)]
mod tests {
use super::*;
use rand::prelude::*;
use std::mem;
#[gpui::test(iterations = 100)]
fn test_locators(mut rng: StdRng) {
let mut lhs = Default::default();
let mut rhs = Default::default();
while lhs == rhs {
lhs = Locator(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
.collect(),
);
rhs = Locator(
(0..rng.gen_range(1..=5))
.map(|_| rng.gen_range(0..=100))
.collect(),
);
}
if lhs > rhs {
mem::swap(&mut lhs, &mut rhs);
}
let middle = Locator::between(&lhs, &rhs);
assert!(middle > lhs);
assert!(middle < rhs);
for ix in 0..middle.0.len() - 1 {
assert!(
middle.0[ix] == *lhs.0.get(ix).unwrap_or(&0)
|| middle.0[ix] == *rhs.0.get(ix).unwrap_or(&0)
);
}
}
}

View file

@ -1,4 +1,5 @@
mod anchor; mod anchor;
mod locator;
mod operation_queue; mod operation_queue;
mod patch; mod patch;
mod point; mod point;
@ -14,6 +15,7 @@ pub use anchor::*;
use anyhow::{anyhow, Result}; use anyhow::{anyhow, Result};
use clock::ReplicaId; use clock::ReplicaId;
use collections::{HashMap, HashSet}; use collections::{HashMap, HashSet};
use locator::Locator;
use operation_queue::OperationQueue; use operation_queue::OperationQueue;
use parking_lot::Mutex; use parking_lot::Mutex;
pub use patch::Patch; pub use patch::Patch;
@ -55,6 +57,7 @@ pub struct Snapshot {
deleted_text: Rope, deleted_text: Rope,
undo_map: UndoMap, undo_map: UndoMap,
fragments: SumTree<Fragment>, fragments: SumTree<Fragment>,
insertions: SumTree<InsertionFragment>,
pub version: clock::Global, pub version: clock::Global,
} }
@ -381,6 +384,7 @@ impl InsertionTimestamp {
#[derive(Eq, PartialEq, Clone, Debug)] #[derive(Eq, PartialEq, Clone, Debug)]
struct Fragment { struct Fragment {
id: Locator,
timestamp: InsertionTimestamp, timestamp: InsertionTimestamp,
len: usize, len: usize,
visible: bool, visible: bool,
@ -391,6 +395,7 @@ struct Fragment {
#[derive(Eq, PartialEq, Clone, Debug)] #[derive(Eq, PartialEq, Clone, Debug)]
pub struct FragmentSummary { pub struct FragmentSummary {
text: FragmentTextSummary, text: FragmentTextSummary,
max_id: Locator,
max_version: clock::Global, max_version: clock::Global,
min_insertion_version: clock::Global, min_insertion_version: clock::Global,
max_insertion_version: clock::Global, max_insertion_version: clock::Global,
@ -402,11 +407,17 @@ struct FragmentTextSummary {
deleted: usize, deleted: usize,
} }
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary { #[derive(Eq, PartialEq, Clone, Debug)]
fn add_summary(&mut self, summary: &'a FragmentSummary, _: &Option<clock::Global>) { struct InsertionFragment {
self.visible += summary.text.visible; timestamp: InsertionTimestamp,
self.deleted += summary.text.deleted; split_offset: usize,
} fragment_id: Locator,
}
#[derive(Clone, Debug, Default)]
struct InsertionSummary {
max_timestamp: InsertionTimestamp,
max_split_offset: usize,
} }
#[derive(Clone, Debug, Eq, PartialEq)] #[derive(Clone, Debug, Eq, PartialEq)]
@ -452,6 +463,7 @@ pub struct UndoOperation {
impl Buffer { impl Buffer {
pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer { pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer {
let mut fragments = SumTree::new(); let mut fragments = SumTree::new();
let mut insertions = SumTree::new();
let mut local_clock = clock::Local::new(replica_id); let mut local_clock = clock::Local::new(replica_id);
let mut lamport_clock = clock::Lamport::new(replica_id); let mut lamport_clock = clock::Lamport::new(replica_id);
@ -466,8 +478,10 @@ impl Buffer {
local_clock.observe(timestamp.local()); local_clock.observe(timestamp.local());
lamport_clock.observe(timestamp.lamport()); lamport_clock.observe(timestamp.lamport());
version.observe(timestamp.local()); version.observe(timestamp.local());
let fragment_id = Locator::between(&Locator::min(), &Locator::max());
fragments.push( fragments.push(
Fragment { Fragment {
id: fragment_id,
timestamp, timestamp,
len: visible_text.len(), len: visible_text.len(),
visible: true, visible: true,
@ -476,6 +490,14 @@ impl Buffer {
}, },
&None, &None,
); );
insertions.push(
InsertionFragment {
timestamp,
split_offset: 0,
fragment_id,
},
&(),
);
} }
Buffer { Buffer {
@ -483,6 +505,7 @@ impl Buffer {
visible_text, visible_text,
deleted_text: Rope::new(), deleted_text: Rope::new(),
fragments, fragments,
insertions,
version, version,
undo_map: Default::default(), undo_map: Default::default(),
}, },
@ -504,13 +527,7 @@ impl Buffer {
} }
pub fn snapshot(&self) -> Snapshot { pub fn snapshot(&self) -> Snapshot {
Snapshot { self.snapshot.clone()
visible_text: self.visible_text.clone(),
deleted_text: self.deleted_text.clone(),
undo_map: self.undo_map.clone(),
fragments: self.fragments.clone(),
version: self.version.clone(),
}
} }
pub fn replica_id(&self) -> ReplicaId { pub fn replica_id(&self) -> ReplicaId {
@ -569,6 +586,7 @@ impl Buffer {
ranges: Vec::with_capacity(ranges.len()), ranges: Vec::with_capacity(ranges.len()),
new_text: None, new_text: None,
}; };
let mut insertions = Vec::new();
let mut ranges = ranges let mut ranges = ranges
.map(|range| range.start.to_offset(&*self)..range.end.to_offset(&*self)) .map(|range| range.start.to_offset(&*self)..range.end.to_offset(&*self))
@ -2040,6 +2058,7 @@ impl sum_tree::Item for Fragment {
let max_insertion_version = min_insertion_version.clone(); let max_insertion_version = min_insertion_version.clone();
if self.visible { if self.visible {
FragmentSummary { FragmentSummary {
max_id: self.id.clone(),
text: FragmentTextSummary { text: FragmentTextSummary {
visible: self.len, visible: self.len,
deleted: 0, deleted: 0,
@ -2050,6 +2069,7 @@ impl sum_tree::Item for Fragment {
} }
} else { } else {
FragmentSummary { FragmentSummary {
max_id: self.id.clone(),
text: FragmentTextSummary { text: FragmentTextSummary {
visible: 0, visible: 0,
deleted: self.len, deleted: self.len,
@ -2079,6 +2099,7 @@ impl sum_tree::Summary for FragmentSummary {
impl Default for FragmentSummary { impl Default for FragmentSummary {
fn default() -> Self { fn default() -> Self {
FragmentSummary { FragmentSummary {
max_id: Locator::min(),
text: FragmentTextSummary::default(), text: FragmentTextSummary::default(),
max_version: clock::Global::new(), max_version: clock::Global::new(),
min_insertion_version: clock::Global::new(), min_insertion_version: clock::Global::new(),
@ -2087,6 +2108,33 @@ impl Default for FragmentSummary {
} }
} }
impl<'a> sum_tree::Dimension<'a, FragmentSummary> for FragmentTextSummary {
fn add_summary(&mut self, summary: &'a FragmentSummary, _: &Option<clock::Global>) {
self.visible += summary.text.visible;
self.deleted += summary.text.deleted;
}
}
impl sum_tree::Item for InsertionFragment {
type Summary = InsertionSummary;
fn summary(&self) -> Self::Summary {
InsertionSummary {
max_timestamp: self.timestamp,
max_split_offset: self.split_offset,
}
}
}
impl sum_tree::Summary for InsertionSummary {
type Context = ();
fn add_summary(&mut self, summary: &Self, cx: &()) {
self.max_timestamp = summary.max_timestamp;
self.max_split_offset = summary.max_split_offset;
}
}
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FullOffset(pub usize); pub struct FullOffset(pub usize);