refactor: add inner content

This commit is contained in:
Zixuan Chen 2022-11-21 18:25:13 +08:00
parent 8dba7d1e3a
commit 7295e1c613
12 changed files with 81 additions and 60 deletions

View file

@ -19,7 +19,7 @@ use crate::{
}, },
context::Context, context::Context,
id::{Counter, ID}, id::{Counter, ID},
op::{Content, Op, RemoteOp, RichOp}, op::{Op, RemoteContent, RemoteOp, RichOp},
value::LoroValue, value::LoroValue,
version::IdSpanVector, version::IdSpanVector,
}; };
@ -54,7 +54,7 @@ impl ListContainer {
self.state.insert(pos, slice.clone().into()); self.state.insert(pos, slice.clone().into());
let op = Op::new( let op = Op::new(
id, id,
Content::List(ListOp::Insert { RemoteContent::List(ListOp::Insert {
slice: slice.into(), slice: slice.into(),
pos, pos,
}), }),
@ -76,7 +76,7 @@ impl ListContainer {
self.state.insert(pos, slice.clone().into()); self.state.insert(pos, slice.clone().into());
let op = Op::new( let op = Op::new(
id, id,
Content::List(ListOp::Insert { RemoteContent::List(ListOp::Insert {
slice: slice.into(), slice: slice.into(),
pos, pos,
}), }),
@ -101,7 +101,7 @@ impl ListContainer {
let id = store.next_id(); let id = store.next_id();
let op = Op::new( let op = Op::new(
id, id,
Content::List(ListOp::new_del(pos, len)), RemoteContent::List(ListOp::new_del(pos, len)),
store.get_or_create_container_idx(&self.id), store.get_or_create_container_idx(&self.id),
); );
@ -199,7 +199,7 @@ impl Container for ListContainer {
fn update_state_directly(&mut self, op: &RichOp) { fn update_state_directly(&mut self, op: &RichOp) {
match &op.get_sliced().content { match &op.get_sliced().content {
Content::List(op) => match op { RemoteContent::List(op) => match op {
ListOp::Insert { slice, pos } => { ListOp::Insert { slice, pos } => {
self.state.insert(*pos, slice.as_slice().unwrap().clone()) self.state.insert(*pos, slice.as_slice().unwrap().clone())
} }

View file

@ -3,7 +3,7 @@ use std::ops::Range;
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;
use rle::{HasLength, Mergable, Sliceable}; use rle::{HasLength, Mergable, Sliceable};
use crate::container::text::text_content::ListSlice; use crate::container::text::text_content::{ListSlice, SliceRange};
#[derive(EnumAsInner, Debug, Clone)] #[derive(EnumAsInner, Debug, Clone)]
pub enum ListOp { pub enum ListOp {
@ -11,6 +11,12 @@ pub enum ListOp {
Delete(DeleteSpan), Delete(DeleteSpan),
} }
#[derive(EnumAsInner, Debug, Clone)]
pub enum InnerListOp {
Insert { slice: SliceRange, pos: usize },
Delete(DeleteSpan),
}
/// `len` can be negative so that we can merge text deletions efficiently. /// `len` can be negative so that we can merge text deletions efficiently.
/// It looks like [crate::span::CounterSpan], but how should they merge ([Mergable] impl) and slice ([Sliceable] impl) are very different /// It looks like [crate::span::CounterSpan], but how should they merge ([Mergable] impl) and slice ([Sliceable] impl) are very different
/// ///

View file

@ -10,7 +10,7 @@ use crate::{
}, },
context::Context, context::Context,
op::RemoteOp, op::RemoteOp,
op::{Content, Op, RichOp}, op::{Op, RemoteContent, RichOp},
span::HasLamport, span::HasLamport,
value::LoroValue, value::LoroValue,
version::{IdSpanVector, TotalOrderStamp}, version::{IdSpanVector, TotalOrderStamp},
@ -70,7 +70,7 @@ impl MapContainer {
store.append_local_ops(&[Op { store.append_local_ops(&[Op {
counter: id.counter, counter: id.counter,
container, container,
content: Content::Map(MapSet { content: RemoteContent::Map(MapSet {
key: key.clone(), key: key.clone(),
value: value.clone(), value: value.clone(),
}), }),
@ -103,7 +103,7 @@ impl MapContainer {
store.append_local_ops(&[Op { store.append_local_ops(&[Op {
counter: id.counter, counter: id.counter,
container, container,
content: Content::Map(MapSet { content: RemoteContent::Map(MapSet {
key: key.clone(), key: key.clone(),
value: value.clone(), value: value.clone(),
}), }),

View file

@ -2,13 +2,18 @@ use rle::{HasLength, Mergable, Sliceable};
use crate::{ContentType, InsertContentTrait, InternalString, LoroValue}; use crate::{ContentType, InsertContentTrait, InternalString, LoroValue};
// TODO: use imported and exported format to save the space
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct MapSet { pub struct MapSet {
pub(crate) key: InternalString, pub(crate) key: InternalString,
pub(crate) value: LoroValue, pub(crate) value: LoroValue,
} }
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InnerMapSet {
pub(crate) key: InternalString,
pub(crate) value: usize,
}
impl Mergable for MapSet {} impl Mergable for MapSet {}
impl Sliceable for MapSet { impl Sliceable for MapSet {
fn slice(&self, from: usize, to: usize) -> Self { fn slice(&self, from: usize, to: usize) -> Self {

View file

@ -4,4 +4,4 @@ mod tests;
pub use map_container::Map; pub use map_container::Map;
pub(crate) use map_container::MapContainer; pub(crate) use map_container::MapContainer;
pub(crate) use map_content::MapSet; pub(crate) use map_content::{InnerMapSet, MapSet};

View file

@ -14,7 +14,7 @@ use crate::{
context::Context, context::Context,
debug_log, debug_log,
id::{Counter, ID}, id::{Counter, ID},
op::{Content, Op, RemoteOp, RichOp}, op::{Op, RemoteContent, RemoteOp, RichOp},
value::LoroValue, value::LoroValue,
version::IdSpanVector, version::IdSpanVector,
}; };
@ -59,7 +59,7 @@ impl TextContainer {
self.state.insert(pos, slice.clone().into()); self.state.insert(pos, slice.clone().into());
let op = Op::new( let op = Op::new(
id, id,
Content::List(ListOp::Insert { RemoteContent::List(ListOp::Insert {
slice: slice.into(), slice: slice.into(),
pos, pos,
}), }),
@ -84,7 +84,7 @@ impl TextContainer {
let id = store.next_id(); let id = store.next_id();
let op = Op::new( let op = Op::new(
id, id,
Content::List(ListOp::new_del(pos, len)), RemoteContent::List(ListOp::new_del(pos, len)),
store.get_or_create_container_idx(&self.id), store.get_or_create_container_idx(&self.id),
); );
@ -142,7 +142,7 @@ impl Container for TextContainer {
.update_aliveness(self.state.iter().map(|x| x.as_ref().0.clone())) .update_aliveness(self.state.iter().map(|x| x.as_ref().0.clone()))
} }
let mut contents: RleVec<[Content; 1]> = RleVec::new(); let mut contents: RleVec<[RemoteContent; 1]> = RleVec::new();
for content in op.contents.iter_mut() { for content in op.contents.iter_mut() {
if let Some((slice, pos)) = content.as_list_mut().and_then(|x| x.as_insert_mut()) { if let Some((slice, pos)) = content.as_list_mut().and_then(|x| x.as_insert_mut()) {
match slice { match slice {
@ -158,13 +158,13 @@ impl Container for TextContainer {
for span in self.raw_str.get_aliveness(&r.0) { for span in self.raw_str.get_aliveness(&r.0) {
match span { match span {
Alive::True(span) => { Alive::True(span) => {
contents.push(Content::List(ListOp::Insert { contents.push(RemoteContent::List(ListOp::Insert {
slice: ListSlice::RawStr(s[start..start + span].into()), slice: ListSlice::RawStr(s[start..start + span].into()),
pos: pos_start, pos: pos_start,
})); }));
} }
Alive::False(span) => { Alive::False(span) => {
let v = Content::List(ListOp::Insert { let v = RemoteContent::List(ListOp::Insert {
slice: ListSlice::Unknown(span), slice: ListSlice::Unknown(span),
pos: pos_start, pos: pos_start,
}); });
@ -177,14 +177,14 @@ impl Container for TextContainer {
} }
assert_eq!(start, r.atom_len()); assert_eq!(start, r.atom_len());
} else { } else {
contents.push(Content::List(ListOp::Insert { contents.push(RemoteContent::List(ListOp::Insert {
slice: ListSlice::RawStr(s), slice: ListSlice::RawStr(s),
pos: *pos, pos: *pos,
})); }));
} }
} }
this => { this => {
contents.push(Content::List(ListOp::Insert { contents.push(RemoteContent::List(ListOp::Insert {
slice: this.clone(), slice: this.clone(),
pos: *pos, pos: *pos,
})); }));
@ -220,7 +220,7 @@ impl Container for TextContainer {
fn update_state_directly(&mut self, op: &RichOp) { fn update_state_directly(&mut self, op: &RichOp) {
match &op.get_sliced().content { match &op.get_sliced().content {
Content::List(op) => match op { RemoteContent::List(op) => match op {
ListOp::Insert { slice, pos } => { ListOp::Insert { slice, pos } => {
let v = match slice { let v = match slice {
ListSlice::Slice(slice) => slice.clone(), ListSlice::Slice(slice) => slice.clone(),

View file

@ -5,7 +5,7 @@ use crate::{
container::{list::list_op::ListOp, text::tracker::yata_impl::YataImpl}, container::{list::list_op::ListOp, text::tracker::yata_impl::YataImpl},
debug_log, debug_log,
id::{Counter, ID}, id::{Counter, ID},
op::{Content, RichOp}, op::{RemoteContent, RichOp},
span::{HasId, HasIdSpan, IdSpan}, span::{HasId, HasIdSpan, IdSpan},
version::IdSpanVector, version::IdSpanVector,
VersionVector, VersionVector,
@ -303,7 +303,7 @@ impl Tracker {
} }
/// apply an operation directly to the current tracker /// apply an operation directly to the current tracker
fn apply(&mut self, id: ID, content: &Content) { fn apply(&mut self, id: ID, content: &RemoteContent) {
self.real_checkout(); self.real_checkout();
assert!(*self.current_vv.get(&id.client_id).unwrap_or(&0) <= id.counter); assert!(*self.current_vv.get(&id.client_id).unwrap_or(&0) <= id.counter);
assert!(*self.all_vv.get(&id.client_id).unwrap_or(&0) <= id.counter); assert!(*self.all_vv.get(&id.client_id).unwrap_or(&0) <= id.counter);

View file

@ -192,7 +192,7 @@ impl HasLength for YSpan {
#[cfg(any(test, features = "test_utils"))] #[cfg(any(test, features = "test_utils"))]
pub mod test { pub mod test {
use crate::{container::text::text_content::ListSlice, op::Content, ContentType, Op, ID}; use crate::{container::text::text_content::ListSlice, op::RemoteContent, ContentType, Op, ID};
use rle::{HasLength, RleVecWithIndex}; use rle::{HasLength, RleVecWithIndex};
use super::YSpan; use super::YSpan;
@ -202,7 +202,7 @@ pub mod test {
let mut vec: RleVecWithIndex<Op> = RleVecWithIndex::new(); let mut vec: RleVecWithIndex<Op> = RleVecWithIndex::new();
vec.push(Op::new( vec.push(Op::new(
ID::new(0, 1), ID::new(0, 1),
Content::Dyn(Box::new(YSpan { RemoteContent::Dyn(Box::new(YSpan {
origin_left: Some(ID::new(0, 0)), origin_left: Some(ID::new(0, 0)),
origin_right: None, origin_right: None,
id: ID::new(0, 1), id: ID::new(0, 1),
@ -213,7 +213,7 @@ pub mod test {
)); ));
vec.push(Op::new( vec.push(Op::new(
ID::new(0, 2), ID::new(0, 2),
Content::Dyn(Box::new(YSpan { RemoteContent::Dyn(Box::new(YSpan {
origin_left: Some(ID::new(0, 1)), origin_left: Some(ID::new(0, 1)),
origin_right: None, origin_right: None,
id: ID::new(0, 2), id: ID::new(0, 2),
@ -235,7 +235,7 @@ pub mod test {
let mut vec: RleVecWithIndex<Op> = RleVecWithIndex::new(); let mut vec: RleVecWithIndex<Op> = RleVecWithIndex::new();
vec.push(Op::new( vec.push(Op::new(
ID::new(0, 1), ID::new(0, 1),
Content::Dyn(Box::new(YSpan { RemoteContent::Dyn(Box::new(YSpan {
origin_left: Some(ID::new(0, 0)), origin_left: Some(ID::new(0, 0)),
origin_right: None, origin_right: None,
id: ID::new(0, 1), id: ID::new(0, 1),
@ -246,7 +246,7 @@ pub mod test {
)); ));
vec.push(Op::new( vec.push(Op::new(
ID::new(0, 2), ID::new(0, 2),
Content::Dyn(Box::new(YSpan { RemoteContent::Dyn(Box::new(YSpan {
origin_left: Some(ID::new(0, 0)), origin_left: Some(ID::new(0, 0)),
origin_right: Some(ID::new(0, 1)), origin_right: Some(ID::new(0, 1)),
id: ID::new(0, 5), id: ID::new(0, 5),

View file

@ -20,7 +20,7 @@ use crate::{
}, },
dag::remove_included_frontiers, dag::remove_included_frontiers,
id::{ClientID, ContainerIdx, Counter, ID}, id::{ClientID, ContainerIdx, Counter, ID},
op::{Content, Op, RemoteOp}, op::{Op, RemoteContent, RemoteOp},
smstring::SmString, smstring::SmString,
span::{HasIdSpan, HasLamportSpan}, span::{HasIdSpan, HasLamportSpan},
ContainerType, InternalString, LogStore, LoroValue, VersionVector, ContainerType, InternalString, LogStore, LoroValue, VersionVector,
@ -129,7 +129,7 @@ fn encode_changes(store: &LogStore) -> Encoded {
for (op, container) in remote_ops.into_iter().zip(containers.into_iter()) { for (op, container) in remote_ops.into_iter().zip(containers.into_iter()) {
for content in op.contents.into_iter() { for content in op.contents.into_iter() {
let (prop, gc, value) = match content { let (prop, gc, value) = match content {
crate::op::Content::Map(MapSet { key, value }) => ( crate::op::RemoteContent::Map(MapSet { key, value }) => (
*key_to_idx.entry(key.clone()).or_insert_with(|| { *key_to_idx.entry(key.clone()).or_insert_with(|| {
keys.push(key); keys.push(key);
keys.len() - 1 keys.len() - 1
@ -137,7 +137,7 @@ fn encode_changes(store: &LogStore) -> Encoded {
0, 0,
value, value,
), ),
crate::op::Content::List(list) => match list { crate::op::RemoteContent::List(list) => match list {
ListOp::Insert { slice, pos } => ( ListOp::Insert { slice, pos } => (
pos, pos,
match &slice { match &slice {
@ -155,7 +155,7 @@ fn encode_changes(store: &LogStore) -> Encoded {
(span.pos as usize, 0, LoroValue::I32(span.len as i32)) (span.pos as usize, 0, LoroValue::I32(span.len as i32))
} }
}, },
crate::op::Content::Dyn(_) => unreachable!(), crate::op::RemoteContent::Dyn(_) => unreachable!(),
}; };
op_len += 1; op_len += 1;
ops.push(OpEncoding { ops.push(OpEncoding {
@ -256,7 +256,7 @@ fn decode_changes(
let content = match container_type { let content = match container_type {
ContainerType::Map => { ContainerType::Map => {
let key = keys[prop].clone(); let key = keys[prop].clone();
Content::Map(MapSet { key, value }) RemoteContent::Map(MapSet { key, value })
} }
ContainerType::List | ContainerType::Text => { ContainerType::List | ContainerType::Text => {
let pos = prop; let pos = prop;
@ -278,7 +278,7 @@ fn decode_changes(
ListOp::Insert { slice, pos } ListOp::Insert { slice, pos }
} }
}; };
Content::List(list_op) RemoteContent::List(list_op)
} }
}; };

View file

@ -23,14 +23,14 @@ use smallvec::{smallvec, SmallVec};
pub struct Op { pub struct Op {
pub(crate) counter: Counter, pub(crate) counter: Counter,
pub(crate) container: ContainerIdx, pub(crate) container: ContainerIdx,
pub(crate) content: Content, pub(crate) content: RemoteContent,
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct RemoteOp { pub struct RemoteOp {
pub(crate) counter: Counter, pub(crate) counter: Counter,
pub(crate) container: ContainerID, pub(crate) container: ContainerID,
pub(crate) contents: RleVec<[Content; 1]>, pub(crate) contents: RleVec<[RemoteContent; 1]>,
} }
/// RichOp includes lamport and timestamp info, which is used for conflict resolution. /// RichOp includes lamport and timestamp info, which is used for conflict resolution.
@ -46,7 +46,7 @@ pub struct RichOp<'a> {
impl Op { impl Op {
#[inline] #[inline]
pub(crate) fn new(id: ID, content: Content, container: u32) -> Self { pub(crate) fn new(id: ID, content: RemoteContent, container: u32) -> Self {
Op { Op {
counter: id.counter, counter: id.counter,
content, content,
@ -104,7 +104,7 @@ impl HasLength for Op {
impl Sliceable for Op { impl Sliceable for Op {
fn slice(&self, from: usize, to: usize) -> Self { fn slice(&self, from: usize, to: usize) -> Self {
assert!(to > from); assert!(to > from);
let content: Content = self.content.slice(from, to); let content: RemoteContent = self.content.slice(from, to);
Op { Op {
counter: (self.counter + from as Counter), counter: (self.counter + from as Counter),
content, content,

View file

@ -3,7 +3,10 @@ use std::any::{Any, TypeId};
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;
use rle::{HasLength, Mergable, Sliceable}; use rle::{HasLength, Mergable, Sliceable};
use crate::container::{list::list_op::ListOp, map::MapSet}; use crate::container::{
list::list_op::{InnerListOp, ListOp},
map::{InnerMapSet, MapSet},
};
#[derive(PartialEq, Eq, Debug, Clone, Copy)] #[derive(PartialEq, Eq, Debug, Clone, Copy)]
pub enum ContentType { pub enum ContentType {
@ -16,13 +19,20 @@ pub enum ContentType {
} }
#[derive(EnumAsInner, Debug)] #[derive(EnumAsInner, Debug)]
pub enum Content { pub enum InnerContent {
Unknown(usize),
List(InnerListOp),
Map(InnerMapSet),
}
#[derive(EnumAsInner, Debug)]
pub enum RemoteContent {
Map(MapSet), Map(MapSet),
List(ListOp), List(ListOp),
Dyn(Box<dyn InsertContentTrait>), Dyn(Box<dyn InsertContentTrait>),
} }
impl Clone for Content { impl Clone for RemoteContent {
fn clone(&self) -> Self { fn clone(&self) -> Self {
match self { match self {
Self::Map(arg0) => Self::Map(arg0.clone()), Self::Map(arg0) => Self::Map(arg0.clone()),
@ -32,7 +42,7 @@ impl Clone for Content {
} }
} }
impl Content { impl RemoteContent {
pub fn id(&self) -> ContentType { pub fn id(&self) -> ContentType {
match self { match self {
Self::Map(_) => ContentType::Map, Self::Map(_) => ContentType::Map,
@ -90,35 +100,35 @@ impl<T: Mergable + Any> MergeableContent for T {
} }
} }
impl HasLength for Content { impl HasLength for RemoteContent {
fn content_len(&self) -> usize { fn content_len(&self) -> usize {
match self { match self {
Content::Map(x) => x.content_len(), RemoteContent::Map(x) => x.content_len(),
Content::Dyn(x) => x.content_len(), RemoteContent::Dyn(x) => x.content_len(),
Content::List(x) => x.content_len(), RemoteContent::List(x) => x.content_len(),
} }
} }
} }
impl Sliceable for Content { impl Sliceable for RemoteContent {
fn slice(&self, from: usize, to: usize) -> Self { fn slice(&self, from: usize, to: usize) -> Self {
match self { match self {
Content::Map(x) => Content::Map(x.slice(from, to)), RemoteContent::Map(x) => RemoteContent::Map(x.slice(from, to)),
Content::Dyn(x) => Content::Dyn(x.slice_content(from, to)), RemoteContent::Dyn(x) => RemoteContent::Dyn(x.slice_content(from, to)),
Content::List(x) => Content::List(x.slice(from, to)), RemoteContent::List(x) => RemoteContent::List(x.slice(from, to)),
} }
} }
} }
impl Mergable for Content { impl Mergable for RemoteContent {
fn is_mergable(&self, other: &Self, _conf: &()) -> bool fn is_mergable(&self, other: &Self, _conf: &()) -> bool
where where
Self: Sized, Self: Sized,
{ {
match (self, other) { match (self, other) {
(Content::Map(x), Content::Map(y)) => x.is_mergable(y, &()), (RemoteContent::Map(x), RemoteContent::Map(y)) => x.is_mergable(y, &()),
(Content::List(x), Content::List(y)) => x.is_mergable(y, &()), (RemoteContent::List(x), RemoteContent::List(y)) => x.is_mergable(y, &()),
(Content::Dyn(x), Content::Dyn(y)) => x.is_mergable_content(&**y), (RemoteContent::Dyn(x), RemoteContent::Dyn(y)) => x.is_mergable_content(&**y),
_ => false, _ => false,
} }
} }
@ -128,15 +138,15 @@ impl Mergable for Content {
Self: Sized, Self: Sized,
{ {
match self { match self {
Content::Map(x) => match _other { RemoteContent::Map(x) => match _other {
Content::Map(y) => x.merge(y, &()), RemoteContent::Map(y) => x.merge(y, &()),
_ => unreachable!(), _ => unreachable!(),
}, },
Content::List(x) => match _other { RemoteContent::List(x) => match _other {
Content::List(y) => x.merge(y, &()), RemoteContent::List(y) => x.merge(y, &()),
_ => unreachable!(), _ => unreachable!(),
}, },
Content::Dyn(x) => x.merge_content(&**_other.as_dyn().unwrap()), RemoteContent::Dyn(x) => x.merge_content(&**_other.as_dyn().unwrap()),
} }
} }
} }

View file

@ -18,7 +18,7 @@ fn size_of() {
use crate::{ use crate::{
container::{map::MapSet, text::text_content::ListSlice, ContainerID}, container::{map::MapSet, text::text_content::ListSlice, ContainerID},
id::ID, id::ID,
op::{Content, Op}, op::{Op, RemoteContent},
span::IdSpan, span::IdSpan,
Container, InternalString, Container, InternalString,
}; };
@ -28,7 +28,7 @@ fn size_of() {
println!("Change {}", std::mem::size_of::<Change>()); println!("Change {}", std::mem::size_of::<Change>());
println!("Op {}", std::mem::size_of::<Op>()); println!("Op {}", std::mem::size_of::<Op>());
println!("InsertContent {}", std::mem::size_of::<Content>()); println!("InsertContent {}", std::mem::size_of::<RemoteContent>());
println!("MapSet {}", std::mem::size_of::<MapSet>()); println!("MapSet {}", std::mem::size_of::<MapSet>());
println!("ListSlice {}", std::mem::size_of::<ListSlice>()); println!("ListSlice {}", std::mem::size_of::<ListSlice>());
println!("Box {}", std::mem::size_of::<Box<dyn Container>>()); println!("Box {}", std::mem::size_of::<Box<dyn Container>>());