mirror of
https://github.com/loro-dev/loro.git
synced 2025-02-02 02:59:51 +00:00
refactor: add container type & twist many things
This commit is contained in:
parent
ad7a1c54ce
commit
268f3c1e96
9 changed files with 114 additions and 40 deletions
|
@ -9,3 +9,4 @@ edition = "2021"
|
|||
string_cache = "0.8.3"
|
||||
rle = {path = "../rle"}
|
||||
smallvec = "1.8.0"
|
||||
smartstring = "1.0.1"
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
48
crates/loro-core/src/container.rs
Normal file
48
crates/loro-core/src/container.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#![allow(dead_code, unused_imports)]
|
||||
#![feature(trait_upcasting)]
|
||||
|
||||
mod change;
|
||||
mod container;
|
||||
mod id;
|
||||
mod id_span;
|
||||
mod log_store;
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
30
crates/loro-core/src/op/op_proxy.rs
Normal file
30
crates/loro-core/src/op/op_proxy.rs
Normal 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
|
||||
}
|
||||
}
|
|
@ -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>,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue