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,
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())
}

View file

@ -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
///

View file

@ -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(),
}),

View file

@ -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 {

View file

@ -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};

View file

@ -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(),

View file

@ -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);

View file

@ -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<Op> = 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<Op> = 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),

View file

@ -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)
}
};

View file

@ -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,

View file

@ -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<dyn InsertContentTrait>),
}
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<T: Mergable + Any> 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()),
}
}
}

View file

@ -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::<Change>());
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!("ListSlice {}", std::mem::size_of::<ListSlice>());
println!("Box {}", std::mem::size_of::<Box<dyn Container>>());