Switch to a dense representation for clock::Global

Co-Authored-By: Nathan Sobo <nathan@zed.dev>
This commit is contained in:
Antonio Scandurra 2021-11-22 17:20:43 +01:00
parent 447f710570
commit b25c3eb740
3 changed files with 124 additions and 74 deletions

View file

@ -438,11 +438,22 @@ 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 local_clock = clock::Local::new(replica_id);
let mut lamport_clock = clock::Lamport::new(replica_id);
let mut version = clock::Global::new();
let visible_text = Rope::from(history.base_text.as_ref()); let visible_text = Rope::from(history.base_text.as_ref());
if visible_text.len() > 0 { if visible_text.len() > 0 {
let timestamp = InsertionTimestamp {
replica_id: 0,
local: 1,
lamport: 1,
};
local_clock.observe(timestamp.local());
lamport_clock.observe(timestamp.lamport());
version.observe(timestamp.local());
fragments.push( fragments.push(
Fragment { Fragment {
timestamp: Default::default(), timestamp,
len: visible_text.len(), len: visible_text.len(),
visible: true, visible: true,
deletions: Default::default(), deletions: Default::default(),
@ -456,7 +467,7 @@ impl Buffer {
visible_text, visible_text,
deleted_text: Rope::new(), deleted_text: Rope::new(),
fragments, fragments,
version: clock::Global::new(), version,
last_edit: clock::Local::default(), last_edit: clock::Local::default(),
undo_map: Default::default(), undo_map: Default::default(),
history, history,
@ -465,8 +476,8 @@ impl Buffer {
deferred_replicas: HashSet::default(), deferred_replicas: HashSet::default(),
replica_id, replica_id,
remote_id, remote_id,
local_clock: clock::Local::new(replica_id), local_clock,
lamport_clock: clock::Lamport::new(replica_id), lamport_clock,
} }
} }
@ -1093,10 +1104,10 @@ impl Buffer {
false false
} else { } else {
match op { match op {
Operation::Edit(edit) => self.version >= edit.version, Operation::Edit(edit) => self.version.ge(&edit.version),
Operation::Undo { undo, .. } => self.version >= undo.version, Operation::Undo { undo, .. } => self.version.ge(&undo.version),
Operation::UpdateSelections { selections, .. } => { Operation::UpdateSelections { selections, .. } => {
self.version >= *selections.version() self.version.ge(selections.version())
} }
Operation::RemoveSelections { .. } => true, Operation::RemoveSelections { .. } => true,
Operation::SetActiveSelections { set_id, .. } => { Operation::SetActiveSelections { set_id, .. } => {
@ -1947,10 +1958,10 @@ impl<'a> Content<'a> {
let fragments_cursor = if since == self.version { let fragments_cursor = if since == self.version {
None None
} else { } else {
Some(self.fragments.filter( Some(
move |summary| summary.max_version.changed_since(since), self.fragments
&None, .filter(move |summary| !since.ge(&summary.max_version), &None),
)) )
}; };
Edits { Edits {
@ -2233,13 +2244,9 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedFullOffset {
fn add_summary(&mut self, summary: &'a FragmentSummary, cx: &Option<clock::Global>) { fn add_summary(&mut self, summary: &'a FragmentSummary, cx: &Option<clock::Global>) {
if let Self::Offset(offset) = self { if let Self::Offset(offset) = self {
let version = cx.as_ref().unwrap(); let version = cx.as_ref().unwrap();
if *version >= summary.max_insertion_version { if version.ge(&summary.max_insertion_version) {
*offset += summary.text.visible + summary.text.deleted; *offset += summary.text.visible + summary.text.deleted;
} else if !summary } else if version.observed_any(&summary.min_insertion_version) {
.min_insertion_version
.iter()
.all(|t| !version.observed(*t))
{
*self = Self::Invalid; *self = Self::Invalid;
} }
} }

View file

@ -1,9 +1,8 @@
use smallvec::SmallVec; use smallvec::SmallVec;
use std::{ use std::{
cmp::{self, Ordering}, cmp::{self, Ordering},
fmt, fmt, iter,
ops::{Add, AddAssign}, ops::{Add, AddAssign},
slice,
}; };
pub type ReplicaId = u16; pub type ReplicaId = u16;
@ -59,7 +58,7 @@ impl<'a> AddAssign<&'a Local> for Local {
} }
#[derive(Clone, Default, Hash, Eq, PartialEq)] #[derive(Clone, Default, Hash, Eq, PartialEq)]
pub struct Global(SmallVec<[Local; 3]>); pub struct Global(SmallVec<[u32; 8]>);
impl From<Vec<rpc::proto::VectorClockEntry>> for Global { impl From<Vec<rpc::proto::VectorClockEntry>> for Global {
fn from(message: Vec<rpc::proto::VectorClockEntry>) -> Self { fn from(message: Vec<rpc::proto::VectorClockEntry>) -> Self {
@ -98,75 +97,119 @@ impl Global {
} }
pub fn get(&self, replica_id: ReplicaId) -> Seq { pub fn get(&self, replica_id: ReplicaId) -> Seq {
self.0 self.0.get(replica_id as usize).copied().unwrap_or(0) as Seq
.iter()
.find(|t| t.replica_id == replica_id)
.map_or(0, |t| t.value)
} }
pub fn observe(&mut self, timestamp: Local) { pub fn observe(&mut self, timestamp: Local) {
if let Some(entry) = self if timestamp.value > 0 {
.0 let new_len = timestamp.replica_id as usize + 1;
.iter_mut() if new_len > self.0.len() {
.find(|t| t.replica_id == timestamp.replica_id) self.0.resize(new_len, 0);
{ }
entry.value = cmp::max(entry.value, timestamp.value);
} else { let entry = &mut self.0[timestamp.replica_id as usize];
self.0.push(timestamp); *entry = cmp::max(*entry, timestamp.value);
} }
} }
pub fn join(&mut self, other: &Self) { pub fn join(&mut self, other: &Self) {
for timestamp in other.0.iter() { if other.0.len() > self.0.len() {
self.observe(*timestamp); self.0.resize(other.0.len(), 0);
}
for (left, right) in self.0.iter_mut().zip(&other.0) {
*left = cmp::max(*left, *right);
} }
} }
pub fn meet(&mut self, other: &Self) { pub fn meet(&mut self, other: &Self) {
for timestamp in other.0.iter() { if other.0.len() > self.0.len() {
if let Some(entry) = self self.0.resize(other.0.len(), 0);
.0 }
.iter_mut()
.find(|t| t.replica_id == timestamp.replica_id) let mut new_len = 0;
{ for (ix, (left, right)) in self
entry.value = cmp::min(entry.value, timestamp.value); .0
} else { .iter_mut()
self.0.push(*timestamp); .zip(other.0.iter().chain(iter::repeat(&0)))
.enumerate()
{
if *left == 0 {
*left = *right;
} else if *right > 0 {
*left = cmp::min(*left, *right);
}
if *left != 0 {
new_len = ix + 1;
} }
} }
self.0.resize(new_len, 0);
} }
pub fn observed(&self, timestamp: Local) -> bool { pub fn observed(&self, timestamp: Local) -> bool {
self.get(timestamp.replica_id) >= timestamp.value self.get(timestamp.replica_id) >= timestamp.value
} }
pub fn changed_since(&self, other: &Self) -> bool { pub fn observed_any(&self, other: &Self) -> bool {
self.0.iter().any(|t| t.value > other.get(t.replica_id)) let mut lhs = self.0.iter();
} let mut rhs = other.0.iter();
loop {
pub fn iter(&self) -> slice::Iter<Local> { if let Some(left) = lhs.next() {
self.0.iter() if let Some(right) = rhs.next() {
} if *right > 0 && left >= right {
} return true;
}
impl PartialOrd for Global { } else {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> { return false;
let mut global_ordering = Ordering::Equal;
for timestamp in self.0.iter().chain(other.0.iter()) {
let ordering = self
.get(timestamp.replica_id)
.cmp(&other.get(timestamp.replica_id));
if ordering != Ordering::Equal {
if global_ordering == Ordering::Equal {
global_ordering = ordering;
} else if ordering != global_ordering {
return None;
} }
} else {
return false;
} }
} }
}
Some(global_ordering) pub fn ge(&self, other: &Self) -> bool {
let mut lhs = self.0.iter();
let mut rhs = other.0.iter();
loop {
if let Some(left) = lhs.next() {
if let Some(right) = rhs.next() {
if left < right {
return false;
}
} else {
return true;
}
} else {
return rhs.next().is_none();
}
}
}
pub fn gt(&self, other: &Self) -> bool {
let mut lhs = self.0.iter();
let mut rhs = other.0.iter();
loop {
if let Some(left) = lhs.next() {
if let Some(right) = rhs.next() {
if left <= right {
return false;
}
} else {
return true;
}
} else {
return rhs.next().is_none();
}
}
}
pub fn iter<'a>(&'a self) -> impl 'a + Iterator<Item = Local> {
self.0.iter().enumerate().map(|(replica_id, seq)| Local {
replica_id: replica_id as ReplicaId,
value: *seq,
})
} }
} }
@ -219,11 +262,11 @@ impl fmt::Debug for Lamport {
impl fmt::Debug for Global { impl fmt::Debug for Global {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Global {{")?; write!(f, "Global {{")?;
for (i, element) in self.0.iter().enumerate() { for timestamp in self.iter() {
if i > 0 { if timestamp.replica_id > 0 {
write!(f, ", ")?; write!(f, ", ")?;
} }
write!(f, "{}: {}", element.replica_id, element.value)?; write!(f, "{}: {}", timestamp.replica_id, timestamp.value)?;
} }
write!(f, "}}") write!(f, "}}")
} }

View file

@ -319,9 +319,9 @@ impl Buffer {
} }
Self { Self {
text: buffer,
saved_mtime, saved_mtime,
saved_version: clock::Global::new(), saved_version: buffer.version(),
text: buffer,
file, file,
syntax_tree: Mutex::new(None), syntax_tree: Mutex::new(None),
parsing_in_background: false, parsing_in_background: false,
@ -620,7 +620,7 @@ impl Buffer {
this.language.as_ref().map_or(true, |curr_language| { this.language.as_ref().map_or(true, |curr_language| {
!Arc::ptr_eq(curr_language, &language) !Arc::ptr_eq(curr_language, &language)
}); });
let parse_again = this.version > parsed_version || language_changed; let parse_again = this.version.gt(&parsed_version) || language_changed;
this.parsing_in_background = false; this.parsing_in_background = false;
this.did_finish_parsing(new_tree, parsed_version, cx); this.did_finish_parsing(new_tree, parsed_version, cx);
@ -1132,12 +1132,12 @@ impl Buffer {
} }
pub fn is_dirty(&self) -> bool { pub fn is_dirty(&self) -> bool {
self.version > self.saved_version !self.saved_version.ge(&self.version)
|| self.file.as_ref().map_or(false, |file| file.is_deleted()) || self.file.as_ref().map_or(false, |file| file.is_deleted())
} }
pub fn has_conflict(&self) -> bool { pub fn has_conflict(&self) -> bool {
self.version > self.saved_version !self.saved_version.ge(&self.version)
&& self && self
.file .file
.as_ref() .as_ref()