use alloc::vec::Vec;
use tracing::trace;
use crate::{ClientError, Location, Prior, Segment as _, Storage, storage::TraversalBuffer};
impl From<strand_heap::ParallelFinalize> for ClientError {
fn from(_: strand_heap::ParallelFinalize) -> Self {
Self::ParallelFinalize
}
}
pub(super) fn last_common_ancestor<S: Storage>(
storage: &mut S,
left: Location,
right: Location,
) -> Result<Location, ClientError> {
trace!(%left, %right, "finding least common ancestor");
let mut left = left;
let mut right = right;
while left != right {
let left_seg = storage.get_segment(left)?;
let right_seg = storage.get_segment(right)?;
if left.max_cut > right.max_cut {
left = if let Some(previous) = left_seg.previous(left) {
previous
} else {
match left_seg.prior() {
Prior::None => left,
Prior::Single(s) => s,
Prior::Merge(_, _) => {
if let Some(l) = left_seg.skip_list().last() {
*l
} else {
return Ok(left);
}
}
}
};
} else {
right = if let Some(previous) = right_seg.previous(right) {
previous
} else {
match right_seg.prior() {
Prior::None => right,
Prior::Single(s) => s,
Prior::Merge(_, _) => {
if let Some(r) = right_seg.skip_list().last() {
*r
} else {
return Ok(right);
}
}
}
};
}
}
Ok(left)
}
pub(super) fn braid<S: Storage>(
storage: &mut S,
left: Location,
right: Location,
buffer: &mut TraversalBuffer,
) -> Result<Vec<Location>, ClientError> {
use strand_heap::{Strand, StrandHeap};
let mut braid = Vec::new();
let mut strands = StrandHeap::new();
trace!(%left, %right, "braiding");
for head in [left, right] {
strands.push(Strand::new(storage, head, None)?)?;
}
while let Some(strand) = strands.pop() {
let (prior, mut maybe_cached_segment) =
if let Some(previous) = strand.segment.previous(strand.next) {
(Prior::Single(previous), Some(strand.segment))
} else {
(strand.segment.prior(), None)
};
if matches!(prior, Prior::Merge(..)) {
trace!("skipping merge command");
} else {
trace!("adding {}", strand.next);
braid.push(strand.next);
}
'location: for location in prior {
for other in strands.iter() {
trace!("checking {}", other.next);
let same_segment_check =
location.same_segment(other.next) && location.max_cut <= other.next.max_cut;
if same_segment_check {
trace!("same segment");
continue 'location;
}
if storage.is_ancestor(location, &other.segment, buffer)? {
trace!("found ancestor");
continue 'location;
}
}
trace!("strand at {location}");
strands.push(Strand::new(
storage,
location,
Option::take(&mut maybe_cached_segment),
)?)?;
}
if let Some(strand) = strands.lone() {
let next = strand.next;
trace!("adding {next}");
braid.push(next);
break;
}
}
braid.reverse();
Ok(braid)
}
mod strand_heap {
use alloc::collections::BinaryHeap;
use crate::{
ClientError, CmdId, Command as _, Location, Priority, Segment, Storage, StorageError,
};
pub struct Strand<S> {
key: (Priority, CmdId),
pub next: Location,
pub segment: S,
}
impl<S: Segment> Strand<S> {
pub fn new(
storage: &mut impl Storage<Segment = S>,
location: Location,
cached_segment: Option<S>,
) -> Result<Self, ClientError> {
let segment = cached_segment.map_or_else(|| storage.get_segment(location), Ok)?;
let key = {
let cmd = segment
.get_command(location)
.ok_or(StorageError::CommandOutOfBounds(location))?;
(cmd.priority(), cmd.id())
};
Ok(Self {
key,
next: location,
segment,
})
}
}
impl<S> Eq for Strand<S> {}
impl<S> PartialEq for Strand<S> {
fn eq(&self, other: &Self) -> bool {
self.key == other.key
}
}
impl<S> Ord for Strand<S> {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.key.cmp(&other.key).reverse()
}
}
impl<S> PartialOrd for Strand<S> {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
pub struct StrandHeap<S> {
heap: BinaryHeap<Strand<S>>,
has_finalize: bool,
}
pub struct ParallelFinalize;
impl<S> StrandHeap<S> {
pub const fn new() -> Self {
Self {
heap: BinaryHeap::new(),
has_finalize: false,
}
}
pub fn push(&mut self, strand: Strand<S>) -> Result<(), ParallelFinalize> {
if matches!(strand.key.0, Priority::Finalize) {
if self.has_finalize {
return Err(ParallelFinalize);
}
self.has_finalize = true;
}
self.heap.push(strand);
Ok(())
}
pub fn pop(&mut self) -> Option<Strand<S>> {
let strand = self.heap.pop()?;
if matches!(strand.key.0, Priority::Finalize) {
debug_assert!(self.heap.is_empty());
debug_assert!(self.has_finalize);
self.has_finalize = false;
}
Some(strand)
}
pub fn lone(&mut self) -> Option<Strand<S>> {
if self.heap.len() != 1 {
return None;
}
self.has_finalize = false;
let item = self.heap.pop();
debug_assert!(item.is_some());
item
}
pub fn iter(&self) -> impl Iterator<Item = &Strand<S>> {
self.heap.iter()
}
}
}