From 51e884a6dd313f1432741aec9fd09b4d11a9d428 Mon Sep 17 00:00:00 2001 From: Seph Gentle Date: Wed, 7 Aug 2024 14:41:16 +1000 Subject: [PATCH] Lots of small cleanups. Moved a lot of code from using old iter to TransformedOpsIterRaw --- src/dtrange.rs | 6 +- src/frontier.rs | 112 ++++++++++++------------ src/list/encoding/save_transformed.rs | 45 +++++++--- src/list/merge.rs | 74 ++++++++-------- src/list_fuzzer_tools.rs | 2 +- src/listmerge/advance_retreat.rs | 25 +----- src/listmerge/merge.rs | 86 ++++++++++--------- src/listmerge/to_old.rs | 4 +- src/listmerge/yjsspan.rs | 64 +++++++------- src/listmerge2/yjsspan.rs | 13 ++- src/ost/content_tree.rs | 77 +++++++---------- src/ost/index_tree.rs | 119 ++++---------------------- src/textinfo.rs | 27 +++++- 13 files changed, 292 insertions(+), 362 deletions(-) diff --git a/src/dtrange.rs b/src/dtrange.rs index 5709780f..9d480929 100644 --- a/src/dtrange.rs +++ b/src/dtrange.rs @@ -228,9 +228,9 @@ impl HasRleKey for DTRange { pub(crate) const UNDERWATER_START: usize = usize::MAX / 4; -pub(crate) fn is_underwater(lv: LV) -> bool { - lv >= UNDERWATER_START -} +// pub(crate) fn is_underwater(lv: LV) -> bool { +// lv >= UNDERWATER_START +// } // #[derive(Debug)] // struct RootTime; diff --git a/src/frontier.rs b/src/frontier.rs index 268aecc6..cdfca17e 100644 --- a/src/frontier.rs +++ b/src/frontier.rs @@ -405,62 +405,62 @@ pub fn local_frontier_is_root(branch: &[LV]) -> bool { } -// This walks both frontiers and finds how the frontier has changed. There's probably a better way -// to implement this. -struct FrontierDiff<'a> { - a: &'a [LV], - b: &'a [LV], -} - -pub(crate) fn diff_frontier_entries<'a>(a: &'a [LV], b: &'a [LV]) -> impl Iterator + 'a { - FrontierDiff { a, b } -} - - -fn slice_take_first(slice: &mut &[LV]) -> Option { - if let [first, tail @ ..] = slice { - *slice = tail; - Some(*first) - } else { None } -} - -impl<'a> Iterator for FrontierDiff<'a> { - type Item = (DiffFlag, LV); - - fn next(&mut self) -> Option { - match (self.a.split_first(), self.b.split_first()) { - (None, None) => None, - (Some((a, rest)), None) => { - self.a = rest; - Some((DiffFlag::OnlyA, *a)) - }, - (None, Some((b, rest))) => { - self.b = rest; - Some((DiffFlag::OnlyB, *b)) - }, - (Some((a, a_rest)), Some((b, b_rest))) => { - match a.cmp(b) { - Ordering::Equal => { - // Take from both. - self.a = a_rest; - self.b = b_rest; - Some((DiffFlag::Shared, *a)) - } - Ordering::Less => { - // Take from a. - self.a = a_rest; - Some((DiffFlag::OnlyA, *a)) - } - Ordering::Greater => { - // Take from b. - self.b = b_rest; - Some((DiffFlag::OnlyB, *a)) - } - } - } - } - } -} +// // This walks both frontiers and finds how the frontier has changed. There's probably a better way +// // to implement this. +// struct FrontierDiff<'a> { +// a: &'a [LV], +// b: &'a [LV], +// } +// +// pub(crate) fn diff_frontier_entries<'a>(a: &'a [LV], b: &'a [LV]) -> impl Iterator + 'a { +// FrontierDiff { a, b } +// } +// +// +// fn slice_take_first(slice: &mut &[LV]) -> Option { +// if let [first, tail @ ..] = slice { +// *slice = tail; +// Some(*first) +// } else { None } +// } +// +// impl<'a> Iterator for FrontierDiff<'a> { +// type Item = (DiffFlag, LV); +// +// fn next(&mut self) -> Option { +// match (self.a.split_first(), self.b.split_first()) { +// (None, None) => None, +// (Some((a, rest)), None) => { +// self.a = rest; +// Some((DiffFlag::OnlyA, *a)) +// }, +// (None, Some((b, rest))) => { +// self.b = rest; +// Some((DiffFlag::OnlyB, *b)) +// }, +// (Some((a, a_rest)), Some((b, b_rest))) => { +// match a.cmp(b) { +// Ordering::Equal => { +// // Take from both. +// self.a = a_rest; +// self.b = b_rest; +// Some((DiffFlag::Shared, *a)) +// } +// Ordering::Less => { +// // Take from a. +// self.a = a_rest; +// Some((DiffFlag::OnlyA, *a)) +// } +// Ordering::Greater => { +// // Take from b. +// self.b = b_rest; +// Some((DiffFlag::OnlyB, *a)) +// } +// } +// } +// } +// } +// } /// This method clones a version or parents vector. Its slightly faster and smaller than just /// calling v.clone() directly. diff --git a/src/list/encoding/save_transformed.rs b/src/list/encoding/save_transformed.rs index 3e9fb641..cf195ae0 100644 --- a/src/list/encoding/save_transformed.rs +++ b/src/list/encoding/save_transformed.rs @@ -3,7 +3,7 @@ use crate::encoding::Merger; use crate::list::encoding::leb::num_encode_zigzag_isize_old; use crate::list::encoding::encode_tools::{push_leb_usize, write_leb_bit_run}; use crate::list::ListOpLog; -use crate::listmerge::merge::TransformedResult; +use crate::listmerge::merge::{TransformedResult, TransformedResultRaw}; use crate::LV; /// *** This is EXPERIMENTAL work-in-progress code to save transformed positions *** @@ -36,22 +36,39 @@ impl ListOpLog { // }); // } - - for (_, op, xf) in self.get_xf_operations_full_old(from_version, self.cg.version.as_ref()) { - let val = match xf { - TransformedResult::BaseMoved(xf_pos) => { - let origin_pos = op.start() as isize; - XFState::XFBy(xf_pos as isize - origin_pos) - }, - TransformedResult::DeleteAlreadyHappened => XFState::Cancelled, + + for op in self.get_xf_operations_full_raw(from_version, self.cg.version.as_ref()) { + let (val, len) = match op { + TransformedResultRaw::FF(range) => { + (XFState::XFBy(0), range.len()) + } + TransformedResultRaw::Apply { xf_pos, op } => { + let origin_pos = op.1.start() as isize; + (XFState::XFBy(xf_pos as isize - origin_pos), op.len()) + } + TransformedResultRaw::DeleteAlreadyHappened(range) => { + (XFState::Cancelled, range.len()) + } }; - - tn_ops.push_rle(RleRun { - val, - len: op.len() - }); + + tn_ops.push_rle(RleRun { val, len }); } + // for (_, op, xf) in self.get_xf_operations_full_old(from_version, self.cg.version.as_ref()) { + // let val = match xf { + // TransformedResult::BaseMoved(xf_pos) => { + // let origin_pos = op.start() as isize; + // XFState::XFBy(xf_pos as isize - origin_pos) + // }, + // TransformedResult::DeleteAlreadyHappened => XFState::Cancelled, + // }; + // + // tn_ops.push_rle(RleRun { + // val, + // len: op.len() + // }); + // } + dbg!(&tn_ops.len()); // First pass: just write it. diff --git a/src/list/merge.rs b/src/list/merge.rs index 7f8014ed..5516dbcf 100644 --- a/src/list/merge.rs +++ b/src/list/merge.rs @@ -81,9 +81,12 @@ impl ListOpLog { #[cfg(feature = "merge_conflict_checks")] pub fn has_conflicts_when_merging(&self) -> bool { - let mut iter = TransformedOpsIter::new(&self.cg.graph, &self.cg.agent_assignment, + let mut iter = TransformedOpsIterRaw::new(&self.cg.graph, &self.cg.agent_assignment, &self.operation_ctx, &self.operations, &[], self.cg.version.as_ref()); + // let mut iter = TransformedOpsIter::new(&self.cg.graph, &self.cg.agent_assignment, + // &self.operation_ctx, &self.operations, + // &[], self.cg.version.as_ref()); for _ in &mut iter {} iter.concurrent_inserts_collided() } @@ -146,39 +149,40 @@ impl ListOpLog { (clears, normal_advances, ff) } - pub fn get_size_stats_during_xf(&self, samples: usize, allow_ff: bool) -> Vec<(LV, usize)> { - let every = usize::max(self.cg.len() / samples, 1); - let (plan, common) = self.cg.graph.make_m1_plan(Some(&self.operations), &[], self.cg.version.as_ref(), allow_ff); - let mut iter = TransformedOpsIter::from_plan(&self.cg.graph, &self.cg.agent_assignment, - &self.operation_ctx, &self.operations, - plan, common); - - let mut result = vec![]; - - let mut emit_next = 0; // Absolute LV. - while let Some((lv, _origin_op, _xf)) = iter.next() { - while emit_next <= lv { - result.push((emit_next, iter.tracker_count())); - emit_next += every; - } - } - // let mut emit_next: isize = 0; - // while let Some((lv, origin_op, _xf)) = iter.next() { - // let len_here = origin_op.len(); - // // println!("op {}", len_here); - // - // emit_next -= len_here as isize; - // - // while emit_next < 0 { - // // emit the size now - // result.push((lv, iter.tracker_count())); - // emit_next += every as isize; - // } - // } - - result - } + // pub fn get_size_stats_during_xf(&self, samples: usize, allow_ff: bool) -> Vec<(LV, usize)> { + // let every = usize::max(self.cg.len() / samples, 1); + // + // let (plan, common) = self.cg.graph.make_m1_plan(Some(&self.operations), &[], self.cg.version.as_ref(), allow_ff); + // let mut iter = TransformedOpsIter::from_plan(&self.cg.graph, &self.cg.agent_assignment, + // &self.operation_ctx, &self.operations, + // plan, common); + // + // let mut result = vec![]; + // + // let mut emit_next = 0; // Absolute LV. + // while let Some((lv, _origin_op, _xf)) = iter.next() { + // while emit_next <= lv { + // result.push((emit_next, iter.tracker_count())); + // emit_next += every; + // } + // } + // // let mut emit_next: isize = 0; + // // while let Some((lv, origin_op, _xf)) = iter.next() { + // // let len_here = origin_op.len(); + // // // println!("op {}", len_here); + // // + // // emit_next -= len_here as isize; + // // + // // while emit_next < 0 { + // // // emit the size now + // // result.push((lv, iter.tracker_count())); + // // emit_next += every as isize; + // // } + // // } + // + // result + // } } @@ -206,10 +210,10 @@ impl ListBranch { pub fn merge(&mut self, oplog: &ListOpLog, merge_frontier: &[LV]) { // let mut iter = oplog.get_xf_operations_full_raw(self.version.as_ref(), merge_frontier).merge_spans(); - let mut iter = oplog.get_xf_operations_full_raw(self.version.as_ref(), merge_frontier); + let iter = oplog.get_xf_operations_full_raw(self.version.as_ref(), merge_frontier); // println!("merge '{}' at {:?} + {:?}", self.content.to_string(), self.version, merge_frontier); - for xf in &mut iter { + for xf in iter { // dbg!(&xf); // dbg!(_lv, &origin_op, &xf); match xf { diff --git a/src/list_fuzzer_tools.rs b/src/list_fuzzer_tools.rs index c3cdcce8..f88a8863 100644 --- a/src/list_fuzzer_tools.rs +++ b/src/list_fuzzer_tools.rs @@ -12,7 +12,7 @@ use rle::zip::{rle_zip, rle_zip3}; use crate::{AgentId, LV}; use crate::listmerge::simple_oplog::*; -const USE_UNICODE: bool = true; +// const USE_UNICODE: bool = true; const UCHARS: [char; 23] = [ 'a', 'b', 'c', '1', '2', '3', ' ', '\n', // ASCII diff --git a/src/listmerge/advance_retreat.rs b/src/listmerge/advance_retreat.rs index aa2a9677..e1202cdf 100644 --- a/src/listmerge/advance_retreat.rs +++ b/src/listmerge/advance_retreat.rs @@ -69,7 +69,7 @@ impl M2Tracker { } } - fn adv_retreat_range(&mut self, mut range: DTRange, incr: i32) { + pub(super) fn adv_retreat_range(&mut self, mut range: DTRange, incr: i32) { // This method handles both advancing and retreating. In either case, because of the way // SpanState is designed, we need to either increment or decrement the state of every // visited item in the LV range. @@ -152,32 +152,13 @@ impl M2Tracker { } + #[inline] pub(crate) fn advance_by_range(&mut self, range: DTRange) { self.adv_retreat_range(range, 1); } + #[inline] pub(crate) fn retreat_by_range(&mut self, range: DTRange) { self.adv_retreat_range(range, -1); } - - // // if let Some(mut cursor) = self.range_tree.try_find_item(last_lv) { - // // // Try just modifying the item directly. - // // // - // // // The item will only exist in the range tree at all if it was an insert. - // // let (e, _offset) = cursor.0.get_item(&self.range_tree); - // // // let chunk_start = last_lv - offset; - // // let start = range.start.max(e.id.start); - // // cursor.0.offset = start - e.id.start; - // // let max_len = range.end - start; - // // - // // range.end -= self.range_tree.mutate_entry( - // // &mut cursor, - // // max_len, - // // &mut notify_for(&mut self.index), - // // |e| { - // // e.current_state.mark_not_inserted_yet(); - // // } - // // ).0; - // // self.range_tree.emplace_cursor_unknown(cursor); - // // } else { } diff --git a/src/listmerge/merge.rs b/src/listmerge/merge.rs index 8a1bf5b2..6b7dd8a9 100644 --- a/src/listmerge/merge.rs +++ b/src/listmerge/merge.rs @@ -36,7 +36,7 @@ use crate::rle::{KVPair, RleSpanHelpers, RleVec}; use crate::textinfo::TextInfo; use crate::unicount::consume_chars; -const ALLOW_FF: bool = true; +// const ALLOW_FF: bool = true; #[cfg(feature = "dot_export")] const MAKE_GRAPHS: bool = false; @@ -721,6 +721,23 @@ impl<'a> TransformedOpsIterRaw<'a> { (remainder, result) } + + /// Returns if concurrent inserts ever collided at the same location while traversing. + #[cfg(feature = "merge_conflict_checks")] + pub(crate) fn concurrent_inserts_collided(&self) -> bool { + self.tracker.concurrent_inserts_collide + } + + #[cfg(feature = "ops_to_old")] + pub(crate) fn get_crdt_items(subgraph: &'a Graph, aa: &'a AgentAssignment, op_ctx: &'a ListOperationCtx, + ops: &'a RleVec>, + from_frontier: &[LV], merge_frontier: &[LV]) -> Vec { + // Importantly, we're passing allow_ff: false to make sure we get the actual output! + let (plan, _common) = subgraph.make_m1_plan(Some(ops), from_frontier, merge_frontier, false); + let mut iter = Self::from_plan(aa, op_ctx, ops, plan); + while let Some(_) = iter.next() {} // Consume all actions. + iter.tracker.dbg_ops + } } impl<'a> Iterator for TransformedOpsIterRaw<'a> { @@ -981,11 +998,9 @@ impl<'a> Iterator for TransformedOpsIter<'a> { match action { M1PlanAction::Retreat(span) => { self.tracker.retreat_by_range(*span); - // self.tracker.check_new_index(); } M1PlanAction::Advance(span) => { self.tracker.advance_by_range(*span); - // self.tracker.check_new_index(); } M1PlanAction::Apply(span) => { // println!("frontier {:?} + span {:?}", self.max_frontier, *span); @@ -1071,11 +1086,11 @@ pub fn reverse_str(s: &str) -> SmartString { } impl TextInfo { - pub(crate) fn get_xf_operations_full<'a>(&'a self, subgraph: &'a Graph, aa: &'a AgentAssignment, from: &[LV], merging: &[LV]) -> TransformedOpsIter<'a> { - TransformedOpsIter::new(subgraph, aa, &self.ctx, &self.ops, from, merging) + pub(crate) fn get_xf_operations_full<'a>(&'a self, subgraph: &'a Graph, aa: &'a AgentAssignment, from: &[LV], merging: &[LV]) -> TransformedOpsIterRaw<'a> { + TransformedOpsIterRaw::new(subgraph, aa, &self.ctx, &self.ops, from, merging) } - pub(crate) fn with_xf_iter R, R>(&self, cg: &CausalGraph, from: &[LV], merge_frontier: &[LV], f: F) -> R { + pub(crate) fn with_xf_iter R, R>(&self, cg: &CausalGraph, from: &[LV], merge_frontier: &[LV], f: F) -> R { // This is a big dirty mess for now, but it should be correct at least. let conflict = cg.graph.find_conflicting_simple(from, merge_frontier); @@ -1118,18 +1133,17 @@ impl TextInfo { /// `get_xf_operations` returns an iterator over the *transformed changes*. That is, the set of /// changes that could be applied linearly to a document to bring it up to date. pub fn xf_operations_from<'a>(&'a self, cg: &'a CausalGraph, from: &[LV], merging: &[LV]) -> Vec<(DTRange, Option)> { - self.with_xf_iter(cg, from, merging, |iter, _| { - iter.map(|(lv, mut origin_op, xf)| { - let len = origin_op.len(); - let op: Option = match xf { - BaseMoved(base) => { - origin_op.loc.span = (base..base+len).into(); - let content = origin_op.get_content(&self.ctx); - Some((origin_op, content).into()) + self.with_xf_iter(cg, from, merging, |raw_iter, _| { + let iter: TransformedSimpleOpsIter = raw_iter.into(); + iter.map(|op| { + match op { + TransformedSimpleOp::Apply(metrics) => { + let span = metrics.span(); + let content = metrics.1.get_content(&self.ctx); + (span, Some((metrics.1, content).into())) } - DeleteAlreadyHappened => None, - }; - ((lv..lv + len).into(), op) + TransformedSimpleOp::DeleteAlreadyHappened(r) => (r, None), + } }).collect() }) } @@ -1147,36 +1161,26 @@ impl TextInfo { // println!("merge from {:?} + {:?}", from, merge_frontier); self.with_xf_iter(cg, from, merge_frontier, |iter, final_frontier| { // iter.plan.dbg_print(); - for (_lv, origin_op, xf) in iter { - match (origin_op.kind, xf) { - (ListOpKind::Ins, BaseMoved(pos)) => { - debug_assert!(origin_op.content_pos.is_some()); // Ok if this is false - we'll just fill with junk. - let content = origin_op.get_content(&self.ctx).unwrap(); - // println!("Insert '{}' at {} (len {})", content, pos, origin_op.len()); - assert!(pos <= into.len_chars()); - if origin_op.loc.fwd { - into.insert(pos, content); - } else { - // We need to insert the content in reverse order. - let c = reverse_str(content); - into.insert(pos, &c); - } - // println!("-> doc len {}", into.len_chars()); - } - (_, DeleteAlreadyHappened) => {}, // Discard. + for xf in iter { + match xf { + TransformedResultRaw::Apply { xf_pos, op: KVPair(_, mut op) } => { + op.transpose_to(xf_pos); + self.apply_op_to(op, into); + } - (ListOpKind::Del, BaseMoved(del_start)) => { - let del_end = del_start + origin_op.len(); - // println!("Delete {}..{} (len {}) doc len {}", del_start, del_end, origin_op.len(), into.len_chars()); - // println!("Delete {}..{} (len {}) '{}'", del_start, del_end, origin_op.len(), to.content.slice_chars(del_start..del_end).collect::()); - debug_assert!(into.len_chars() >= del_end); - into.remove(del_start..del_end); + TransformedResultRaw::FF(range) => { + // Activate *SUPER FAST MODE*. + for KVPair(_, op) in self.ops.iter_range_ctx(range, &self.ctx) { + // dbg!(&op); + self.apply_op_to(op, into); + } } + + TransformedResultRaw::DeleteAlreadyHappened(_) => {} // Discard. } } - // iter.into_frontier() final_frontier }) } diff --git a/src/listmerge/to_old.rs b/src/listmerge/to_old.rs index 78a9d505..fb4b513d 100644 --- a/src/listmerge/to_old.rs +++ b/src/listmerge/to_old.rs @@ -4,7 +4,7 @@ use crate::causalgraph::agent_assignment::remote_ids::RemoteVersionSpan; use crate::list::ListOpLog; use crate::list::operation::ListOpKind; use crate::listmerge::M2Tracker; -use crate::listmerge::merge::TransformedOpsIter; +use crate::listmerge::merge::{TransformedOpsIter, TransformedOpsIterRaw}; use crate::rev_range::RangeRev; use crate::unicount::{chars_to_bytes, split_at_char}; @@ -171,7 +171,7 @@ impl MergableSpan for OldCRDTOpInternal { impl ListOpLog { #[cfg(feature = "ops_to_old")] pub fn dbg_items(&self) -> Vec { - let items = TransformedOpsIter::get_crdt_items(&self.cg.graph, &self.cg.agent_assignment, + let items = TransformedOpsIterRaw::get_crdt_items(&self.cg.graph, &self.cg.agent_assignment, &self.operation_ctx, &self.operations, &[], self.cg.version.as_ref()); diff --git a/src/listmerge/yjsspan.rs b/src/listmerge/yjsspan.rs index f09c5fe4..599a520b 100644 --- a/src/listmerge/yjsspan.rs +++ b/src/listmerge/yjsspan.rs @@ -69,38 +69,38 @@ impl SpanState { } } - pub(crate) fn undelete(&mut self) { - if self.0 >= DELETED_ONCE.0 { - // Double delete -> single delete - // Deleted -> inserted - self.0 -= 1; - } else { - panic!("Invalid undelete target"); - } - } - - pub(crate) fn raw_decrement(&mut self) { - debug_assert!(self.0 >= 1); - self.0 -= 1; - } - pub(crate) fn raw_increment(&mut self) { - self.0 += 1; - } - - pub(crate) fn mark_inserted(&mut self) { - if *self != NOT_INSERTED_YET { - panic!("Invalid insert target - item already marked as inserted"); - } - - *self = INSERTED; - } - pub(crate) fn mark_not_inserted_yet(&mut self) { - if *self != INSERTED { - panic!("Invalid insert target - item not inserted"); - } - - *self = NOT_INSERTED_YET; - } + // pub(crate) fn undelete(&mut self) { + // if self.0 >= DELETED_ONCE.0 { + // // Double delete -> single delete + // // Deleted -> inserted + // self.0 -= 1; + // } else { + // panic!("Invalid undelete target"); + // } + // } + + // pub(crate) fn raw_decrement(&mut self) { + // debug_assert!(self.0 >= 1); + // self.0 -= 1; + // } + // pub(crate) fn raw_increment(&mut self) { + // self.0 += 1; + // } + // + // pub(crate) fn mark_inserted(&mut self) { + // if *self != NOT_INSERTED_YET { + // panic!("Invalid insert target - item already marked as inserted"); + // } + // + // *self = INSERTED; + // } + // pub(crate) fn mark_not_inserted_yet(&mut self) { + // if *self != INSERTED { + // panic!("Invalid insert target - item not inserted"); + // } + // + // *self = NOT_INSERTED_YET; + // } } impl Debug for CRDTSpan { diff --git a/src/listmerge2/yjsspan.rs b/src/listmerge2/yjsspan.rs index 32ee476d..1c2301cf 100644 --- a/src/listmerge2/yjsspan.rs +++ b/src/listmerge2/yjsspan.rs @@ -4,7 +4,12 @@ use crate::{DTRange, LV}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Ord, PartialOrd)] pub(crate) enum SpanState { - NotInsertedYet = 0, Inserted = 1, Deleted = 2 + #[allow(unused)] + NotInsertedYet = 0, + #[allow(unused)] + Inserted = 1, + #[allow(unused)] + Deleted = 2, } use SpanState::*; @@ -31,9 +36,9 @@ pub struct YjsSpan { // at usize::MAX / 4. pub(crate) const UNDIFFERENTIATED_START: usize = usize::MAX / 4; -pub(crate) fn is_undiff(time: LV) -> bool { - time >= UNDIFFERENTIATED_START -} +// pub(crate) fn is_undiff(time: LV) -> bool { +// time >= UNDIFFERENTIATED_START +// } impl YjsSpan { diff --git a/src/ost/content_tree.rs b/src/ost/content_tree.rs index dca1a024..1141de18 100644 --- a/src/ost/content_tree.rs +++ b/src/ost/content_tree.rs @@ -30,9 +30,9 @@ pub(crate) trait Content: SplitableSpan + MergableSpan + Copy + HasLength { fn none() -> Self; } -trait LeafMap { - fn notify(&mut self, range: DTRange, leaf_idx: LeafIdx); -} +// trait LeafMap { +// fn notify(&mut self, range: DTRange, leaf_idx: LeafIdx); +// } pub(crate) trait FlushUpdate: Default { fn flush(&self, tree: &mut ContentTree, leaf_idx: LeafIdx); @@ -144,7 +144,7 @@ fn initial_root_leaf() -> ContentLeaf { // // const EMPTY_NODE_CHILD: ContentNodeChild = (LenPair { cur: 0, end: 0 }, usize::MAX); -const EMPTY_LEN_PAIR: LenPair = LenPair { cur: 0, end: 0 }; +// const EMPTY_LEN_PAIR: LenPair = LenPair { cur: 0, end: 0 }; impl ContentLeaf { #[inline(always)] @@ -258,9 +258,9 @@ impl ContentCursor { self.offset += 1; } - fn flush_delta(&self, tree: &mut ContentTree, delta: LenUpdate) { - tree.flush_delta_len(self.leaf_idx, delta); - } + // fn flush_delta(&self, tree: &mut ContentTree, delta: LenUpdate) { + // tree.flush_delta_len(self.leaf_idx, delta); + // } pub fn get_item<'a, V: Content>(&self, tree: &'a ContentTree) -> (&'a V, usize) { let leaf = &tree[self.leaf_idx]; @@ -344,14 +344,14 @@ impl DeltaCursor { has_next } - pub(crate) fn next_entry(&mut self, tree: &mut ContentTree) -> bool { - let (has_next, flush_leaf) = self.0.next_entry(tree); - if let Some(flush_leaf) = flush_leaf { - tree.flush_delta_and_clear(flush_leaf, &mut self.1); - } - - has_next - } + // pub(crate) fn next_entry(&mut self, tree: &mut ContentTree) -> bool { + // let (has_next, flush_leaf) = self.0.next_entry(tree); + // if let Some(flush_leaf) = flush_leaf { + // tree.flush_delta_and_clear(flush_leaf, &mut self.1); + // } + // + // has_next + // } pub fn flush(self, tree: &mut ContentTree) { tree.flush_delta_len(self.0.leaf_idx, self.1); @@ -892,29 +892,14 @@ impl ContentTree { LeafIdx(new_leaf_idx) } - /// This function blindly assumes the item is definitely in the recursive children. - /// - /// Returns (child index, len_remaining). - fn find_pos_in_node(node: &ContentNode, mut at_pos: usize) -> (usize, usize) { - for i in 0..NODE_CHILDREN { - let width = node.child_width[i].get::(); - if at_pos <= width { return (node.child_indexes[i], at_pos); } - at_pos -= width; - } - panic!("Position not in node"); - } - // /// This function blindly assumes the item is definitely in the recursive children. // /// - // /// Returns (child index, relative position, requested len remaining). - // fn find_pos_in_node_2(node: &ContentNode, at_pos: usize) -> (usize, LenPair) { - // let mut offset = LenPair::default(); + // /// Returns (child index, len_remaining). + // fn find_pos_in_node(node: &ContentNode, mut at_pos: usize) -> (usize, usize) { // for i in 0..NODE_CHILDREN { - // let width = node.child_width[i]; - // if at_pos <= offset.get::() + width.get::() { - // return (node.child_indexes[i], offset); - // } - // offset += width; + // let width = node.child_width[i].get::(); + // if at_pos <= width { return (node.child_indexes[i], at_pos); } + // at_pos -= width; // } // panic!("Position not in node"); // } @@ -936,15 +921,15 @@ impl ContentTree { panic!("Position not in node"); } - /// Returns (index, offset). - fn find_pos_in_leaf(leaf: &ContentLeaf, mut at_pos: usize) -> (usize, usize) { - for i in 0..LEAF_CHILDREN { - let width = leaf.children[i].content_len::(); - if at_pos <= width { return (i, at_pos); } - at_pos -= width; - } - panic!("Position not in leaf"); - } + // /// Returns (index, offset). + // fn find_pos_in_leaf(leaf: &ContentLeaf, mut at_pos: usize) -> (usize, usize) { + // for i in 0..LEAF_CHILDREN { + // let width = leaf.children[i].content_len::(); + // if at_pos <= width { return (i, at_pos); } + // at_pos -= width; + // } + // panic!("Position not in leaf"); + // } /// Returns (index, end_pos, offset). fn find_cur_pos_in_leaf(leaf: &ContentLeaf, mut at_cur_pos: usize) -> (usize, usize, usize) { @@ -1309,10 +1294,6 @@ impl ContentTree { self.iter().merge_spans() } - pub fn to_vec(&self) -> Vec { - self.iter().collect::>() - } - pub fn count_entries(&self) -> usize { let mut count = 0; for (_idx, children) in self.iter_leaves() { diff --git a/src/ost/index_tree.rs b/src/ost/index_tree.rs index d82ca524..91274517 100644 --- a/src/ost/index_tree.rs +++ b/src/ost/index_tree.rs @@ -21,10 +21,6 @@ pub(crate) struct IndexTree { height: usize, root: usize, cursor: Cell<(LV, IndexCursor)>, - - // Linked lists. - // free_leaf_pool_head: LeafIdx, - // free_node_pool_head: NodeIdx, } #[derive(Debug, Clone, Copy)] @@ -194,8 +190,6 @@ impl IndexTree { height: 0, root: 0, cursor: Default::default(), - // free_leaf_pool_head: LeafIdx(usize::MAX), - // free_node_pool_head: NodeIdx(usize::MAX), } } @@ -205,8 +199,6 @@ impl IndexTree { self.height = 0; self.root = 0; self.cursor = Default::default(); - // self.free_leaf_pool_head = LeafIdx(usize::MAX); - // self.free_node_pool_head = NodeIdx(usize::MAX); self.leaves.push(initial_root_leaf()); } @@ -754,62 +746,6 @@ impl IndexTree { self.leaf_upper_bound(&self.leaves[idx]) } - // fn discard_leaf_internal(leaves: &mut Vec>, leaf_pool_head: &mut LeafIdx, leaf_idx: LeafIdx) { - // let leaf = &mut leaves[leaf_idx.0]; - // leaf.next_leaf = *leaf_pool_head; - // *leaf_pool_head = leaf_idx; - // } - - // fn discard_leaf(&mut self, leaf_idx: LeafIdx) { - // // println!("Discard leaf {:?}", leaf_idx); - // - // // Self::discard_leaf_internal(&mut self.leaves, &mut self.free_leaf_pool_head, leaf_idx); - // let leaf = &mut self.leaves[leaf_idx.0]; - // leaf.next_leaf = self.free_leaf_pool_head; - // self.free_leaf_pool_head = leaf_idx; - // - // if cfg!(debug_assertions) { - // // Make sure discarded leaves aren't added multiple times to the discard queue. - // assert_ne!(leaf.parent, NodeIdx(0xfefe)); - // leaf.parent = NodeIdx(0xfefe); - // } - // } - - // fn discard_node(&mut self, idx: usize, height: usize) { - // if height == 0 { - // self.discard_leaf(LeafIdx(idx)); - // } else { - // // println!("DISCARD NODE {idx}"); - // // Move it to the free list. - // let node = &mut self.nodes[idx]; - // node.parent = self.free_node_pool_head; - // self.free_node_pool_head = NodeIdx(idx); - // - // let old_children = mem::replace(&mut node.children, [EMPTY_NODE_CHILD; NODE_CHILDREN]); - // - // for (_, child_idx) in old_children { - // if child_idx == usize::MAX { break; } - // self.discard_node(child_idx, height - 1); - // } - // } - // } - // - // fn remove_and_queue_node_children(&mut self, node_idx: NodeIdx, child_range: Range, _height: usize) { - // // This is horrible. - // // for i in child_range.clone() { - // // // TODO: Benchmark this against just copying out the children we care about. - // // let child_idx = self.nodes[node_idx.0].children[i].1; // boooo. - // // self.discard_node(child_idx, height - 1); - // // } - // - // // Bleh. I want to do this but the borrowck suuucks. - // // for (_, idx) in &node.children[..keep_child_idx] { - // // self.discard_node(*idx, height - 1); - // // } - // - // self.nodes[node_idx.0].remove_children(child_range); - // } - fn trim_node_start(&mut self, mut idx: usize, end: LV, mut height: usize) -> LeafIdx { while height > 0 { let mut node = &mut self.nodes[idx]; @@ -1260,40 +1196,21 @@ impl IndexTree { LeafIdx(0) } - pub fn count_items(&self) -> usize { - let mut count = 0; - let mut leaf = &self[self.first_leaf()]; - loop { - // SIMD should make this fast. - count += leaf.bounds.iter().filter(|b| **b != usize::MAX).count(); - - // There is always at least one leaf. - if leaf.is_last() { break; } - else { - leaf = &self[leaf.next_leaf]; - } - } - - count - } - - // /// returns number of internal nodes, leaves. - // pub fn count_obj_pool(&self) -> (usize, usize) { - // let mut nodes = 0; - // let mut leaves = 0; + // pub fn count_items(&self) -> usize { + // let mut count = 0; + // let mut leaf = &self[self.first_leaf()]; + // loop { + // // SIMD should make this fast. + // count += leaf.bounds.iter().filter(|b| **b != usize::MAX).count(); // - // let mut idx = self.free_node_pool_head; - // while idx.0 != usize::MAX { - // nodes += 1; - // idx = self.nodes[idx.0].parent; - // } - // let mut idx = self.free_leaf_pool_head; - // while idx.0 != usize::MAX { - // leaves += 1; - // idx = self.leaves[idx.0].next_leaf; + // // There is always at least one leaf. + // if leaf.is_last() { break; } + // else { + // leaf = &self[leaf.next_leaf]; + // } // } // - // (nodes, leaves) + // count // } /// Iterate over the contents of the index. Note the index tree may contain extra entries @@ -1307,10 +1224,6 @@ impl IndexTree { } } - pub fn to_vec(&self) -> Vec> { - self.iter().collect::>() - } - // Returns the next leaf pointer. fn dbg_check_walk(&self, idx: usize, height: usize, expect_start: Option, expect_parent: NodeIdx, mut expect_next_leaf: LeafIdx) -> LeafIdx { if height != 0 { @@ -1560,9 +1473,9 @@ mod test { use crate::list_fuzzer_tools::fuzz_multithreaded; use super::*; - #[derive(Debug, Copy, Clone, Eq, PartialEq)] - enum Foo { A, B, C } - use Foo::*; + // #[derive(Debug, Copy, Clone, Eq, PartialEq)] + // enum Foo { A, B, C } + // use Foo::*; #[derive(Debug, Copy, Clone, Eq, PartialEq, Default)] struct X(usize); @@ -1778,7 +1691,7 @@ mod test { #[test] #[ignore] - fn tree_fuzz_forever() { + fn index_tree_fuzz_forever() { fuzz_multithreaded(u64::MAX, |seed| { if seed % 100 == 0 { println!("Iteration {}", seed); diff --git a/src/textinfo.rs b/src/textinfo.rs index cdeff075..1ae8f3af 100644 --- a/src/textinfo.rs +++ b/src/textinfo.rs @@ -1,10 +1,13 @@ +use jumprope::JumpRopeBuf; use rle::HasLength; use crate::causalgraph::graph::Graph; use crate::dtrange::DTRange; use crate::frontier::Frontier; +use crate::list::ListOpLog; use crate::list::op_iter::{OpMetricsWithContent, OpMetricsIter}; use crate::list::op_metrics::{ListOperationCtx, ListOpMetrics}; -use crate::list::operation::TextOperation; +use crate::list::operation::{ListOpKind, TextOperation}; +use crate::listmerge::merge::reverse_str; use crate::LV; use crate::rle::KVPair; use crate::rle::rle_vec::RleVec; @@ -66,4 +69,26 @@ impl TextInfo { self.push_op_internal(op, v_range); self.frontier.replace_with_1(v_range.last()); } + + #[inline(always)] + pub(crate) fn apply_op_to(&self, op: ListOpMetrics, dest: &mut JumpRopeBuf) { + // let xf_pos = op.loc.span.start; + match op.kind { + ListOpKind::Ins => { + let content = self.ctx.get_str(ListOpKind::Ins, op.content_pos.unwrap()); + // assert!(pos <= self.content.len_chars()); + if op.loc.fwd { + dest.insert(op.loc.span.start, content); + } else { + // We need to insert the content in reverse order. + let c = reverse_str(content); + dest.insert(op.loc.span.start, &c); + } + } + ListOpKind::Del => { + dest.remove(op.loc.span.into()); + } + } + } + }