diff --git a/crates/buffer/src/lib.rs b/crates/buffer/src/lib.rs index a01430d309..8d694a3c5b 100644 --- a/crates/buffer/src/lib.rs +++ b/crates/buffer/src/lib.rs @@ -438,11 +438,22 @@ impl Buffer { pub fn new(replica_id: u16, remote_id: u64, history: History) -> Buffer { 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()); 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( Fragment { - timestamp: Default::default(), + timestamp, len: visible_text.len(), visible: true, deletions: Default::default(), @@ -456,7 +467,7 @@ impl Buffer { visible_text, deleted_text: Rope::new(), fragments, - version: clock::Global::new(), + version, last_edit: clock::Local::default(), undo_map: Default::default(), history, @@ -465,8 +476,8 @@ impl Buffer { deferred_replicas: HashSet::default(), replica_id, remote_id, - local_clock: clock::Local::new(replica_id), - lamport_clock: clock::Lamport::new(replica_id), + local_clock, + lamport_clock, } } @@ -1093,10 +1104,10 @@ impl Buffer { false } else { match op { - Operation::Edit(edit) => self.version >= edit.version, - Operation::Undo { undo, .. } => self.version >= undo.version, + Operation::Edit(edit) => self.version.ge(&edit.version), + Operation::Undo { undo, .. } => self.version.ge(&undo.version), Operation::UpdateSelections { selections, .. } => { - self.version >= *selections.version() + self.version.ge(selections.version()) } Operation::RemoveSelections { .. } => true, Operation::SetActiveSelections { set_id, .. } => { @@ -1947,10 +1958,10 @@ impl<'a> Content<'a> { let fragments_cursor = if since == self.version { None } else { - Some(self.fragments.filter( - move |summary| summary.max_version.changed_since(since), - &None, - )) + Some( + self.fragments + .filter(move |summary| !since.ge(&summary.max_version), &None), + ) }; Edits { @@ -2233,13 +2244,9 @@ impl<'a> sum_tree::Dimension<'a, FragmentSummary> for VersionedFullOffset { fn add_summary(&mut self, summary: &'a FragmentSummary, cx: &Option) { if let Self::Offset(offset) = self { 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; - } else if !summary - .min_insertion_version - .iter() - .all(|t| !version.observed(*t)) - { + } else if version.observed_any(&summary.min_insertion_version) { *self = Self::Invalid; } } diff --git a/crates/clock/src/lib.rs b/crates/clock/src/lib.rs index b3158d99dd..6e8b460861 100644 --- a/crates/clock/src/lib.rs +++ b/crates/clock/src/lib.rs @@ -1,9 +1,8 @@ use smallvec::SmallVec; use std::{ cmp::{self, Ordering}, - fmt, + fmt, iter, ops::{Add, AddAssign}, - slice, }; pub type ReplicaId = u16; @@ -59,7 +58,7 @@ impl<'a> AddAssign<&'a Local> for Local { } #[derive(Clone, Default, Hash, Eq, PartialEq)] -pub struct Global(SmallVec<[Local; 3]>); +pub struct Global(SmallVec<[u32; 8]>); impl From> for Global { fn from(message: Vec) -> Self { @@ -98,75 +97,119 @@ impl Global { } pub fn get(&self, replica_id: ReplicaId) -> Seq { - self.0 - .iter() - .find(|t| t.replica_id == replica_id) - .map_or(0, |t| t.value) + self.0.get(replica_id as usize).copied().unwrap_or(0) as Seq } pub fn observe(&mut self, timestamp: Local) { - if let Some(entry) = self - .0 - .iter_mut() - .find(|t| t.replica_id == timestamp.replica_id) - { - entry.value = cmp::max(entry.value, timestamp.value); - } else { - self.0.push(timestamp); + if timestamp.value > 0 { + let new_len = timestamp.replica_id as usize + 1; + if new_len > self.0.len() { + self.0.resize(new_len, 0); + } + + let entry = &mut self.0[timestamp.replica_id as usize]; + *entry = cmp::max(*entry, timestamp.value); } } pub fn join(&mut self, other: &Self) { - for timestamp in other.0.iter() { - self.observe(*timestamp); + if other.0.len() > self.0.len() { + 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) { - for timestamp in other.0.iter() { - if let Some(entry) = self - .0 - .iter_mut() - .find(|t| t.replica_id == timestamp.replica_id) - { - entry.value = cmp::min(entry.value, timestamp.value); - } else { - self.0.push(*timestamp); + if other.0.len() > self.0.len() { + self.0.resize(other.0.len(), 0); + } + + let mut new_len = 0; + for (ix, (left, right)) in self + .0 + .iter_mut() + .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 { self.get(timestamp.replica_id) >= timestamp.value } - pub fn changed_since(&self, other: &Self) -> bool { - self.0.iter().any(|t| t.value > other.get(t.replica_id)) - } - - pub fn iter(&self) -> slice::Iter { - self.0.iter() - } -} - -impl PartialOrd for Global { - fn partial_cmp(&self, other: &Self) -> Option { - 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; + pub fn observed_any(&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 *right > 0 && left >= right { + return true; + } + } else { + return false; } + } 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 { + 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 { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "Global {{")?; - for (i, element) in self.0.iter().enumerate() { - if i > 0 { + for timestamp in self.iter() { + if timestamp.replica_id > 0 { write!(f, ", ")?; } - write!(f, "{}: {}", element.replica_id, element.value)?; + write!(f, "{}: {}", timestamp.replica_id, timestamp.value)?; } write!(f, "}}") } diff --git a/crates/language/src/lib.rs b/crates/language/src/lib.rs index 0755ed9571..a8914b9abe 100644 --- a/crates/language/src/lib.rs +++ b/crates/language/src/lib.rs @@ -319,9 +319,9 @@ impl Buffer { } Self { - text: buffer, saved_mtime, - saved_version: clock::Global::new(), + saved_version: buffer.version(), + text: buffer, file, syntax_tree: Mutex::new(None), parsing_in_background: false, @@ -620,7 +620,7 @@ impl Buffer { this.language.as_ref().map_or(true, |curr_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.did_finish_parsing(new_tree, parsed_version, cx); @@ -1132,12 +1132,12 @@ impl Buffer { } 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()) } pub fn has_conflict(&self) -> bool { - self.version > self.saved_version + !self.saved_version.ge(&self.version) && self .file .as_ref()