use generic_btree::{
rle::{HasLength, Mergeable},
LeafIndex,
};
use loro_common::{Counter, IdSpan, PeerID, ID};
use rle::{HasLength as RHasLength, Mergable as RMergeable, Sliceable};
use rustc_hash::FxHashMap;
use smallvec::smallvec;
use self::insert_set::InsertSet;
const MAX_FRAGMENT_LEN: usize = 256;
#[cfg(not(test))]
const SMALL_SET_MAX_LEN: usize = 32;
#[cfg(test)]
const SMALL_SET_MAX_LEN: usize = 4;
#[derive(Debug, Default)]
pub(super) struct IdToCursor {
map: FxHashMap<PeerID, Vec<Fragment>>,
}
static EMPTY_VEC: Vec<Fragment> = vec![];
impl IdToCursor {
pub fn insert_without_split(&mut self, id: ID, cursor: Cursor) {
let list = self.map.entry(id.peer).or_default();
list.push(Fragment {
counter: id.counter,
cursor,
});
}
pub fn insert(&mut self, id: ID, cursor: Cursor) {
let list = self.map.entry(id.peer).or_default();
if let Some(last) = list.last_mut() {
let last_end = last.counter + last.cursor.rle_len() as Counter;
debug_assert!(last_end <= id.counter, "id:{}, {:#?}", id, &self);
if last_end == id.counter
&& last.cursor.can_merge(&cursor)
&& last.cursor.rle_len() + cursor.rle_len() < MAX_FRAGMENT_LEN
{
last.cursor.merge_right(&cursor);
return;
}
}
if let Cursor::Insert(InsertSet::Small(set)) = cursor {
if set.len > MAX_FRAGMENT_LEN as u32 {
assert!(set.set.len() == 1);
let insert = set.set[0];
let mut counter = id.counter;
for start in (0..set.len).step_by(MAX_FRAGMENT_LEN) {
let end = (start + MAX_FRAGMENT_LEN as u32).min(set.len);
let len = (end - start) as usize;
list.push(Fragment {
counter,
cursor: Cursor::new_insert(insert.leaf, len),
});
counter += len as Counter;
}
} else {
list.push(Fragment {
counter: id.counter,
cursor: Cursor::Insert(InsertSet::Small(set)),
});
}
} else {
list.push(Fragment {
counter: id.counter,
cursor,
});
}
}
pub fn update_insert(&mut self, id_span: IdSpan, new_leaf: LeafIndex) {
debug_assert!(!id_span.is_reversed());
let list = self.map.get_mut(&id_span.peer).unwrap();
let last = list.last().unwrap();
debug_assert!(last.counter + last.cursor.rle_len() as Counter > id_span.counter.max());
let mut index = match list.binary_search_by_key(&id_span.counter.start, |x| x.counter) {
Ok(index) => index,
Err(index) => index.saturating_sub(1),
};
let mut start_counter = id_span.counter.start;
while start_counter < id_span.counter.end
&& index < list.len()
&& start_counter < list[index].counter_end()
{
let fragment = &mut list[index];
let from = (start_counter - fragment.counter) as usize;
let to =
((id_span.counter.end - fragment.counter) as usize).min(fragment.cursor.rle_len());
fragment.cursor.update_insert(from, to, new_leaf);
start_counter += (to - from) as Counter;
index += 1;
}
assert_eq!(start_counter, id_span.counter.end);
}
pub fn update_insert_batch(&mut self, updates: &mut [(IdSpan, LeafIndex)]) {
if updates.is_empty() {
return;
}
if updates.len() == 1 {
let (span, leaf) = updates[0];
self.update_insert(span, leaf);
return;
}
let mut per_peer: FxHashMap<PeerID, Vec<(IdSpan, LeafIndex)>> = FxHashMap::default();
for &(id_span, new_leaf) in updates.iter() {
per_peer
.entry(id_span.peer)
.or_default()
.push((id_span, new_leaf));
}
for (peer, peer_updates) in per_peer {
let Some(list) = self.map.get_mut(&peer) else {
continue;
};
let mut per_fragment: FxHashMap<usize, Vec<(usize, usize, LeafIndex)>> =
FxHashMap::default();
for (id_span, new_leaf) in peer_updates {
debug_assert!(!id_span.is_reversed());
let mut index =
match list.binary_search_by_key(&id_span.counter.start, |x| x.counter) {
Ok(index) => index,
Err(index) => index.saturating_sub(1),
};
let mut start_counter = id_span.counter.start;
while start_counter < id_span.counter.end
&& index < list.len()
&& start_counter < list[index].counter_end()
{
let fragment = &list[index];
let from = (start_counter - fragment.counter) as usize;
let to = ((id_span.counter.end - fragment.counter) as usize)
.min(fragment.cursor.rle_len());
if from != to {
per_fragment
.entry(index)
.or_default()
.push((from, to, new_leaf));
}
start_counter += (to - from) as Counter;
index += 1;
}
assert_eq!(start_counter, id_span.counter.end);
}
let mut fragment_indexes: Vec<usize> = per_fragment.keys().copied().collect();
fragment_indexes.sort_unstable();
for index in fragment_indexes {
let updates = per_fragment.get(&index).unwrap();
list[index].cursor.update_insert_many(updates);
}
}
}
pub fn iter_all(&self) -> impl Iterator<Item = IterCursor> + '_ {
self.map.iter().flat_map(|(peer, list)| {
list.iter()
.flat_map(move |f| -> Box<dyn Iterator<Item = IterCursor>> {
match &f.cursor {
Cursor::Insert(set) => set.iter_all(*peer, f.counter),
Cursor::Delete(span) => {
let start_counter = f.counter;
let end_counter = f.counter + span.atom_len() as Counter;
let id_span = IdSpan::new(*peer, start_counter, end_counter);
Box::new(std::iter::once(IterCursor::Delete(id_span)))
}
Cursor::Move { from, to } => Box::new(std::iter::once(IterCursor::Move {
from_id: *from,
to_leaf: *to,
new_op_id: ID::new(*peer, f.counter),
})),
}
})
})
}
pub fn iter(&self, mut iter_id_span: IdSpan) -> impl Iterator<Item = IterCursor> + '_ {
iter_id_span.normalize_();
let list = self.map.get(&iter_id_span.peer).unwrap_or(&EMPTY_VEC);
let mut index = 0;
let mut insert_set_iter: Option<Box<dyn Iterator<Item = IterCursor>>> = None;
if !list.is_empty() {
index = match list.binary_search_by_key(&iter_id_span.counter.start, |x| x.counter) {
Ok(index) => index,
Err(index) => index.saturating_sub(1),
};
}
std::iter::from_fn(move || loop {
if index >= list.len() {
return None;
}
if let Some(iter) = insert_set_iter.as_mut() {
let Some(next) = iter.next() else {
index += 1;
insert_set_iter = None;
continue;
};
return Some(next);
}
let f = &list[index];
let iter_counter = f.counter;
if iter_counter >= iter_id_span.counter.end {
return None;
}
if iter_counter + f.cursor.rle_len() as Counter <= iter_id_span.counter.start {
index += 1;
continue;
}
match &f.cursor {
Cursor::Insert(set) => {
insert_set_iter = Some(set.iter_range(
ID::new(iter_id_span.peer, iter_counter),
iter_id_span.counter.start,
iter_id_span.counter.end,
));
continue;
}
Cursor::Delete(span) => {
index += 1;
let from = (iter_id_span.counter.start - iter_counter)
.max(0)
.min(span.atom_len() as Counter);
let to = (iter_id_span.counter.end - iter_counter)
.max(0)
.min(span.atom_len() as Counter);
if from == to {
continue;
}
return Some(IterCursor::Delete(span.slice(from as usize, to as usize)));
}
Cursor::Move { from, to } => {
index += 1;
let op_id = ID::new(iter_id_span.peer, f.counter);
return Some(IterCursor::Move {
from_id: *from,
to_leaf: *to,
new_op_id: op_id,
});
}
}
})
}
pub fn get_insert(&self, id: ID) -> Option<LeafIndex> {
let list = self.map.get(&id.peer)?;
let index = match list.binary_search_by_key(&id.counter, |x| x.counter) {
Ok(index) => index,
Err(index) => index - 1,
};
list[index]
.cursor
.get_insert((id.counter - list[index].counter) as usize)
}
#[allow(unused)]
pub fn diagnose(&self) {
let fragment_num = self.map.iter().map(|x| x.1.len()).sum::<usize>();
let insert_pieces = self
.map
.iter()
.flat_map(|x| x.1.iter())
.map(|x| match &x.cursor {
Cursor::Insert(set) => set.len(),
Cursor::Delete(_) => 0,
Cursor::Move { .. } => 0,
})
.sum::<usize>();
let max_insert_len = self
.map
.iter()
.map(|x| {
x.1.iter()
.filter_map(|x| match &x.cursor {
Cursor::Insert(set) => Some(set.len()),
_ => None,
})
.max()
.unwrap_or(0)
})
.max()
.unwrap_or(0);
println!(
"fragments:{}, insert_pieces:{}, max_insert_len:{}",
fragment_num, insert_pieces, max_insert_len
);
}
}
#[derive(Debug)]
pub(super) struct Fragment {
pub(super) counter: Counter,
pub(super) cursor: Cursor,
}
impl PartialEq for Fragment {
fn eq(&self, other: &Self) -> bool {
self.counter == other.counter
}
}
impl Eq for Fragment {}
impl PartialOrd for Fragment {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Fragment {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.counter.cmp(&other.counter)
}
}
impl Fragment {
fn counter_end(&self) -> Counter {
self.counter + self.cursor.rle_len() as Counter
}
}
#[derive(Debug, Clone, Copy)]
pub(super) enum IterCursor {
Insert {
leaf: LeafIndex,
id_span: IdSpan,
},
Delete(IdSpan),
Move {
from_id: ID,
to_leaf: LeafIndex,
new_op_id: ID,
},
}
#[derive(Debug)]
pub(super) enum Cursor {
Insert(InsertSet),
Delete(IdSpan),
Move {
from: ID,
to: LeafIndex,
},
}
mod insert_set {
use super::*;
use generic_btree::{
rle::{CanRemove, TryInsert},
BTree, BTreeTrait, LengthFinder, UseLengthFinder,
};
use smallvec::SmallVec;
#[derive(Debug)]
pub(crate) enum InsertSet {
Small(SmallInsertSet),
Large(LargeInsertSet),
}
impl InsertSet {
pub(crate) fn new(leaf: LeafIndex, len: usize) -> Self {
Self::Small(SmallInsertSet {
set: smallvec![Insert {
leaf,
len: len as u32
}],
len: len as u32,
})
}
pub(crate) fn update(&mut self, from: usize, to: usize, new_leaf: LeafIndex) {
self.upgrade_if_needed();
match self {
Self::Small(set) => {
set.update(from, to, new_leaf);
}
Self::Large(set) => {
set.update(from, to, new_leaf);
}
}
}
pub(crate) fn update_many(&mut self, updates: &[(usize, usize, LeafIndex)]) {
if updates.is_empty() {
return;
}
if updates.len() == 1 {
let (from, to, leaf) = updates[0];
self.update(from, to, leaf);
return;
}
let len = self.len();
if len > MAX_FRAGMENT_LEN {
for &(from, to, leaf) in updates {
self.update(from, to, leaf);
}
return;
}
let mut dense: SmallVec<[LeafIndex; MAX_FRAGMENT_LEN]> = SmallVec::with_capacity(len);
match self {
InsertSet::Small(set) => {
for insert in set.set.iter() {
for _ in 0..insert.len {
dense.push(insert.leaf);
}
}
}
InsertSet::Large(set) => {
for insert in set.tree.iter() {
for _ in 0..insert.len {
dense.push(insert.leaf);
}
}
}
}
debug_assert_eq!(dense.len(), len);
for &(from, to, leaf) in updates {
debug_assert!(from <= to);
debug_assert!(to <= len);
for i in from..to {
dense[i] = leaf;
}
}
let mut new_set: SmallVec<[Insert; 1]> = SmallVec::new();
if !dense.is_empty() {
let mut cur_leaf = dense[0];
let mut cur_len: usize = 1;
for &leaf in dense.iter().skip(1) {
if leaf == cur_leaf {
cur_len += 1;
} else {
new_set.push(Insert {
leaf: cur_leaf,
len: cur_len as u32,
});
cur_leaf = leaf;
cur_len = 1;
}
}
new_set.push(Insert {
leaf: cur_leaf,
len: cur_len as u32,
});
}
*self = InsertSet::Small(SmallInsertSet {
set: new_set,
len: len as u32,
});
}
pub(crate) fn get_insert(&self, pos: usize) -> Option<LeafIndex> {
match self {
Self::Small(set) => {
let mut index = 0;
for insert in set.set.iter() {
if index + insert.len as usize > pos {
return Some(insert.leaf);
}
index += insert.len as usize;
}
unreachable!()
}
Self::Large(set) => set.get_insert(pos),
}
}
pub(crate) fn len(&self) -> usize {
match self {
Self::Small(set) => set.len as usize,
Self::Large(set) => *set.tree.root_cache() as usize,
}
}
pub(crate) fn iter_all(
&self,
peer: PeerID,
counter: Counter,
) -> Box<dyn Iterator<Item = IterCursor> + '_> {
match self {
InsertSet::Small(set) => {
let mut offset = 0;
Box::new(set.set.iter().map(move |elem| {
let ans = IterCursor::Insert {
leaf: elem.leaf,
id_span: IdSpan::new(
peer,
counter + offset as Counter,
counter + offset as Counter + elem.len as Counter,
),
};
offset += elem.len;
ans
}))
}
InsertSet::Large(set) => {
let mut offset = 0;
Box::new(set.tree.iter().map(move |elem| {
let ans = IterCursor::Insert {
leaf: elem.leaf,
id_span: IdSpan::new(
peer,
counter + offset as Counter,
counter + offset as Counter + elem.len as Counter,
),
};
offset += elem.len;
ans
}))
}
}
}
pub(crate) fn iter_range(
&self,
cur_id: ID,
target_start_counter: i32,
target_end_counter: i32,
) -> Box<dyn Iterator<Item = IterCursor> + '_> {
match self {
InsertSet::Small(set) => {
let mut offset = 0;
Box::new(set.set.iter().filter_map(move |elem| {
let id_span = IdSpan::new(
cur_id.peer,
(cur_id.counter + offset as Counter)
.max(target_start_counter)
.min(target_end_counter),
(cur_id.counter + offset as Counter + elem.len as Counter)
.max(target_start_counter)
.min(target_end_counter),
);
offset += elem.len;
if id_span.atom_len() == 0 {
return None;
}
let ans = IterCursor::Insert {
leaf: elem.leaf,
id_span,
};
Some(ans)
}))
}
InsertSet::Large(set) => {
let offset = (target_start_counter - cur_id.counter).max(0);
let (start, mut start_counter) = if offset > 0 {
match set.tree.query::<LengthFinder>(&(offset as usize)) {
Some(start) => (
std::ops::Bound::Included(start.cursor),
target_start_counter - start.cursor.offset as Counter,
),
_ => (std::ops::Bound::Unbounded, cur_id.counter),
}
} else {
(std::ops::Bound::Unbounded, cur_id.counter)
};
Box::new(
set.tree
.iter_range((start, std::ops::Bound::Unbounded))
.map(move |b| {
let id_span = IdSpan::new(
cur_id.peer,
(start_counter)
.max(target_start_counter)
.min(target_end_counter),
(start_counter + b.elem.rle_len() as Counter)
.max(target_start_counter)
.min(target_end_counter),
);
start_counter += b.elem.rle_len() as Counter;
if id_span.atom_len() == 0 {
return None;
}
let ans = IterCursor::Insert {
leaf: b.elem.leaf,
id_span,
};
Some(ans)
})
.take_while(|x| x.is_some())
.map(|x| x.unwrap()),
)
}
}
}
fn upgrade_if_needed(&mut self) {
match self {
InsertSet::Small(set) => {
if set.set.len() < SMALL_SET_MAX_LEN {
return;
}
let set = std::mem::take(&mut set.set);
let tree_set = LargeInsertSet::init_from_small(set);
*self = InsertSet::Large(tree_set);
}
InsertSet::Large(_) => {}
}
}
}
#[derive(Debug)]
pub(crate) struct SmallInsertSet {
pub set: SmallVec<[Insert; 1]>,
pub len: u32,
}
impl SmallInsertSet {
fn update(&mut self, from: usize, to: usize, new_leaf: LeafIndex) {
if from == to {
return;
}
let mut cur_scan_index: usize = 0;
let old_set = std::mem::take(&mut self.set);
let mut new_set = SmallVec::with_capacity(old_set.len() + 2);
let mut new_leaf_inserted = false;
for insert in old_set.iter() {
if new_leaf_inserted {
let end = cur_scan_index + insert.len as usize;
if end <= to {
cur_scan_index = end;
continue;
}
if cur_scan_index >= to {
new_set.push(*insert);
} else {
new_set.push(Insert {
leaf: insert.leaf,
len: (end - to) as u32,
});
}
cur_scan_index = end;
continue;
}
if cur_scan_index + insert.len as usize <= from {
new_set.push(*insert);
cur_scan_index += insert.len as usize;
} else {
debug_assert!(!new_leaf_inserted);
let elem_end = cur_scan_index + insert.len as usize;
if cur_scan_index < from {
new_set.push(Insert {
leaf: insert.leaf,
len: (from - cur_scan_index) as u32,
});
cur_scan_index = from;
}
if elem_end > to {
new_set.push(Insert {
leaf: new_leaf,
len: (to - cur_scan_index) as u32,
});
new_set.push(Insert {
leaf: insert.leaf,
len: (elem_end - to) as u32,
});
} else {
new_set.push(Insert {
leaf: new_leaf,
len: (to - cur_scan_index) as u32,
});
}
new_leaf_inserted = true;
cur_scan_index = elem_end;
}
}
self.set = new_set;
debug_assert_eq!(
self.len,
self.set.iter().map(|x| x.len as usize).sum::<usize>() as u32
);
}
}
pub(super) struct InsertSetBTreeTrait;
impl CanRemove for Insert {
fn can_remove(&self) -> bool {
self.len == 0
}
}
impl generic_btree::rle::Mergeable for Insert {
fn can_merge(&self, rhs: &Self) -> bool {
self.leaf == rhs.leaf
}
fn merge_right(&mut self, rhs: &Self) {
self.len += rhs.len;
}
fn merge_left(&mut self, left: &Self) {
self.len += left.len;
}
}
impl HasLength for Insert {
fn rle_len(&self) -> usize {
self.len as usize
}
}
impl generic_btree::rle::Sliceable for Insert {
fn _slice(&self, range: std::ops::Range<usize>) -> Self {
Insert {
leaf: self.leaf,
len: range.len() as u32,
}
}
}
impl TryInsert for Insert {
fn try_insert(&mut self, _: usize, elem: Self) -> Result<(), Self> {
if self.leaf == elem.leaf {
self.len += elem.len;
Ok(())
} else {
Err(elem)
}
}
}
impl BTreeTrait for InsertSetBTreeTrait {
type Elem = Insert;
type Cache = i32;
type CacheDiff = i32;
fn calc_cache_internal(
cache: &mut Self::Cache,
caches: &[generic_btree::Child<Self>],
) -> Self::CacheDiff {
let new: Self::Cache = caches.iter().map(|x| *x.cache()).sum();
let diff = new - *cache;
*cache = new;
diff
}
fn apply_cache_diff(cache: &mut Self::Cache, diff: &Self::CacheDiff) {
*cache += *diff;
}
fn merge_cache_diff(diff1: &mut Self::CacheDiff, diff2: &Self::CacheDiff) {
*diff1 += *diff2;
}
fn get_elem_cache(elem: &Self::Elem) -> Self::Cache {
elem.len as i32
}
fn new_cache_to_diff(cache: &Self::Cache) -> Self::CacheDiff {
*cache
}
fn sub_cache(cache_lhs: &Self::Cache, cache_rhs: &Self::Cache) -> Self::CacheDiff {
*cache_lhs - *cache_rhs
}
}
impl UseLengthFinder<InsertSetBTreeTrait> for InsertSetBTreeTrait {
fn get_len(cache: &i32) -> usize {
*cache as usize
}
}
#[derive(Debug)]
pub(crate) struct LargeInsertSet {
tree: Box<BTree<InsertSetBTreeTrait>>,
}
impl LargeInsertSet {
pub fn new() -> Self {
Self {
tree: Box::new(BTree::new()),
}
}
fn update(&mut self, from: usize, to: usize, new_leaf: LeafIndex) {
if from == to {
return;
}
let Some(from) = self.tree.query::<LengthFinder>(&from) else {
return;
};
let to = self
.tree
.query::<LengthFinder>(&to)
.map(|x| x.cursor)
.or_else(|| self.tree.end_cursor())
.unwrap();
self.tree.update(from.cursor..to, &mut |x| {
x.leaf = new_leaf;
None
});
}
fn get_insert(&self, pos: usize) -> Option<LeafIndex> {
let c = self.tree.query::<LengthFinder>(&pos)?.cursor;
Some(self.tree.get_elem(c.leaf)?.leaf)
}
fn init_from_small(set: SmallVec<[Insert; 1]>) -> LargeInsertSet {
let mut tree_set = LargeInsertSet::new();
for item in set.into_iter() {
tree_set.tree.push(item);
}
tree_set
}
}
}
#[derive(Debug, Clone, Copy)]
pub(super) struct Insert {
leaf: LeafIndex,
len: u32,
}
impl Cursor {
pub fn new_insert(leaf: LeafIndex, len: usize) -> Self {
Self::Insert(InsertSet::new(leaf, len))
}
pub fn new_move(leaf: LeafIndex, from_id: ID) -> Self {
Self::Move {
to: leaf,
from: from_id,
}
}
#[allow(unused)]
pub fn new_delete(id_span: IdSpan) -> Self {
Self::Delete(id_span)
}
fn update_insert(&mut self, from: usize, to: usize, new_leaf: LeafIndex) {
assert!(from <= to);
assert!(to <= self.rle_len());
match self {
Self::Insert(set) => {
set.update(from, to, new_leaf);
}
Self::Move { .. } => {
unreachable!("update_insert on Move")
}
_ => unreachable!(),
}
}
fn update_insert_many(&mut self, updates: &[(usize, usize, LeafIndex)]) {
match self {
Self::Insert(set) => {
set.update_many(updates);
}
Self::Move { .. } => {
unreachable!("update_insert_many on Move")
}
_ => unreachable!(),
}
}
fn get_insert(&self, pos: usize) -> Option<LeafIndex> {
if pos >= self.rle_len() {
return None;
}
match self {
Cursor::Insert(set) => set.get_insert(pos),
Cursor::Move { to, .. } => {
assert!(pos == 0);
Some(*to)
}
Cursor::Delete(_) => unreachable!(),
}
}
}
impl HasLength for Cursor {
fn rle_len(&self) -> usize {
match self {
Cursor::Insert(set) => set.len(),
Cursor::Delete(d) => d.atom_len(),
Cursor::Move { .. } => 1,
}
}
}
impl Mergeable for Cursor {
fn can_merge(&self, rhs: &Self) -> bool {
match (self, rhs) {
(Self::Insert(InsertSet::Small(a)), Self::Insert(InsertSet::Small(b))) => {
a.set.last().unwrap().leaf == b.set.first().unwrap().leaf && b.len == 1
}
(Self::Delete(a), Self::Delete(b)) => a.is_mergable(b, &()),
_ => false,
}
}
fn merge_right(&mut self, rhs: &Self) {
match (self, rhs) {
(Self::Insert(InsertSet::Small(a)), Self::Insert(InsertSet::Small(b))) => {
assert!(b.len == 1);
a.set.last_mut().unwrap().len += b.set.first().unwrap().len;
a.len += b.len;
}
(Self::Delete(a), Self::Delete(b)) => {
a.merge(b, &());
}
_ => unreachable!(),
}
}
fn merge_left(&mut self, _: &Self) {
unreachable!();
}
}
#[cfg(test)]
mod test {
use std::{any::Any, ops::Range, panic::AssertUnwindSafe};
use super::*;
use generic_btree::{
rle::{CanRemove, TryInsert},
BTree, BTreeTrait, UseLengthFinder,
};
use smallvec::smallvec;
#[derive(Debug, Clone)]
struct DummyElem;
impl generic_btree::rle::HasLength for DummyElem {
fn rle_len(&self) -> usize {
1
}
}
impl generic_btree::rle::Sliceable for DummyElem {
fn _slice(&self, range: Range<usize>) -> Self {
assert_eq!(range.start, 0);
assert_eq!(range.end, 1);
DummyElem
}
fn split(&mut self, _pos: usize) -> Self {
unreachable!()
}
}
impl generic_btree::rle::Mergeable for DummyElem {
fn can_merge(&self, _rhs: &Self) -> bool {
false
}
fn merge_right(&mut self, _rhs: &Self) {
unreachable!()
}
fn merge_left(&mut self, _left: &Self) {
unreachable!()
}
}
impl TryInsert for DummyElem {
fn try_insert(&mut self, _pos: usize, elem: Self) -> Result<(), Self> {
Err(elem)
}
}
impl CanRemove for DummyElem {
fn can_remove(&self) -> bool {
false
}
}
struct DummyTree;
impl BTreeTrait for DummyTree {
type Elem = DummyElem;
type Cache = isize;
type CacheDiff = isize;
const USE_DIFF: bool = true;
fn calc_cache_internal(
cache: &mut Self::Cache,
caches: &[generic_btree::Child<Self>],
) -> Self::CacheDiff {
let new_cache = caches.iter().map(|child| child.cache).sum::<isize>();
let diff = new_cache - *cache;
*cache = new_cache;
diff
}
fn apply_cache_diff(cache: &mut Self::Cache, diff: &Self::CacheDiff) {
*cache += diff;
}
fn merge_cache_diff(diff1: &mut Self::CacheDiff, diff2: &Self::CacheDiff) {
*diff1 += diff2;
}
fn get_elem_cache(_elem: &Self::Elem) -> Self::Cache {
1
}
fn new_cache_to_diff(cache: &Self::Cache) -> Self::CacheDiff {
*cache
}
fn sub_cache(cache_lhs: &Self::Cache, cache_rhs: &Self::Cache) -> Self::CacheDiff {
cache_lhs - cache_rhs
}
}
impl UseLengthFinder<DummyTree> for DummyTree {
fn get_len(cache: &isize) -> usize {
*cache as usize
}
}
fn dummy_leaf() -> LeafIndex {
let mut tree = BTree::<DummyTree>::new();
tree.push(DummyElem).leaf
}
fn dummy_leaves(n: usize) -> Vec<LeafIndex> {
let mut tree = BTree::<DummyTree>::new();
(0..n).map(|_| tree.push(DummyElem).leaf).collect()
}
fn panic_message(payload: Box<dyn Any + Send>) -> String {
if let Some(msg) = payload.downcast_ref::<String>() {
msg.clone()
} else if let Some(msg) = payload.downcast_ref::<&str>() {
msg.to_string()
} else {
"<non-string panic>".to_string()
}
}
#[test]
fn test_id_to_cursor() {
let _map = IdToCursor::default();
}
#[test]
fn zero_len_fragment_panics_with_left_0_right_3() {
let peer = 1;
let mut map = IdToCursor::default();
map.map.insert(
peer,
vec![
Fragment {
counter: 0,
cursor: Cursor::Insert(insert_set::InsertSet::Small(
insert_set::SmallInsertSet {
set: smallvec![],
len: 0,
},
)),
},
Fragment {
counter: 3,
cursor: Cursor::new_insert(dummy_leaf(), 1),
},
],
);
let err = std::panic::catch_unwind(AssertUnwindSafe(|| {
map.update_insert(IdSpan::new(peer, 0, 3), dummy_leaf());
}))
.expect_err("expected zero-length fragment to trip the invariant");
let msg = panic_message(err);
assert!(msg.contains("left == right"), "{msg}");
assert!(msg.contains("left: 0"), "{msg}");
assert!(msg.contains("right: 3"), "{msg}");
}
#[test]
fn zero_width_small_update_keeps_insert_set_non_empty() {
let leaf = dummy_leaf();
let mut set = insert_set::InsertSet::new(leaf, 3);
set.update(0, 0, dummy_leaf());
match set {
insert_set::InsertSet::Small(set) => {
assert_eq!(set.len, 3);
assert_eq!(set.set.len(), 1);
assert_eq!(set.set[0].len, 3);
}
insert_set::InsertSet::Large(_) => panic!("expected small insert set"),
}
}
#[test]
fn large_update_can_replace_the_tail_range() {
let leaves = dummy_leaves(4);
let mut set = insert_set::InsertSet::Small(insert_set::SmallInsertSet {
set: smallvec![
Insert {
leaf: leaves[0],
len: 100,
},
Insert {
leaf: leaves[1],
len: 100,
},
Insert {
leaf: leaves[2],
len: 100,
}
],
len: 300,
});
set.update(0, 1, leaves[3]);
let new_leaf = leaves[3];
set.update_many(&[(200, 300, new_leaf)]);
assert_ne!(set.get_insert(199), Some(new_leaf));
for i in 200..300 {
assert_eq!(set.get_insert(i), Some(new_leaf));
}
}
}