diff --git a/crates/loro-core/src/container/list/list_container.rs b/crates/loro-core/src/container/list/list_container.rs index 30951f28..ca8b6c4f 100644 --- a/crates/loro-core/src/container/list/list_container.rs +++ b/crates/loro-core/src/container/list/list_container.rs @@ -19,7 +19,7 @@ use crate::{ }, context::Context, id::{Counter, ID}, - op::{Content, Op, RemoteOp, RichOp}, + op::{Op, RemoteContent, RemoteOp, RichOp}, value::LoroValue, version::IdSpanVector, }; @@ -54,7 +54,7 @@ impl ListContainer { self.state.insert(pos, slice.clone().into()); let op = Op::new( id, - Content::List(ListOp::Insert { + RemoteContent::List(ListOp::Insert { slice: slice.into(), pos, }), @@ -76,7 +76,7 @@ impl ListContainer { self.state.insert(pos, slice.clone().into()); let op = Op::new( id, - Content::List(ListOp::Insert { + RemoteContent::List(ListOp::Insert { slice: slice.into(), pos, }), @@ -101,7 +101,7 @@ impl ListContainer { let id = store.next_id(); let op = Op::new( id, - Content::List(ListOp::new_del(pos, len)), + RemoteContent::List(ListOp::new_del(pos, len)), store.get_or_create_container_idx(&self.id), ); @@ -199,7 +199,7 @@ impl Container for ListContainer { fn update_state_directly(&mut self, op: &RichOp) { match &op.get_sliced().content { - Content::List(op) => match op { + RemoteContent::List(op) => match op { ListOp::Insert { slice, pos } => { self.state.insert(*pos, slice.as_slice().unwrap().clone()) } diff --git a/crates/loro-core/src/container/list/list_op.rs b/crates/loro-core/src/container/list/list_op.rs index 4fbd7f98..1bf917dd 100644 --- a/crates/loro-core/src/container/list/list_op.rs +++ b/crates/loro-core/src/container/list/list_op.rs @@ -3,7 +3,7 @@ use std::ops::Range; use enum_as_inner::EnumAsInner; use rle::{HasLength, Mergable, Sliceable}; -use crate::container::text::text_content::ListSlice; +use crate::container::text::text_content::{ListSlice, SliceRange}; #[derive(EnumAsInner, Debug, Clone)] pub enum ListOp { @@ -11,6 +11,12 @@ pub enum ListOp { 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. /// It looks like [crate::span::CounterSpan], but how should they merge ([Mergable] impl) and slice ([Sliceable] impl) are very different /// diff --git a/crates/loro-core/src/container/map/map_container.rs b/crates/loro-core/src/container/map/map_container.rs index d808f7c5..45dc5601 100644 --- a/crates/loro-core/src/container/map/map_container.rs +++ b/crates/loro-core/src/container/map/map_container.rs @@ -10,7 +10,7 @@ use crate::{ }, context::Context, op::RemoteOp, - op::{Content, Op, RichOp}, + op::{Op, RemoteContent, RichOp}, span::HasLamport, value::LoroValue, version::{IdSpanVector, TotalOrderStamp}, @@ -70,7 +70,7 @@ impl MapContainer { store.append_local_ops(&[Op { counter: id.counter, container, - content: Content::Map(MapSet { + content: RemoteContent::Map(MapSet { key: key.clone(), value: value.clone(), }), @@ -103,7 +103,7 @@ impl MapContainer { store.append_local_ops(&[Op { counter: id.counter, container, - content: Content::Map(MapSet { + content: RemoteContent::Map(MapSet { key: key.clone(), value: value.clone(), }), diff --git a/crates/loro-core/src/container/map/map_content.rs b/crates/loro-core/src/container/map/map_content.rs index ff2aa5da..e6e63423 100644 --- a/crates/loro-core/src/container/map/map_content.rs +++ b/crates/loro-core/src/container/map/map_content.rs @@ -2,13 +2,18 @@ use rle::{HasLength, Mergable, Sliceable}; use crate::{ContentType, InsertContentTrait, InternalString, LoroValue}; -// TODO: use imported and exported format to save the space #[derive(Clone, Debug, PartialEq)] pub struct MapSet { pub(crate) key: InternalString, 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 Sliceable for MapSet { fn slice(&self, from: usize, to: usize) -> Self { diff --git a/crates/loro-core/src/container/map/mod.rs b/crates/loro-core/src/container/map/mod.rs index 28d003dd..b4e99065 100644 --- a/crates/loro-core/src/container/map/mod.rs +++ b/crates/loro-core/src/container/map/mod.rs @@ -4,4 +4,4 @@ mod tests; pub use map_container::Map; pub(crate) use map_container::MapContainer; -pub(crate) use map_content::MapSet; +pub(crate) use map_content::{InnerMapSet, MapSet}; diff --git a/crates/loro-core/src/container/text/text_container.rs b/crates/loro-core/src/container/text/text_container.rs index 512eb786..76909493 100644 --- a/crates/loro-core/src/container/text/text_container.rs +++ b/crates/loro-core/src/container/text/text_container.rs @@ -14,7 +14,7 @@ use crate::{ context::Context, debug_log, id::{Counter, ID}, - op::{Content, Op, RemoteOp, RichOp}, + op::{Op, RemoteContent, RemoteOp, RichOp}, value::LoroValue, version::IdSpanVector, }; @@ -59,7 +59,7 @@ impl TextContainer { self.state.insert(pos, slice.clone().into()); let op = Op::new( id, - Content::List(ListOp::Insert { + RemoteContent::List(ListOp::Insert { slice: slice.into(), pos, }), @@ -84,7 +84,7 @@ impl TextContainer { let id = store.next_id(); let op = Op::new( id, - Content::List(ListOp::new_del(pos, len)), + RemoteContent::List(ListOp::new_del(pos, len)), 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())) } - let mut contents: RleVec<[Content; 1]> = RleVec::new(); + let mut contents: RleVec<[RemoteContent; 1]> = RleVec::new(); for content in op.contents.iter_mut() { if let Some((slice, pos)) = content.as_list_mut().and_then(|x| x.as_insert_mut()) { match slice { @@ -158,13 +158,13 @@ impl Container for TextContainer { for span in self.raw_str.get_aliveness(&r.0) { match span { Alive::True(span) => { - contents.push(Content::List(ListOp::Insert { + contents.push(RemoteContent::List(ListOp::Insert { slice: ListSlice::RawStr(s[start..start + span].into()), pos: pos_start, })); } Alive::False(span) => { - let v = Content::List(ListOp::Insert { + let v = RemoteContent::List(ListOp::Insert { slice: ListSlice::Unknown(span), pos: pos_start, }); @@ -177,14 +177,14 @@ impl Container for TextContainer { } assert_eq!(start, r.atom_len()); } else { - contents.push(Content::List(ListOp::Insert { + contents.push(RemoteContent::List(ListOp::Insert { slice: ListSlice::RawStr(s), pos: *pos, })); } } this => { - contents.push(Content::List(ListOp::Insert { + contents.push(RemoteContent::List(ListOp::Insert { slice: this.clone(), pos: *pos, })); @@ -220,7 +220,7 @@ impl Container for TextContainer { fn update_state_directly(&mut self, op: &RichOp) { match &op.get_sliced().content { - Content::List(op) => match op { + RemoteContent::List(op) => match op { ListOp::Insert { slice, pos } => { let v = match slice { ListSlice::Slice(slice) => slice.clone(), diff --git a/crates/loro-core/src/container/text/tracker.rs b/crates/loro-core/src/container/text/tracker.rs index a3be11a7..a3977a0d 100644 --- a/crates/loro-core/src/container/text/tracker.rs +++ b/crates/loro-core/src/container/text/tracker.rs @@ -5,7 +5,7 @@ use crate::{ container::{list::list_op::ListOp, text::tracker::yata_impl::YataImpl}, debug_log, id::{Counter, ID}, - op::{Content, RichOp}, + op::{RemoteContent, RichOp}, span::{HasId, HasIdSpan, IdSpan}, version::IdSpanVector, VersionVector, @@ -303,7 +303,7 @@ impl 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(); 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); diff --git a/crates/loro-core/src/container/text/tracker/y_span.rs b/crates/loro-core/src/container/text/tracker/y_span.rs index b69353be..15a45c79 100644 --- a/crates/loro-core/src/container/text/tracker/y_span.rs +++ b/crates/loro-core/src/container/text/tracker/y_span.rs @@ -192,7 +192,7 @@ impl HasLength for YSpan { #[cfg(any(test, features = "test_utils"))] 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 super::YSpan; @@ -202,7 +202,7 @@ pub mod test { let mut vec: RleVecWithIndex = RleVecWithIndex::new(); vec.push(Op::new( ID::new(0, 1), - Content::Dyn(Box::new(YSpan { + RemoteContent::Dyn(Box::new(YSpan { origin_left: Some(ID::new(0, 0)), origin_right: None, id: ID::new(0, 1), @@ -213,7 +213,7 @@ pub mod test { )); vec.push(Op::new( ID::new(0, 2), - Content::Dyn(Box::new(YSpan { + RemoteContent::Dyn(Box::new(YSpan { origin_left: Some(ID::new(0, 1)), origin_right: None, id: ID::new(0, 2), @@ -235,7 +235,7 @@ pub mod test { let mut vec: RleVecWithIndex = RleVecWithIndex::new(); vec.push(Op::new( ID::new(0, 1), - Content::Dyn(Box::new(YSpan { + RemoteContent::Dyn(Box::new(YSpan { origin_left: Some(ID::new(0, 0)), origin_right: None, id: ID::new(0, 1), @@ -246,7 +246,7 @@ pub mod test { )); vec.push(Op::new( ID::new(0, 2), - Content::Dyn(Box::new(YSpan { + RemoteContent::Dyn(Box::new(YSpan { origin_left: Some(ID::new(0, 0)), origin_right: Some(ID::new(0, 1)), id: ID::new(0, 5), diff --git a/crates/loro-core/src/log_store/encoding.rs b/crates/loro-core/src/log_store/encoding.rs index f0b2529e..835b0060 100644 --- a/crates/loro-core/src/log_store/encoding.rs +++ b/crates/loro-core/src/log_store/encoding.rs @@ -20,7 +20,7 @@ use crate::{ }, dag::remove_included_frontiers, id::{ClientID, ContainerIdx, Counter, ID}, - op::{Content, Op, RemoteOp}, + op::{Op, RemoteContent, RemoteOp}, smstring::SmString, span::{HasIdSpan, HasLamportSpan}, 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 content in op.contents.into_iter() { 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(|| { keys.push(key); keys.len() - 1 @@ -137,7 +137,7 @@ fn encode_changes(store: &LogStore) -> Encoded { 0, value, ), - crate::op::Content::List(list) => match list { + crate::op::RemoteContent::List(list) => match list { ListOp::Insert { slice, pos } => ( pos, match &slice { @@ -155,7 +155,7 @@ fn encode_changes(store: &LogStore) -> Encoded { (span.pos as usize, 0, LoroValue::I32(span.len as i32)) } }, - crate::op::Content::Dyn(_) => unreachable!(), + crate::op::RemoteContent::Dyn(_) => unreachable!(), }; op_len += 1; ops.push(OpEncoding { @@ -256,7 +256,7 @@ fn decode_changes( let content = match container_type { ContainerType::Map => { let key = keys[prop].clone(); - Content::Map(MapSet { key, value }) + RemoteContent::Map(MapSet { key, value }) } ContainerType::List | ContainerType::Text => { let pos = prop; @@ -278,7 +278,7 @@ fn decode_changes( ListOp::Insert { slice, pos } } }; - Content::List(list_op) + RemoteContent::List(list_op) } }; diff --git a/crates/loro-core/src/op.rs b/crates/loro-core/src/op.rs index 3848c93d..7c2ce7bc 100644 --- a/crates/loro-core/src/op.rs +++ b/crates/loro-core/src/op.rs @@ -23,14 +23,14 @@ use smallvec::{smallvec, SmallVec}; pub struct Op { pub(crate) counter: Counter, pub(crate) container: ContainerIdx, - pub(crate) content: Content, + pub(crate) content: RemoteContent, } #[derive(Debug, Clone)] pub struct RemoteOp { pub(crate) counter: Counter, 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. @@ -46,7 +46,7 @@ pub struct RichOp<'a> { impl Op { #[inline] - pub(crate) fn new(id: ID, content: Content, container: u32) -> Self { + pub(crate) fn new(id: ID, content: RemoteContent, container: u32) -> Self { Op { counter: id.counter, content, @@ -104,7 +104,7 @@ impl HasLength for Op { impl Sliceable for Op { fn slice(&self, from: usize, to: usize) -> Self { assert!(to > from); - let content: Content = self.content.slice(from, to); + let content: RemoteContent = self.content.slice(from, to); Op { counter: (self.counter + from as Counter), content, diff --git a/crates/loro-core/src/op/content.rs b/crates/loro-core/src/op/content.rs index a41581f3..53c6374e 100644 --- a/crates/loro-core/src/op/content.rs +++ b/crates/loro-core/src/op/content.rs @@ -3,7 +3,10 @@ use std::any::{Any, TypeId}; use enum_as_inner::EnumAsInner; 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)] pub enum ContentType { @@ -16,13 +19,20 @@ pub enum ContentType { } #[derive(EnumAsInner, Debug)] -pub enum Content { +pub enum InnerContent { + Unknown(usize), + List(InnerListOp), + Map(InnerMapSet), +} + +#[derive(EnumAsInner, Debug)] +pub enum RemoteContent { Map(MapSet), List(ListOp), Dyn(Box), } -impl Clone for Content { +impl Clone for RemoteContent { fn clone(&self) -> Self { match self { Self::Map(arg0) => Self::Map(arg0.clone()), @@ -32,7 +42,7 @@ impl Clone for Content { } } -impl Content { +impl RemoteContent { pub fn id(&self) -> ContentType { match self { Self::Map(_) => ContentType::Map, @@ -90,35 +100,35 @@ impl MergeableContent for T { } } -impl HasLength for Content { +impl HasLength for RemoteContent { fn content_len(&self) -> usize { match self { - Content::Map(x) => x.content_len(), - Content::Dyn(x) => x.content_len(), - Content::List(x) => x.content_len(), + RemoteContent::Map(x) => x.content_len(), + RemoteContent::Dyn(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 { match self { - Content::Map(x) => Content::Map(x.slice(from, to)), - Content::Dyn(x) => Content::Dyn(x.slice_content(from, to)), - Content::List(x) => Content::List(x.slice(from, to)), + RemoteContent::Map(x) => RemoteContent::Map(x.slice(from, to)), + RemoteContent::Dyn(x) => RemoteContent::Dyn(x.slice_content(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 where Self: Sized, { match (self, other) { - (Content::Map(x), Content::Map(y)) => x.is_mergable(y, &()), - (Content::List(x), Content::List(y)) => x.is_mergable(y, &()), - (Content::Dyn(x), Content::Dyn(y)) => x.is_mergable_content(&**y), + (RemoteContent::Map(x), RemoteContent::Map(y)) => x.is_mergable(y, &()), + (RemoteContent::List(x), RemoteContent::List(y)) => x.is_mergable(y, &()), + (RemoteContent::Dyn(x), RemoteContent::Dyn(y)) => x.is_mergable_content(&**y), _ => false, } } @@ -128,15 +138,15 @@ impl Mergable for Content { Self: Sized, { match self { - Content::Map(x) => match _other { - Content::Map(y) => x.merge(y, &()), + RemoteContent::Map(x) => match _other { + RemoteContent::Map(y) => x.merge(y, &()), _ => unreachable!(), }, - Content::List(x) => match _other { - Content::List(y) => x.merge(y, &()), + RemoteContent::List(x) => match _other { + RemoteContent::List(y) => x.merge(y, &()), _ => unreachable!(), }, - Content::Dyn(x) => x.merge_content(&**_other.as_dyn().unwrap()), + RemoteContent::Dyn(x) => x.merge_content(&**_other.as_dyn().unwrap()), } } } diff --git a/crates/loro-core/src/tests/mod.rs b/crates/loro-core/src/tests/mod.rs index 42c9b67e..f8123c1f 100644 --- a/crates/loro-core/src/tests/mod.rs +++ b/crates/loro-core/src/tests/mod.rs @@ -18,7 +18,7 @@ fn size_of() { use crate::{ container::{map::MapSet, text::text_content::ListSlice, ContainerID}, id::ID, - op::{Content, Op}, + op::{Op, RemoteContent}, span::IdSpan, Container, InternalString, }; @@ -28,7 +28,7 @@ fn size_of() { println!("Change {}", std::mem::size_of::()); println!("Op {}", std::mem::size_of::()); - println!("InsertContent {}", std::mem::size_of::()); + println!("InsertContent {}", std::mem::size_of::()); println!("MapSet {}", std::mem::size_of::()); println!("ListSlice {}", std::mem::size_of::()); println!("Box {}", std::mem::size_of::>());