diff --git a/crates/loro-internal/examples/encoding.rs b/crates/loro-internal/examples/encoding.rs index bd428eed..de98b778 100644 --- a/crates/loro-internal/examples/encoding.rs +++ b/crates/loro-internal/examples/encoding.rs @@ -24,7 +24,7 @@ fn main() { let data = loro.export_from(&Default::default()); let start = Instant::now(); - for _ in 0..100 { + for _ in 0..10 { let mut b = LoroDoc::default(); b.detach(); b.import(&data).unwrap(); diff --git a/crates/loro-internal/src/container/list/list_op.rs b/crates/loro-internal/src/container/list/list_op.rs index df5aa85b..91a45c33 100644 --- a/crates/loro-internal/src/container/list/list_op.rs +++ b/crates/loro-internal/src/container/list/list_op.rs @@ -54,14 +54,6 @@ impl InnerListOp { }) } - pub fn new_unknown(pos: usize, len: usize) -> Self { - assert!(len != 0); - Self::Insert { - slice: SliceRange::new_unknown(len as u32), - pos, - } - } - pub fn new_insert(slice: Range, pos: usize) -> Self { Self::Insert { slice: SliceRange(slice), diff --git a/crates/loro-internal/src/container/text/text_content.rs b/crates/loro-internal/src/container/text/text_content.rs index 59dc879e..9bd368bc 100644 --- a/crates/loro-internal/src/container/text/text_content.rs +++ b/crates/loro-internal/src/container/text/text_content.rs @@ -136,7 +136,7 @@ impl<'a> Sliceable for ListSlice<'a> { } impl<'a> Mergable for ListSlice<'a> { - fn is_mergable(&self, other: &Self, _: &()) -> bool { + fn is_mergable(&self, _other: &Self, _: &()) -> bool { false } } diff --git a/crates/loro-internal/src/encoding/encode_enhanced.rs b/crates/loro-internal/src/encoding/encode_enhanced.rs index 536d17f4..29e8c099 100644 --- a/crates/loro-internal/src/encoding/encode_enhanced.rs +++ b/crates/loro-internal/src/encoding/encode_enhanced.rs @@ -70,11 +70,11 @@ struct OpEncoding { /// key index or insert/delete pos #[columnar(strategy = "DeltaRle")] prop: usize, - // the length of the deletion + #[columnar(strategy = "BoolRle")] + is_del: bool, + // the length of the deletion or insertion #[columnar(strategy = "Rle")] - del_len: isize, - #[columnar(strategy = "Rle")] - insert_len: usize, + insert_del_len: isize, } #[columnar(vec, ser, de, iterable)] @@ -183,7 +183,7 @@ pub fn encode_oplog_v2(oplog: &OpLog, vv: &VersionVector) -> Vec { let container_index = *container_idx2index.get(&container).unwrap(); let op = oplog.local_op_to_remote(op); for content in op.contents.into_iter() { - let (prop, del_len, insert_len) = match content { + let (prop, is_del, insert_del_len) = match content { crate::op::RawOpContent::Map(MapSet { key, value }) => { values.push(value.clone()); ( @@ -191,7 +191,7 @@ pub fn encode_oplog_v2(oplog: &OpLog, vv: &VersionVector) -> Vec { keys.push(key.clone()); keys.len() - 1 }), - -1, + false, 0, ) } @@ -210,19 +210,20 @@ pub fn encode_oplog_v2(oplog: &OpLog, vv: &VersionVector) -> Vec { string.push_str(str.deref()); } }; - (pos, -1, len) + assert!(len > 0); + (pos, false, len as isize) } ListOp::Delete(span) => { - assert!(span.len >= 0); - (span.pos as usize, span.len, 0) + // span.len maybe negative + (span.pos as usize, true, span.len) } }, }; op_len += 1; ops.push(OpEncoding { prop, - del_len, - insert_len, + is_del, + insert_del_len, container: container_index, }) } @@ -414,9 +415,9 @@ pub fn decode_oplog_v2(oplog: &mut OpLog, input: &[u8]) -> Result<(), LoroError> for op in op_iter.by_ref().take(op_len as usize) { let OpEncoding { container: container_idx, - insert_len, prop, - del_len, + is_del, + insert_del_len, } = op; let Some(container_id) = get_container(container_idx) else { @@ -433,14 +434,15 @@ pub fn decode_oplog_v2(oplog: &mut OpLog, input: &[u8]) -> Result<(), LoroError> } ContainerType::List | ContainerType::Text => { let pos = prop; - if del_len >= 0 { + if is_del { RawOpContent::List(ListOp::Delete(DeleteSpan { pos: pos as isize, - len: del_len, + len: insert_del_len, })) } else { match container_type { ContainerType::Text => { + let insert_len = insert_del_len as usize; let s = &str[str_index..str_index + insert_len]; str_index += insert_len; RawOpContent::List(ListOp::Insert { diff --git a/crates/loro-internal/src/snapshot_encode.rs b/crates/loro-internal/src/snapshot_encode.rs index d614a536..3f87bd03 100644 --- a/crates/loro-internal/src/snapshot_encode.rs +++ b/crates/loro-internal/src/snapshot_encode.rs @@ -118,11 +118,6 @@ pub fn decode_oplog( InnerContent::List(InnerListOp::new_del(pos, len)), container_idx, ), - SnapshotOp::TextOrListUnknown { len, pos } => Op::new( - id, - InnerContent::List(InnerListOp::new_unknown(pos, len)), - container_idx, - ), SnapshotOp::Map { .. } => { unreachable!() } @@ -322,11 +317,11 @@ struct EncodedSnapshotOp { // Map: always 0 #[columnar(strategy = "DeltaRle")] len: i64, - // List: insert 0 | unkonwn -2 | deletion -1 - // Text: insert 0 | unkonwn -2 | deletion -1 + // List: insert 0 | deletion -1 + // Text: insert 0 | deletion -1 // Map: always 0 - #[columnar(strategy = "Rle")] - kind: i64, + #[columnar(strategy = "BoolRle")] + is_del: bool, // Text: 0 // List: 0 | value index // Map: value index @@ -338,22 +333,16 @@ enum SnapshotOp { TextInsert { pos: usize, len: usize }, ListInsert { pos: usize, value_idx: u32 }, TextOrListDelete { pos: usize, len: isize }, - TextOrListUnknown { pos: usize, len: usize }, Map { key: usize, value_idx_plus_one: u32 }, } impl EncodedSnapshotOp { pub fn get_text(&self) -> SnapshotOp { - if self.kind == -1 { + if self.is_del { SnapshotOp::TextOrListDelete { pos: self.prop, len: self.len as isize, } - } else if self.kind == -2 { - SnapshotOp::TextOrListUnknown { - pos: self.prop, - len: self.len as usize, - } } else { SnapshotOp::TextInsert { pos: self.prop, @@ -363,16 +352,11 @@ impl EncodedSnapshotOp { } pub fn get_list(&self) -> SnapshotOp { - if self.kind == -1 { + if self.is_del { SnapshotOp::TextOrListDelete { pos: self.prop, len: self.len as isize, } - } else if self.kind == -2 { - SnapshotOp::TextOrListUnknown { - pos: self.prop, - len: self.len as usize, - } } else { SnapshotOp::ListInsert { pos: self.prop, @@ -397,21 +381,14 @@ impl EncodedSnapshotOp { container, prop: pos, len: 0, - kind: 0, + is_del: false, value: start as usize, }, SnapshotOp::TextOrListDelete { pos, len } => Self { container, prop: pos, len: len as i64, - kind: -1, - value: 0, - }, - SnapshotOp::TextOrListUnknown { pos, len } => Self { - container, - prop: pos, - len: len as i64, - kind: -2, + is_del: true, value: 0, }, SnapshotOp::Map { @@ -421,14 +398,14 @@ impl EncodedSnapshotOp { container, prop: key, len: 0, - kind: 0, + is_del: false, value: value as usize, }, SnapshotOp::TextInsert { pos, len } => Self { container, prop: pos, len: len as i64, - kind: 0, + is_del: false, value: 0, }, } @@ -644,51 +621,39 @@ fn encode_oplog(oplog: &OpLog, state_ref: Option) -> FinalPhase for op in change.ops.iter() { match &op.content { InnerContent::List(list) => match list { - InnerListOp::Insert { slice, pos } => { - if slice.is_unknown() { - encoded_ops.push(EncodedSnapshotOp::from( - SnapshotOp::TextOrListUnknown { - pos: *pos, - len: slice.atom_len(), - }, - op.container.to_index(), - )); - } else { - match op.container.get_type() { - loro_common::ContainerType::Text => { - let range = slice.0.start as usize..slice.0.end as usize; - let mut pos = *pos; - oplog.arena.with_text_slice(range, |slice| { - encoded_ops.push(record_str( - slice.as_bytes(), - pos, - op.container.to_index(), - )); + InnerListOp::Insert { slice, pos } => match op.container.get_type() { + loro_common::ContainerType::Text => { + let range = slice.0.start as usize..slice.0.end as usize; + let mut pos = *pos; + oplog.arena.with_text_slice(range, |slice| { + encoded_ops.push(record_str( + slice.as_bytes(), + pos, + op.container.to_index(), + )); - pos += slice.chars().count(); - }) - } - loro_common::ContainerType::List => { - let values = oplog - .arena - .get_values(slice.0.start as usize..slice.0.end as usize); - let mut pos = *pos; - for value in values { - let idx = record_value(&value); - encoded_ops.push(EncodedSnapshotOp::from( - SnapshotOp::ListInsert { - pos, - value_idx: idx as u32, - }, - op.container.to_index(), - )); - pos += 1; - } - } - loro_common::ContainerType::Map => unreachable!(), + pos += slice.chars().count(); + }) + } + loro_common::ContainerType::List => { + let values = oplog + .arena + .get_values(slice.0.start as usize..slice.0.end as usize); + let mut pos = *pos; + for value in values { + let idx = record_value(&value); + encoded_ops.push(EncodedSnapshotOp::from( + SnapshotOp::ListInsert { + pos, + value_idx: idx as u32, + }, + op.container.to_index(), + )); + pos += 1; } } - } + loro_common::ContainerType::Map => unreachable!(), + }, InnerListOp::Delete(del) => { encoded_ops.push(EncodedSnapshotOp::from( SnapshotOp::TextOrListDelete { diff --git a/crates/loro-internal/tests/history_compressed_rle_updates.dat b/crates/loro-internal/tests/history_compressed_rle_updates.dat index 8d41ec8e..ebe79528 100644 Binary files a/crates/loro-internal/tests/history_compressed_rle_updates.dat and b/crates/loro-internal/tests/history_compressed_rle_updates.dat differ diff --git a/crates/loro-internal/tests/history_snapshot.dat b/crates/loro-internal/tests/history_snapshot.dat index 598b750b..3a8a381c 100644 Binary files a/crates/loro-internal/tests/history_snapshot.dat and b/crates/loro-internal/tests/history_snapshot.dat differ diff --git a/crates/loro-internal/tests/test.rs b/crates/loro-internal/tests/test.rs index f877244e..faf2c62c 100644 --- a/crates/loro-internal/tests/test.rs +++ b/crates/loro-internal/tests/test.rs @@ -1,5 +1,3 @@ -use std::fs::File; - use loro_common::ID; use loro_internal::{version::Frontiers, LoroDoc, ToJson};