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