diff --git a/crates/loro-core/justfile b/crates/loro-core/justfile new file mode 100644 index 00000000..164a1fb4 --- /dev/null +++ b/crates/loro-core/justfile @@ -0,0 +1,18 @@ +build: + cargo build + +test: + cargo nextest run + +# test without proptest +test-fast: + RUSTFLAGS='--cfg no_proptest' cargo nextest run + +check-unsafe: + env RUSTFLAGS="-Funsafe-code --cap-lints=warn" cargo check + +deny: + cargo deny check + +crev: + cargo crev crate check diff --git a/crates/loro-core/src/container/map/map_container.rs b/crates/loro-core/src/container/map/map_container.rs index 08159f58..3baa62bb 100644 --- a/crates/loro-core/src/container/map/map_container.rs +++ b/crates/loro-core/src/container/map/map_container.rs @@ -70,6 +70,15 @@ impl MapContainer { }, }]); + if self.value.is_some() { + self.value + .as_mut() + .unwrap() + .as_map_mut() + .unwrap() + .insert(key.clone(), value.clone().into()); + } + self.state.insert( key, ValueSlot { @@ -120,6 +129,15 @@ impl Container for MapContainer { counter: op.id().counter, }, ); + + if self.value.is_some() { + self.value + .as_mut() + .unwrap() + .as_map_mut() + .unwrap() + .insert(v.key.clone(), v.value.clone().into()); + } } } _ => unreachable!(), diff --git a/crates/loro-core/src/container/map/tests.rs b/crates/loro-core/src/container/map/tests.rs index 10572950..d7f67222 100644 --- a/crates/loro-core/src/container/map/tests.rs +++ b/crates/loro-core/src/container/map/tests.rs @@ -48,7 +48,7 @@ mod map_proptest { map.insert(k.clone(), v.clone()); container.insert(k.clone().into(), v.clone()); let snapshot = container.get_value(); - for (key, value) in snapshot.to_map().unwrap().iter() { + for (key, value) in snapshot.as_map().unwrap().iter() { assert_eq!(map.get(&key.to_string()).map(|x|x.clone().into()), Some(value.clone())); } } diff --git a/crates/loro-core/src/dag.rs b/crates/loro-core/src/dag.rs index e69de29b..2e661aee 100644 --- a/crates/loro-core/src/dag.rs +++ b/crates/loro-core/src/dag.rs @@ -0,0 +1,135 @@ +use std::{ + collections::{BinaryHeap, HashMap, VecDeque}, + ops::Range, +}; + +use fxhash::FxHashMap; +mod test; + +use crate::{ + change::Lamport, + id::{ClientID, Counter, ID}, + span::{CounterSpan, IdSpan}, +}; + +pub trait DagNode { + fn dag_id_start(&self) -> ID; + fn lamport_start(&self) -> Lamport; + fn len(&self) -> usize; + fn deps(&self) -> &Vec; + + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn dag_id_span(&self) -> IdSpan { + let id = self.dag_id_start(); + IdSpan { + client_id: id.client_id, + counter: CounterSpan::new(id.counter, id.counter + self.len() as Counter), + } + } +} + +pub(crate) trait Dag { + type Node: DagNode; + + fn get(&self, id: ID) -> Option<&Self::Node>; + fn contains(&self, id: ID) -> bool; + fn frontier(&self) -> &[ID]; + fn roots(&self) -> Vec<&Self::Node>; + + fn get_common_ancestor(&self, a: ID, b: ID) -> Option { + if a.client_id == b.client_id { + if a.counter <= b.counter { + Some(a) + } else { + Some(b) + } + } else { + #[derive(Debug, PartialEq, Eq)] + struct OrdId { + id: ID, + lamport: Lamport, + } + + impl PartialOrd for OrdId { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.lamport.cmp(&other.lamport)) + } + } + + impl Ord for OrdId { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.lamport.cmp(&other.lamport) + } + } + + let mut a_map: HashMap, _> = FxHashMap::default(); + let mut b_map: HashMap, _> = FxHashMap::default(); + let mut a_heap: BinaryHeap = BinaryHeap::new(); + let mut b_heap: BinaryHeap = BinaryHeap::new(); + { + let a = self.get(a).unwrap(); + let b = self.get(b).unwrap(); + a_heap.push(OrdId { + id: a.dag_id_start(), + lamport: a.lamport_start() + a.len() as Lamport, + }); + b_heap.push(OrdId { + id: b.dag_id_start(), + lamport: b.lamport_start() + b.len() as Lamport, + }); + } + + while !a_heap.is_empty() || !b_heap.is_empty() { + let (a_heap, b_heap, a_map, b_map) = + if a_heap.peek().map(|x| x.lamport).unwrap_or(0) + < b_heap.peek().map(|x| x.lamport).unwrap_or(0) + { + // swap + (&mut b_heap, &mut a_heap, &mut b_map, &mut a_map) + } else { + (&mut a_heap, &mut b_heap, &mut a_map, &mut b_map) + }; + + while !a_heap.is_empty() + && a_heap.peek().map(|x| x.lamport).unwrap_or(0) + >= b_heap.peek().map(|x| x.lamport).unwrap_or(0) + { + let id = a_heap.pop().unwrap().id; + if let Some(range) = b_map.get(&id.client_id) { + if range.contains(&id.counter) { + return Some(id); + } + } + + let a = self.get(id).unwrap(); + for dep in a.deps() { + a_heap.push(OrdId { + id: *dep, + lamport: a.lamport_start() + a.len() as Lamport, + }); + } + if let Some(range) = a_map.get_mut(&id.client_id) { + range.start = a.dag_id_start().counter; + } else { + let span = a.dag_id_span(); + a_map.insert(id.client_id, span.counter.from..span.counter.to); + } + } + } + + None + } + } +} + +fn update_frontier(frontier: &mut Vec, new_node_id: ID, new_node_deps: &[ID]) { + frontier.retain(|x| { + !new_node_deps + .iter() + .any(|y| y.client_id == x.client_id && y.counter >= x.counter) + }); + frontier.push(new_node_id); +} diff --git a/crates/loro-core/src/dag/test.rs b/crates/loro-core/src/dag/test.rs new file mode 100644 index 00000000..695a5d1b --- /dev/null +++ b/crates/loro-core/src/dag/test.rs @@ -0,0 +1,343 @@ +#![cfg(test)] + +use proptest::proptest; + +use super::*; +use crate::{ + change::Lamport, + id::{ClientID, Counter, ID}, + span::{CounterSpan, IdSpan}, +}; +use std::collections::HashSet; +use std::iter::FromIterator; + +#[derive(Debug, PartialEq, Eq, Clone)] +struct TestNode { + id: ID, + lamport: Lamport, + len: usize, + deps: Vec, +} + +impl TestNode { + fn new(id: ID, lamport: Lamport, deps: Vec, len: usize) -> Self { + Self { + id, + lamport, + deps, + len, + } + } +} + +impl DagNode for TestNode { + fn dag_id_start(&self) -> ID { + self.id + } + fn lamport_start(&self) -> Lamport { + self.lamport + } + fn len(&self) -> usize { + self.len + } + fn deps(&self) -> &Vec { + &self.deps + } +} + +#[derive(Debug, PartialEq, Eq)] +struct TestDag { + nodes: FxHashMap>, + frontier: Vec, + version_vec: FxHashMap, + next_lamport: Lamport, + client_id: ClientID, +} + +impl Dag for TestDag { + type Node = TestNode; + + fn get(&self, id: ID) -> Option<&Self::Node> { + self.nodes.get(&id.client_id)?.iter().find(|node| { + id.counter >= node.id.counter && id.counter < node.id.counter + node.len as Counter + }) + } + fn frontier(&self) -> &[ID] { + &self.frontier + } + + fn roots(&self) -> Vec<&Self::Node> { + self.nodes.iter().map(|(_, v)| &v[0]).collect() + } + + fn contains(&self, id: ID) -> bool { + self.version_vec + .get(&id.client_id) + .and_then(|x| if *x > id.counter { Some(()) } else { None }) + .is_some() + } +} + +impl TestDag { + pub fn new(client_id: ClientID) -> Self { + Self { + nodes: FxHashMap::default(), + frontier: Vec::new(), + version_vec: FxHashMap::default(), + next_lamport: 0, + client_id, + } + } + + fn push(&mut self, len: usize) { + let client_id = self.client_id; + let counter = self.version_vec.entry(client_id).or_insert(0); + let id = ID::new(client_id, *counter); + *counter += len as u32; + let deps = std::mem::replace(&mut self.frontier, vec![id]); + self.nodes + .entry(client_id) + .or_insert(vec![]) + .push(TestNode::new(id, self.next_lamport, deps, len)); + self.next_lamport += len as u32; + } + + fn merge(&mut self, other: &TestDag) { + let mut pending = Vec::new(); + for (_, nodes) in other.nodes.iter() { + for (i, node) in nodes.iter().enumerate() { + if self._try_push_node(node, &mut pending, i) { + break; + } + } + } + + let mut current = pending; + let mut pending = Vec::new(); + while !pending.is_empty() || !current.is_empty() { + if current.is_empty() { + std::mem::swap(&mut pending, &mut current); + } + + let (client_id, index) = current.pop().unwrap(); + let node_vec = other.nodes.get(&client_id).unwrap(); + #[allow(clippy::needless_range_loop)] + for i in index..node_vec.len() { + let node = &node_vec[i]; + if self._try_push_node(node, &mut pending, i) { + break; + } + } + } + } + + fn _try_push_node( + &mut self, + node: &TestNode, + pending: &mut Vec<(u64, usize)>, + i: usize, + ) -> bool { + let client_id = node.id.client_id; + if self.contains(node.id) { + return false; + } + if node.deps.iter().any(|dep| !self.contains(*dep)) { + pending.push((client_id, i)); + return true; + } + update_frontier(&mut self.frontier, node.id, &node.deps); + self.nodes + .entry(client_id) + .or_insert(vec![]) + .push(node.clone()); + self.version_vec + .insert(client_id, node.id.counter + node.len as u32); + self.next_lamport = self.next_lamport.max(node.lamport + node.len as u32); + false + } +} + +#[test] +fn test_dag() { + let mut a = TestDag::new(0); + let mut b = TestDag::new(1); + a.push(1); + assert_eq!(a.frontier().len(), 1); + assert_eq!(a.frontier()[0].counter, 0); + b.push(1); + a.merge(&b); + assert_eq!(a.frontier().len(), 2); + a.push(1); + assert_eq!(a.frontier().len(), 1); + // a: 0 --(merge)--- 1 + // ↑ + // | + // b: 0 ---- + assert_eq!( + a.frontier()[0], + ID { + client_id: 0, + counter: 1 + } + ); + + // a: 0 --(merge)--- 1 --- 2 ------- + // ↑ | + // | ↓ + // b: 0 ------------1----------(merge) + a.push(1); + b.push(1); + b.merge(&a); + assert_eq!(b.next_lamport, 3); + assert_eq!(b.frontier().len(), 2); + assert_eq!( + b.get_common_ancestor(ID::new(0, 2), ID::new(1, 1)), + Some(ID::new(1, 0)) + ); +} + +#[cfg(not(no_proptest))] +mod find_common_ancestors { + use proptest::prelude::*; + + use crate::{array_mut_ref, unsafe_array_mut_ref}; + + use super::*; + + #[derive(Debug, Clone, Copy)] + struct Interaction { + dag_idx: usize, + merge_with: Option, + len: usize, + } + + prop_compose! { + fn gen_interaction(num: usize)(dag_idx in 0..num, merge_with in 0..num, length in 1..10, should_merge in 0..2) -> Interaction { + Interaction { + dag_idx, + merge_with: if should_merge == 1 && merge_with != dag_idx { Some(merge_with) } else { None }, + len: length as usize, + } + } + } + + proptest! { + #[test] + fn test_2dags( + before_merged_insertions in prop::collection::vec(gen_interaction(2), 0..300), + after_merged_insertions in prop::collection::vec(gen_interaction(2), 0..300) + ) { + test(2, before_merged_insertions, after_merged_insertions)?; + } + + #[test] + fn test_3dags( + before_merged_insertions in prop::collection::vec(gen_interaction(3), 0..300), + after_merged_insertions in prop::collection::vec(gen_interaction(3), 0..300) + ) { + test(3, before_merged_insertions, after_merged_insertions)?; + } + + #[test] + fn test_4dags( + before_merged_insertions in prop::collection::vec(gen_interaction(4), 0..300), + after_merged_insertions in prop::collection::vec(gen_interaction(4), 0..300) + ) { + test(4, before_merged_insertions, after_merged_insertions)?; + } + + #[test] + fn test_10dags( + before_merged_insertions in prop::collection::vec(gen_interaction(10), 0..300), + after_merged_insertions in prop::collection::vec(gen_interaction(10), 0..300) + ) { + test(10, before_merged_insertions, after_merged_insertions)?; + } + + #[test] + fn test_100dags( + before_merged_insertions in prop::collection::vec(gen_interaction(100), 0..2000), + after_merged_insertions in prop::collection::vec(gen_interaction(100), 0..2000) + ) { + test(100, before_merged_insertions, after_merged_insertions)?; + } + } + + fn preprocess(interactions: &mut [Interaction], num: i32) { + for interaction in interactions.iter_mut() { + interaction.dag_idx %= num as usize; + if let Some(ref mut merge_with) = interaction.merge_with { + *merge_with %= num as usize; + if *merge_with == interaction.dag_idx { + *merge_with = (*merge_with + 1) % num as usize; + } + } + } + } + + fn test( + dag_num: i32, + mut before_merge_insertion: Vec, + mut after_merge_insertion: Vec, + ) -> Result<(), TestCaseError> { + preprocess(&mut before_merge_insertion, dag_num); + preprocess(&mut after_merge_insertion, dag_num); + let mut dags = Vec::new(); + for i in 0..dag_num { + dags.push(TestDag::new(i as ClientID)); + } + + for interaction in before_merge_insertion { + apply(interaction, &mut dags); + } + + let (dag0,): (&mut TestDag,) = unsafe_array_mut_ref!(&mut dags, [0]); + for dag in &dags[1..] { + dag0.merge(dag); + } + + dag0.push(1); + let expected = dag0.frontier()[0]; + for dag in &mut dags[1..] { + dag.merge(dag0); + } + for interaction in after_merge_insertion.iter_mut() { + if let Some(merge) = interaction.merge_with { + // odd dag merges with the odd + // even dag merges with the even + if merge % 2 != interaction.dag_idx % 2 { + interaction.merge_with = None; + } + } + + apply(*interaction, &mut dags); + } + + let (dag0, dag1) = array_mut_ref!(&mut dags, [0, 1]); + dag1.push(1); + dag0.merge(dag1); + // dbg!(dag0, dag1, expected); + let actual = dags[0].get_common_ancestor( + dags[0].nodes.get(&0).unwrap().last().unwrap().id, + dags[1].nodes.get(&1).unwrap().last().unwrap().id, + ); + prop_assert_eq!(actual.unwrap(), expected); + Ok(()) + } + + fn apply(interaction: Interaction, dags: &mut [TestDag]) { + let Interaction { + dag_idx, + len, + merge_with, + } = interaction; + if let Some(merge_with) = merge_with { + let (dag, merge_target): (&mut TestDag, &mut TestDag) = + array_mut_ref!(dags, [dag_idx, merge_with]); + dag.push(len); + dag.merge(merge_target); + } else { + dags[dag_idx].push(len); + } + } +} diff --git a/crates/loro-core/src/macros.rs b/crates/loro-core/src/macros.rs index 21e3ad69..e7994ed9 100644 --- a/crates/loro-core/src/macros.rs +++ b/crates/loro-core/src/macros.rs @@ -20,3 +20,68 @@ macro_rules! fx_map { } }; } + +#[macro_export] +macro_rules! unsafe_array_mut_ref { + ($arr:expr, [$($idx:expr),*]) => { + { + unsafe { + ( + $( + { &mut *(&mut $arr[$idx] as *mut _) } + ),*, + ) + } + } + } +} + +#[macro_export] +macro_rules! array_mut_ref { + ($arr:expr, [$a0:expr, $a1:expr]) => {{ + #[inline] + fn borrow_mut_ref(arr: &mut [T], a0: usize, a1: usize) -> (&mut T, &mut T) { + debug_assert!(a0 != a1); + unsafe { + ( + &mut *(&mut arr[a0] as *mut _), + &mut *(&mut arr[a1] as *mut _), + ) + } + } + + borrow_mut_ref($arr, $a0, $a1) + }}; + ($arr:expr, [$a0:expr, $a1:expr, $a2:expr]) => {{ + #[inline] + fn borrow_mut_ref( + arr: &mut [T], + a0: usize, + a1: usize, + a2: usize, + ) -> (&mut T, &mut T, &mut T) { + debug_assert!(a0 != a1 && a1 != a2 && a0 != a2); + unsafe { + ( + &mut *(&mut arr[a0] as *mut _), + &mut *(&mut arr[a1] as *mut _), + &mut *(&mut arr[a2] as *mut _), + ) + } + } + + borrow_mut_ref($arr, $a0, $a1, $a2) + }}; +} + +#[test] +fn test_macro() { + let mut arr = vec![100, 101, 102, 103]; + let (a, b, c) = array_mut_ref!(&mut arr, [1, 2, 3]); + assert_eq!(*a, 101); + assert_eq!(*b, 102); + *a = 50; + *b = 51; + assert!(arr[1] == 50); + assert!(arr[2] == 51); +} diff --git a/crates/loro-core/src/span.rs b/crates/loro-core/src/span.rs index 966bf20d..9eeb2b7d 100644 --- a/crates/loro-core/src/span.rs +++ b/crates/loro-core/src/span.rs @@ -1,6 +1,8 @@ use crate::id::{ClientID, Counter, ID}; use rle::{HasLength, Mergable, Slice, Sliceable}; +/// [from, to) +/// this is different from [std::ops::Range] because `from` may be greater than `to` #[derive(Clone, Copy, PartialEq, Eq, Debug)] pub struct CounterSpan { pub from: Counter, @@ -18,7 +20,7 @@ impl CounterSpan { if self.from < self.to { self.from } else { - self.to + self.to + 1 } } @@ -27,9 +29,27 @@ impl CounterSpan { if self.from > self.to { self.from } else { - self.to + self.to - 1 } } + + #[inline] + pub fn intersect(&self, other: &Self) -> Option { + let min = self.min().max(other.min()); + let max = self.max().min(other.max()); + if min <= max { + Some(CounterSpan::new(min, max)) + } else { + None + } + } + + #[inline] + pub fn does_intersect(&self, other: &Self) -> bool { + let min = self.min().max(other.min()); + let max = self.max().min(other.max()); + min <= max + } } impl HasLength for CounterSpan { @@ -90,6 +110,33 @@ impl IdSpan { pub fn max(&self) -> Counter { self.counter.max() } + + #[inline] + pub fn does_intersect(&self, other: &Self) -> bool { + self.client_id == other.client_id && self.counter.does_intersect(&other.counter) + } + + #[inline] + pub fn intersect(&self, other: &Self) -> Option { + if self.client_id != other.client_id { + None + } else { + Some(IdSpan { + client_id: self.client_id, + counter: self.counter.intersect(&other.counter)?, + }) + } + } + + #[inline] + pub fn start(&self) -> ID { + ID::new(self.client_id, self.counter.min()) + } + + #[inline] + pub fn end(&self) -> ID { + ID::new(self.client_id, self.counter.max()) + } } impl HasLength for IdSpan { diff --git a/crates/loro-core/src/value.rs b/crates/loro-core/src/value.rs index a1af55b9..a37145ea 100644 --- a/crates/loro-core/src/value.rs +++ b/crates/loro-core/src/value.rs @@ -96,7 +96,7 @@ impl LoroValue { } #[inline] - pub fn to_map(&self) -> Option<&FxHashMap> { + pub fn as_map(&self) -> Option<&FxHashMap> { match self { LoroValue::Map(m) => Some(m), _ => None, @@ -104,7 +104,7 @@ impl LoroValue { } #[inline] - pub fn to_list(&self) -> Option<&Vec> { + pub fn as_list(&self) -> Option<&Vec> { match self { LoroValue::List(l) => Some(l), _ => None, @@ -112,7 +112,7 @@ impl LoroValue { } #[inline] - pub fn to_string(&self) -> Option<&SmString> { + pub fn as_string(&self) -> Option<&SmString> { match self { LoroValue::String(s) => Some(s), _ => None, @@ -120,7 +120,7 @@ impl LoroValue { } #[inline] - pub fn to_integer(&self) -> Option { + pub fn as_integer(&self) -> Option { match self { LoroValue::Integer(i) => Some(*i), _ => None, @@ -128,7 +128,7 @@ impl LoroValue { } #[inline] - pub fn to_double(&self) -> Option { + pub fn as_double(&self) -> Option { match self { LoroValue::Double(d) => Some(*d), _ => None, @@ -136,7 +136,7 @@ impl LoroValue { } #[inline] - pub fn to_bool(&self) -> Option { + pub fn as_bool(&self) -> Option { match self { LoroValue::Bool(b) => Some(*b), _ => None, @@ -144,7 +144,63 @@ impl LoroValue { } #[inline] - pub fn to_container(&self) -> Option<&ContainerID> { + pub fn as_container(&self) -> Option<&ContainerID> { + match self { + LoroValue::Unresolved(c) => Some(c), + _ => None, + } + } + + #[inline] + pub fn as_map_mut(&mut self) -> Option<&mut FxHashMap> { + match self { + LoroValue::Map(m) => Some(m), + _ => None, + } + } + + #[inline] + pub fn as_list_mut(&mut self) -> Option<&mut Vec> { + match self { + LoroValue::List(l) => Some(l), + _ => None, + } + } + + #[inline] + pub fn as_string_mut(&mut self) -> Option<&mut SmString> { + match self { + LoroValue::String(s) => Some(s), + _ => None, + } + } + + #[inline] + pub fn as_integer_mut(&mut self) -> Option<&mut i32> { + match self { + LoroValue::Integer(i) => Some(i), + _ => None, + } + } + + #[inline] + pub fn as_double_mut(&mut self) -> Option<&mut f64> { + match self { + LoroValue::Double(d) => Some(d), + _ => None, + } + } + + #[inline] + pub fn as_bool_mut(&mut self) -> Option<&mut bool> { + match self { + LoroValue::Bool(b) => Some(b), + _ => None, + } + } + + #[inline] + pub fn as_container_mut(&mut self) -> Option<&mut ContainerID> { match self { LoroValue::Unresolved(c) => Some(c), _ => None, diff --git a/crates/rle/Cargo.toml b/crates/rle/Cargo.toml index 19ed8d08..07b334c6 100644 --- a/crates/rle/Cargo.toml +++ b/crates/rle/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +num = "0.4.0" diff --git a/crates/rle/src/lib.rs b/crates/rle/src/lib.rs index 5af76106..368ecab7 100644 --- a/crates/rle/src/lib.rs +++ b/crates/rle/src/lib.rs @@ -1,2 +1,4 @@ -mod rle; -pub use crate::rle::{HasLength, Mergable, RleVec, SearchResult, Slice, SliceIterator, Sliceable}; +mod rle_trait; +mod rle_vec; +pub use crate::rle_trait::{HasLength, Mergable, Rle, Slice, Sliceable}; +pub use crate::rle_vec::{RleVec, SearchResult, SliceIterator}; diff --git a/crates/rle/src/rle_trait.rs b/crates/rle/src/rle_trait.rs new file mode 100644 index 00000000..d5ab3e81 --- /dev/null +++ b/crates/rle/src/rle_trait.rs @@ -0,0 +1,44 @@ +pub trait Mergable { + fn is_mergable(&self, _other: &Self, _conf: &Cfg) -> bool + where + Self: Sized, + { + false + } + + fn merge(&mut self, _other: &Self, _conf: &Cfg) + where + Self: Sized, + { + unreachable!() + } +} + +pub trait Sliceable { + fn slice(&self, from: usize, to: usize) -> Self; +} + +#[derive(Debug, Clone, Copy)] +pub struct Slice<'a, T> { + pub value: &'a T, + pub start: usize, + pub end: usize, +} + +impl Slice<'_, T> { + pub fn into_inner(&self) -> T { + self.value.slice(self.start, self.end) + } +} + +pub trait HasLength { + fn is_empty(&self) -> bool { + self.len() == 0 + } + + fn len(&self) -> usize; +} + +pub trait Rle: HasLength + Sliceable + Mergable {} + +impl, Cfg> Rle for T {} diff --git a/crates/rle/src/rle.rs b/crates/rle/src/rle_vec.rs similarity index 92% rename from crates/rle/src/rle.rs rename to crates/rle/src/rle_vec.rs index a22717f2..f7e4e4a9 100644 --- a/crates/rle/src/rle.rs +++ b/crates/rle/src/rle_vec.rs @@ -1,4 +1,7 @@ -use std::ops::Range; +use num::{cast, Integer, NumCast}; +use std::ops::{Range, Sub}; + +use crate::{HasLength, Mergable, Rle, Slice, Sliceable}; /// RleVec is a vector that can be compressed using run-length encoding. /// @@ -20,40 +23,6 @@ pub struct RleVec { cfg: Cfg, } -pub trait Mergable { - fn is_mergable(&self, _other: &Self, _conf: &Cfg) -> bool - where - Self: Sized, - { - false - } - - fn merge(&mut self, _other: &Self, _conf: &Cfg) - where - Self: Sized, - { - unreachable!() - } -} - -pub trait Sliceable { - fn slice(&self, from: usize, to: usize) -> Self; -} - -impl Slice<'_, T> { - pub fn into_inner(&self) -> T { - self.value.slice(self.start, self.end) - } -} - -pub trait HasLength { - fn is_empty(&self) -> bool { - self.len() == 0 - } - - fn len(&self) -> usize; -} - pub struct SearchResult<'a, T> { pub element: &'a T, pub merged_index: usize, @@ -253,13 +222,6 @@ pub struct SliceIterator<'a, T> { end_offset: usize, } -#[derive(Debug, Clone, Copy)] -pub struct Slice<'a, T> { - pub value: &'a T, - pub start: usize, - pub end: usize, -} - impl<'a, T: HasLength> Iterator for SliceIterator<'a, T> { type Item = Slice<'a, T>; @@ -316,6 +278,31 @@ impl HasLength for RleVec { } } +impl Sliceable for Range { + fn slice(&self, start: usize, end: usize) -> Self { + self.start + cast(start).unwrap()..self.start + cast(end).unwrap() + } +} + +impl + Copy> Mergable for Range { + fn is_mergable(&self, other: &Self, _: &()) -> bool { + other.start <= self.end && other.start >= self.start + } + + fn merge(&mut self, other: &Self, _conf: &()) + where + Self: Sized, + { + self.end = other.end; + } +} + +impl HasLength for Range { + fn len(&self) -> usize { + cast(self.end - self.start).unwrap() + } +} + #[cfg(test)] mod test { mod prime_value {