You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
159 lines
5.3 KiB
Rust
159 lines
5.3 KiB
Rust
use crate::index::{IndexShift, RelativeNextIndex};
|
|
use crate::offsetmap::Offset;
|
|
use crate::{Author, Change, Chronofold, LogIndex, Timestamp};
|
|
|
|
impl<A: Author, T> Chronofold<A, T> {
|
|
pub(crate) fn next_log_index(&self) -> LogIndex {
|
|
LogIndex(self.log.len())
|
|
}
|
|
|
|
pub(crate) fn find_predecessor(
|
|
&self,
|
|
id: Timestamp<A>,
|
|
reference: Option<LogIndex>,
|
|
change: &Change<T>,
|
|
) -> Option<LogIndex> {
|
|
match (reference, change) {
|
|
(None, Change::Root) => None,
|
|
(None, Change::Insert(_)) => None,
|
|
(_, Change::Root) => {
|
|
// Roots cannot reference other entries.
|
|
// XXX: Should we cover this by the type system?
|
|
unreachable!()
|
|
}
|
|
(Some(reference), _change) => {
|
|
if let Some((_, idx, _)) = self
|
|
.iter_log_indices_causal_range(reference..)
|
|
.filter(|(_, i, _)| {
|
|
self.references.get(i) == Some(reference)
|
|
&& self.timestamp(*i).unwrap() > id
|
|
})
|
|
.last()
|
|
{
|
|
self.iter_subtree(idx).last()
|
|
} else {
|
|
Some(reference)
|
|
}
|
|
}
|
|
(None, _change) => {
|
|
// Non-roots have to reference another entry.
|
|
// XXX: Should we cover this by the type system?
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
|
|
pub(crate) fn apply_change(
|
|
&mut self,
|
|
id: Timestamp<A>,
|
|
reference: Option<LogIndex>,
|
|
change: Change<T>,
|
|
) -> LogIndex {
|
|
// Find the predecessor to `op`.
|
|
let predecessor = self.find_predecessor(id, reference, &change);
|
|
|
|
// Set the predecessors next index to our new change's index while
|
|
// keeping it's previous next index for ourselves.
|
|
let new_index = LogIndex(self.log.len());
|
|
let next_index;
|
|
if let Some(idx) = predecessor {
|
|
next_index = self.next_indices.get(&idx);
|
|
self.next_indices.set(idx, Some(new_index));
|
|
} else {
|
|
// Inserting another root will result in two disjunct subsequences.
|
|
next_index = None;
|
|
}
|
|
|
|
if let (Change::Delete, Some(deleted)) = (&change, reference) {
|
|
self.mark_as_deleted(deleted, new_index);
|
|
}
|
|
|
|
// Append to the chronofold's log and secondary logs.
|
|
self.log.push((change, None));
|
|
self.next_indices.set(new_index, next_index);
|
|
self.authors.set(new_index, id.1);
|
|
self.index_shifts
|
|
.set(new_index, IndexShift(new_index.0 - (id.0).0));
|
|
self.references.set(new_index, reference);
|
|
|
|
// Increment version.
|
|
self.version.inc(&id);
|
|
|
|
new_index
|
|
}
|
|
|
|
/// Applies consecutive local changes.
|
|
///
|
|
/// For local changes the following optimizations can be applied:
|
|
/// - id equals (log index, author)
|
|
/// - predecessor always equals reference (no preemptive siblings)
|
|
/// - next index has to be set only for the first and the last change
|
|
pub(crate) fn apply_local_changes<I>(
|
|
&mut self,
|
|
author: A,
|
|
reference: LogIndex,
|
|
changes: I,
|
|
) -> Option<LogIndex>
|
|
where
|
|
I: IntoIterator<Item = Change<T>>,
|
|
{
|
|
let mut last_id = None;
|
|
let mut last_next_index = None;
|
|
|
|
let mut predecessor = reference;
|
|
|
|
let mut changes = changes.into_iter();
|
|
if let Some(first_change) = changes.next() {
|
|
let new_index = LogIndex(self.log.len());
|
|
let id = Timestamp(new_index, author);
|
|
last_id = Some(id);
|
|
|
|
// Set the predecessors next index to our new change's index while
|
|
// keeping it's previous next index for ourselves.
|
|
last_next_index = Some(self.next_indices.get(&predecessor));
|
|
self.next_indices.set(predecessor, Some(new_index));
|
|
|
|
if let Change::Delete = &first_change {
|
|
self.mark_as_deleted(predecessor, new_index);
|
|
}
|
|
|
|
self.log.push((first_change, None));
|
|
self.authors.set(new_index, author);
|
|
self.index_shifts.set(new_index, IndexShift(0));
|
|
self.references.set(new_index, Some(predecessor));
|
|
|
|
predecessor = new_index;
|
|
}
|
|
|
|
for change in changes {
|
|
let new_index = RelativeNextIndex::default().add(&predecessor);
|
|
let id = Timestamp(new_index, author);
|
|
last_id = Some(id);
|
|
|
|
if let Change::Delete = &change {
|
|
self.mark_as_deleted(predecessor, new_index);
|
|
}
|
|
|
|
// Append to the chronofold's log and secondary logs.
|
|
self.log.push((change, None));
|
|
|
|
predecessor = new_index;
|
|
}
|
|
|
|
if let (Some(id), Some(next_index)) = (last_id, last_next_index) {
|
|
self.next_indices.set(id.0, next_index);
|
|
self.version.inc(&id);
|
|
Some(id.0)
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
fn mark_as_deleted(&mut self, index: LogIndex, deletion: LogIndex) {
|
|
self.log[index.0].1 = Some(match self.log[index.0].1 {
|
|
None => deletion,
|
|
Some(other_deletion) => LogIndex(usize::min(deletion.0, other_deletion.0)),
|
|
})
|
|
}
|
|
}
|