mirror of
https://github.com/loro-dev/loro.git
synced 2024-11-28 17:41:49 +00:00
feat: dag init
This commit is contained in:
parent
0d13c9562c
commit
1ca2b4226e
12 changed files with 770 additions and 54 deletions
18
crates/loro-core/justfile
Normal file
18
crates/loro-core/justfile
Normal file
|
@ -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
|
|
@ -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!(),
|
||||
|
|
|
@ -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()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<ID>;
|
||||
|
||||
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<ID> {
|
||||
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<std::cmp::Ordering> {
|
||||
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<ClientID, Range<Counter>, _> = FxHashMap::default();
|
||||
let mut b_map: HashMap<ClientID, Range<Counter>, _> = FxHashMap::default();
|
||||
let mut a_heap: BinaryHeap<OrdId> = BinaryHeap::new();
|
||||
let mut b_heap: BinaryHeap<OrdId> = 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<ID>, 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);
|
||||
}
|
343
crates/loro-core/src/dag/test.rs
Normal file
343
crates/loro-core/src/dag/test.rs
Normal file
|
@ -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<ID>,
|
||||
}
|
||||
|
||||
impl TestNode {
|
||||
fn new(id: ID, lamport: Lamport, deps: Vec<ID>, 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<ID> {
|
||||
&self.deps
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct TestDag {
|
||||
nodes: FxHashMap<ClientID, Vec<TestNode>>,
|
||||
frontier: Vec<ID>,
|
||||
version_vec: FxHashMap<ClientID, Counter>,
|
||||
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<usize>,
|
||||
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<Interaction>,
|
||||
mut after_merge_insertion: Vec<Interaction>,
|
||||
) -> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<T>(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<T>(
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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<Self> {
|
||||
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<Self> {
|
||||
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 {
|
||||
|
|
|
@ -96,7 +96,7 @@ impl LoroValue {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_map(&self) -> Option<&FxHashMap<InternalString, LoroValue>> {
|
||||
pub fn as_map(&self) -> Option<&FxHashMap<InternalString, LoroValue>> {
|
||||
match self {
|
||||
LoroValue::Map(m) => Some(m),
|
||||
_ => None,
|
||||
|
@ -104,7 +104,7 @@ impl LoroValue {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_list(&self) -> Option<&Vec<LoroValue>> {
|
||||
pub fn as_list(&self) -> Option<&Vec<LoroValue>> {
|
||||
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<i32> {
|
||||
pub fn as_integer(&self) -> Option<i32> {
|
||||
match self {
|
||||
LoroValue::Integer(i) => Some(*i),
|
||||
_ => None,
|
||||
|
@ -128,7 +128,7 @@ impl LoroValue {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_double(&self) -> Option<f64> {
|
||||
pub fn as_double(&self) -> Option<f64> {
|
||||
match self {
|
||||
LoroValue::Double(d) => Some(*d),
|
||||
_ => None,
|
||||
|
@ -136,7 +136,7 @@ impl LoroValue {
|
|||
}
|
||||
|
||||
#[inline]
|
||||
pub fn to_bool(&self) -> Option<bool> {
|
||||
pub fn as_bool(&self) -> Option<bool> {
|
||||
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<InternalString, LoroValue>> {
|
||||
match self {
|
||||
LoroValue::Map(m) => Some(m),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_list_mut(&mut self) -> Option<&mut Vec<LoroValue>> {
|
||||
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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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};
|
||||
|
|
44
crates/rle/src/rle_trait.rs
Normal file
44
crates/rle/src/rle_trait.rs
Normal file
|
@ -0,0 +1,44 @@
|
|||
pub trait Mergable<Cfg = ()> {
|
||||
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<T: Sliceable> 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<Cfg = ()>: HasLength + Sliceable + Mergable<Cfg> {}
|
||||
|
||||
impl<T: HasLength + Sliceable + Mergable<Cfg>, Cfg> Rle<Cfg> for T {}
|
|
@ -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<T> is a vector that can be compressed using run-length encoding.
|
||||
///
|
||||
|
@ -20,40 +23,6 @@ pub struct RleVec<T, Cfg = ()> {
|
|||
cfg: Cfg,
|
||||
}
|
||||
|
||||
pub trait Mergable<Cfg = ()> {
|
||||
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<T: Sliceable> 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<T> HasLength for RleVec<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Integer + NumCast + Copy> Sliceable for Range<T> {
|
||||
fn slice(&self, start: usize, end: usize) -> Self {
|
||||
self.start + cast(start).unwrap()..self.start + cast(end).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd<T> + Copy> Mergable for Range<T> {
|
||||
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<T: num::Integer + NumCast + Copy> HasLength for Range<T> {
|
||||
fn len(&self) -> usize {
|
||||
cast(self.end - self.start).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
mod prime_value {
|
Loading…
Reference in a new issue