refactor: use list slice

This commit is contained in:
Zixuan Chen 2022-10-24 11:33:39 +08:00
parent 5a95a3c04d
commit bfd5e090d9
12 changed files with 92 additions and 37 deletions

View file

@ -39,4 +39,7 @@ ctor = "0.1.23"
doctest = false
[features]
fuzzing = ["crdt-list/fuzzing", "rand", "arbitrary", "tabled"]
# whether to use list slice instead of raw str in text container
slice = []
fuzzing = ["crdt-list/fuzzing", "slice", "rand", "arbitrary", "tabled"]
proptest = ["fuzzing"]

View file

@ -31,6 +31,7 @@ pub trait Container: Debug + Any + Unpin {
/// convert an op to export format. for example [ListSlice] should be convert to str before export
fn to_export(&self, op: &mut Op);
fn to_import(&mut self, op: &mut Op);
}
/// it's really cheap to clone

View file

@ -64,6 +64,14 @@ impl Container for ContainerInstance {
ContainerInstance::Dyn(x) => x.to_export(op),
}
}
fn to_import(&mut self, op: &mut crate::op::Op) {
match self {
ContainerInstance::Map(x) => x.to_import(op),
ContainerInstance::Text(x) => x.to_import(op),
ContainerInstance::Dyn(x) => x.to_import(op),
}
}
}
// TODO: containers snapshot: we need to resolve each container's parent even

View file

@ -156,4 +156,6 @@ impl Container for MapContainer {
}
fn to_export(&self, _op: &mut Op) {}
fn to_import(&mut self, op: &mut Op) {}
}

View file

@ -1,4 +1,3 @@
use rle::{RleTree, Sliceable};
use smallvec::{smallvec, SmallVec};
@ -62,7 +61,9 @@ impl TextContainer {
let id = if let Ok(mut store) = self.log_store.upgrade().unwrap().write() {
let id = store.next_id();
// let slice = ListSlice::from_range(self.raw_str.alloc(text));
#[cfg(feature = "slice")]
let slice = ListSlice::from_range(self.raw_str.alloc(text));
#[cfg(not(feature = "slice"))]
let slice = ListSlice::from_raw(SmString::from(text));
self.state.insert(pos, slice.clone());
let op = Op::new(
@ -253,15 +254,33 @@ impl Container for TextContainer {
.and_then(|c| c.as_list_mut())
.and_then(|x| x.as_insert_mut())
{
let change = if let ListSlice::Slice(ranges) = slice {
if let Some(change) = if let ListSlice::Slice(ranges) = slice {
Some(self.raw_str.get_str(ranges))
} else {
None
};
if let Some(change) = change {
} {
*slice = ListSlice::RawStr(change);
}
}
}
fn to_import(&mut self, op: &mut Op) {
if let Some((slice, _pos)) = op
.content
.as_normal_mut()
.and_then(|c| c.as_list_mut())
.and_then(|x| x.as_insert_mut())
{
if let Some(slice_range) = match slice {
ListSlice::RawStr(s) => {
let range = self.raw_str.alloc(s);
Some(range)
}
ListSlice::Slice(_) => unreachable!(),
ListSlice::Unknown(_) => unreachable!(),
} {
*slice = ListSlice::Slice(slice_range);
}
}
}
}

View file

@ -8,7 +8,7 @@ use crate::{
debug_log, LoroCore,
};
#[derive(Arbitrary, EnumAsInner, Clone, PartialEq, Eq, Debug)]
#[derive(arbitrary::Arbitrary, EnumAsInner, Clone, PartialEq, Eq, Debug)]
pub enum Action {
Ins {
content: String,
@ -307,6 +307,17 @@ mod test {
use super::Action::*;
use super::*;
#[test]
fn test_10() {
test_multi_sites(
10,
vec![Ins {
content: "\0\0".into(),
pos: 0,
site: 0,
}],
)
}
#[test]
fn test_9() {
test_multi_sites(
2,

View file

@ -17,7 +17,7 @@ use crate::{
change::{Change, ChangeMergeCfg},
configure::Configure,
container::{manager::ContainerManager, Container, ContainerID},
dag::{Dag, DagUtils},
dag::Dag,
debug_log,
id::{ClientID, Counter},
span::{HasIdSpan, IdSpan},
@ -143,6 +143,17 @@ impl LogStore {
}
}
fn change_to_imported_format(
&self,
container_manager: &mut ContainerManager,
change: &mut Change,
) {
for op in change.ops.vec_mut().iter_mut() {
let container = container_manager.get_or_create(&op.container, self.to_self.clone());
container.to_import(op);
}
}
fn change_to_export_format(&self, change: &mut Change) {
let container_manager = self.container.read().unwrap();
for op in change.ops.vec_mut().iter_mut() {
@ -251,14 +262,23 @@ impl LogStore {
}
// TODO: find a way to remove this clone? we don't need change in apply method actually
let change = self.push_change(change).clone();
let mut container_manager = self.container.write().unwrap();
#[cfg(feature = "slice")]
self.change_to_imported_format(&mut container_manager, &mut change);
let v = self
.changes
.entry(change.id.client_id)
.or_insert_with(RleVec::new);
v.push(change);
let change = v.vec().last().unwrap().clone();
// Apply ops.
// NOTE: applying expects that log_store has store the Change, and updated self vv
let mut set = FxHashSet::default();
for op in change.ops.iter() {
set.insert(&op.container);
}
for container in set {
let container = container_manager.get_or_create(container, self.to_self.clone());
container.apply(change.id_span(), self);
@ -277,16 +297,6 @@ impl LogStore {
}
}
#[inline]
fn push_change(&mut self, change: Change) -> &Change {
let v = self
.changes
.entry(change.id.client_id)
.or_insert_with(RleVec::new);
v.push(change);
v.vec().last().unwrap()
}
#[inline]
pub fn contains(&self, id: ID) -> bool {
self.changes

View file

@ -24,16 +24,16 @@ macro_rules! fx_map {
#[macro_export]
macro_rules! debug_log {
() => {
if cfg!(test) {
// $crate::print!("\n")
}
// if cfg!(test) {
// $crate::print!("\n")
// }
};
($($arg:tt)*) => {{
if cfg!(test) {
// print!("{}:{}\t", file!().purple(), line!().to_string().purple());
// println!($($arg)*);
}
// if cfg!(test) {
// print!("{}:{}\t", file!().purple(), line!().to_string().purple());
// println!($($arg)*);
// }
}};
}

View file

@ -1,11 +1,11 @@
#![cfg(test)]
#[cfg(feature = "fuzzing")]
#[cfg(feature = "proptest")]
pub const PROPTEST_FACTOR_10: usize = 10;
#[cfg(not(feature = "fuzzing"))]
#[cfg(not(feature = "proptest"))]
pub const PROPTEST_FACTOR_10: usize = 1;
#[cfg(feature = "fuzzing")]
#[cfg(feature = "proptest")]
pub const PROPTEST_FACTOR_1: usize = 1;
#[cfg(not(feature = "fuzzing"))]
#[cfg(not(feature = "proptest"))]
pub const PROPTEST_FACTOR_1: usize = 0;

View file

@ -25,3 +25,4 @@ static_assertions = "1.1.0"
[features]
fuzzing = []
proptest = ["fuzzing"]

View file

@ -1,11 +1,11 @@
#![cfg(test)]
#[cfg(feature = "fuzzing")]
#[cfg(feature = "proptest")]
pub const PROPTEST_FACTOR_10: usize = 10;
#[cfg(not(feature = "fuzzing"))]
#[cfg(not(feature = "proptest"))]
pub const PROPTEST_FACTOR_10: usize = 1;
#[cfg(feature = "fuzzing")]
#[cfg(feature = "proptest")]
pub const PROPTEST_FACTOR_1: usize = 1;
#[cfg(not(feature = "fuzzing"))]
#[cfg(not(feature = "proptest"))]
pub const PROPTEST_FACTOR_1: usize = 0;

View file

@ -5,7 +5,7 @@ test *FLAGS:
RUST_BACKTRACE=full cargo nextest run {{FLAGS}}
test-all:
cargo nextest run &
cargo nextest run --features=fuzzing &
just quickfuzz
quickfuzz: