refactor: add container type & twist many things

This commit is contained in:
Zixuan Chen 2022-07-15 20:34:23 +08:00
parent ad7a1c54ce
commit 268f3c1e96
9 changed files with 114 additions and 40 deletions

View file

@ -9,3 +9,4 @@ edition = "2021"
string_cache = "0.8.3"
rle = {path = "../rle"}
smallvec = "1.8.0"
smartstring = "1.0.1"

View file

@ -5,9 +5,9 @@ use rle::{HasLength, Mergable, RleVec};
use smallvec::SmallVec;
pub type Timestamp = i64;
pub type Lamport = u64;
pub type Lamport = u32;
/// Change
/// A `Change` contains a list of [Op]s.
#[derive(Debug)]
pub struct Change {
pub(crate) ops: RleVec<Op>,
@ -89,5 +89,6 @@ impl Mergable<ChangeMergeCfg> for Change {
self.id.client_id == other.id.client_id
&& self.id.counter + self.len() as u32 == other.id.counter
&& self.lamport + self.len() as Lamport == other.lamport
}
}

View file

@ -0,0 +1,48 @@
use rle::{HasLength, Mergable, Sliceable};
use smartstring::{LazyCompact, SmartString};
use crate::{InsertContent, ID};
/// Container is a special kind of op content. Each container has its own CRDT implementation.
/// Each [Op] must be associated with a container.
///
#[derive(Debug, Clone)]
pub struct Container {
parent: Option<ID>,
parent_slot: SmartString<LazyCompact>,
}
impl HasLength for Container {
fn len(&self) -> usize {
1
}
}
impl Mergable for Container {
fn is_mergable(&self, other: &Self, conf: &()) -> bool
where
Self: Sized,
{
false
}
fn merge(&mut self, other: &Self, conf: &())
where
Self: Sized,
{
unreachable!()
}
}
impl Sliceable for Container {
fn slice(&self, from: usize, to: usize) -> Self {
assert!(from == 0 && to == 1);
self.clone()
}
}
impl InsertContent for Container {
fn id(&self) -> crate::ContentTypeID {
crate::ContentTypeID::Container
}
}

View file

@ -1,7 +1,7 @@
#![allow(dead_code, unused_imports)]
#![feature(trait_upcasting)]
mod change;
mod container;
mod id;
mod id_span;
mod log_store;

View file

@ -2,6 +2,7 @@ use crate::{id::ID, id_span::IdSpan};
use rle::{HasLength, Mergable, RleVec, Sliceable};
mod insert_content;
mod op_content;
mod op_proxy;
pub use insert_content::*;
pub use op_content::*;
@ -15,6 +16,14 @@ pub enum OpType {
}
#[derive(Debug, Clone)]
/// Operation is a unit of change.
///
/// It has 3 types:
/// - Insert
/// - Delete
/// - Restore
///
/// A Op may have multiple atomic operations, since Op can be merged.
pub struct Op {
id: ID,
content: OpContent,
@ -59,24 +68,16 @@ impl Mergable for Op {
} => container == &other_container && content.is_mergable_content(&**other_content),
_ => false,
},
OpContent::Delete { target, lamport } => match other.content {
OpContent::Delete { target } => match other.content {
OpContent::Delete {
target: ref other_target,
lamport: ref other_lamport,
} => {
lamport + target.len() == *other_lamport
&& target.is_mergable(other_target, cfg)
}
} => target.is_mergable(other_target, cfg),
_ => false,
},
OpContent::Restore { target, lamport } => match other.content {
OpContent::Restore { target } => match other.content {
OpContent::Restore {
target: ref other_target,
lamport: ref other_lamport,
} => {
lamport + target.len() == *other_lamport
&& target.is_mergable(other_target, cfg)
}
} => target.is_mergable(other_target, cfg),
_ => false,
},
}

View file

@ -12,11 +12,9 @@ pub enum OpContent {
},
Delete {
target: RleVec<IdSpan>,
lamport: usize,
},
Restore {
target: RleVec<IdSpan>,
lamport: usize,
},
}
@ -37,13 +35,11 @@ impl Clone for OpContent {
container: *container,
content: content.clone_content(),
},
OpContent::Delete { target, lamport } => OpContent::Delete {
OpContent::Delete { target } => OpContent::Delete {
target: target.clone(),
lamport: *lamport,
},
OpContent::Restore { target, lamport } => OpContent::Restore {
OpContent::Restore { target } => OpContent::Restore {
target: target.clone(),
lamport: *lamport,
},
}
}
@ -54,15 +50,13 @@ impl Sliceable for OpContent {
match self {
OpContent::Insert { container, content } => OpContent::Insert {
container: *container,
content: content.slice(from, to),
content: content.slice_content(from, to),
},
OpContent::Delete { target, lamport } => OpContent::Delete {
OpContent::Delete { target } => OpContent::Delete {
target: target.slice(from, to),
lamport: lamport + from,
},
OpContent::Restore { target, lamport } => OpContent::Restore {
OpContent::Restore { target } => OpContent::Restore {
target: target.slice(from, to),
lamport: lamport + from,
},
}
}

View file

@ -0,0 +1,30 @@
use crate::{Change, Lamport, Op, Timestamp, ID};
pub struct OpProxy<'a> {
change: &'a Change,
op: &'a Op,
/// offset of op in change
offset: u32,
}
impl<'a> OpProxy<'a> {
pub fn new(change: &'a Change, op: &'a Op) -> Self {
OpProxy {
change,
op,
offset: 0,
}
}
pub fn lamport(&self) -> Lamport {
self.change.lamport + self.offset
}
pub fn id(&self) -> ID {
self.op.id
}
pub fn timestamp(&self) -> Timestamp {
self.change.timestamp
}
}

View file

@ -1,9 +1,10 @@
use loro_core::LogStore;
use loro_core::{ClientID, LogStore};
use crate::raw_store::RawStore;
#[derive(Default)]
pub struct Loro {
pub this_client_id: ClientID,
pub raw_store: Option<RawStore>,
pub log_store: Option<LogStore>,
}

View file

@ -1,5 +1,5 @@
use loro_core::{content, ContentTypeID, InsertContent, ID};
use rle::{HasLength, Mergable};
use rle::{HasLength, Mergable, Sliceable};
#[derive(Debug, Clone)]
pub struct TextInsertContent {
@ -23,21 +23,17 @@ impl Mergable for TextInsertContent {
}
}
impl InsertContent for TextInsertContent {
fn id(&self) -> ContentTypeID {
ContentTypeID::Text
}
fn slice(&self, from: usize, to: usize) -> Box<dyn InsertContent> {
impl Sliceable for TextInsertContent {
fn slice(&self, from: usize, to: usize) -> Self {
if from == 0 {
Box::new(TextInsertContent {
TextInsertContent {
origin_left: self.origin_left,
origin_right: self.origin_right,
id: self.id,
text: self.text[..to].to_owned(),
})
}
} else {
Box::new(TextInsertContent {
TextInsertContent {
origin_left: ID {
client_id: self.id.client_id,
counter: self.id.counter + from as u32 - 1,
@ -48,12 +44,14 @@ impl InsertContent for TextInsertContent {
counter: self.id.counter + from as u32,
},
text: self.text[from..to].to_owned(),
})
}
}
}
}
fn clone_content(&self) -> Box<dyn InsertContent> {
Box::new(self.clone())
impl InsertContent for TextInsertContent {
fn id(&self) -> ContentTypeID {
ContentTypeID::Text
}
}