fix: encode when only create container but no op

This commit is contained in:
leeeon233 2022-11-16 15:35:08 +08:00
parent 33edd89e6e
commit f468e3b57b
10 changed files with 122 additions and 18 deletions

1
Cargo.lock generated
View file

@ -302,6 +302,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"columnar_derive", "columnar_derive",
"flate2",
"itertools", "itertools",
"lazy_static", "lazy_static",
"postcard", "postcard",

View file

@ -1,10 +1,6 @@
use std::{ use std::{io::Read, time::Instant};
io::{Read, Write},
time::Instant,
};
use flate2::write::GzEncoder; use flate2::read::GzDecoder;
use flate2::{read::GzDecoder, Compression};
use loro_core::{configure::Configure, container::registry::ContainerWrapper, LoroCore}; use loro_core::{configure::Configure, container::registry::ContainerWrapper, LoroCore};
use serde_json::Value; use serde_json::Value;
const RAW_DATA: &[u8; 901823] = include_bytes!("../benches/automerge-paper.json.gz"); const RAW_DATA: &[u8; 901823] = include_bytes!("../benches/automerge-paper.json.gz");
@ -64,8 +60,12 @@ fn main() {
} }
println!("Longest continuous bytes length {}", max_count); println!("Longest continuous bytes length {}", max_count);
let mut e = GzEncoder::new(Vec::new(), Compression::new(6)); use columnar::{compress, CompressConfig};
e.write_all(&buf).unwrap(); let s = Instant::now();
let result = e.finish().unwrap(); let result = compress(&buf, &CompressConfig::default()).unwrap();
println!("GZipped Size {}", result.len()); println!(
"GZipped Size {} time: {}ms",
result.len(),
s.elapsed().as_millis()
);
} }

View file

@ -8,6 +8,12 @@ version = "0.11.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]] [[package]]
name = "aho-corasick" name = "aho-corasick"
version = "0.7.19" version = "0.7.19"
@ -177,6 +183,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"bincode", "bincode",
"columnar_derive", "columnar_derive",
"flate2",
"itertools", "itertools",
"lazy_static", "lazy_static",
"postcard", "postcard",
@ -206,6 +213,15 @@ dependencies = [
"volatile-register", "volatile-register",
] ]
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]] [[package]]
name = "crdt-list" name = "crdt-list"
version = "0.3.0" version = "0.3.0"
@ -303,6 +319,16 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "flate2"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f82b0f4c27ad9f8bfd1f3208d882da2b09c301bc1c828fd3a00d0216d2fbbff6"
dependencies = [
"crc32fast",
"miniz_oxide",
]
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -504,6 +530,15 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
[[package]]
name = "miniz_oxide"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96590ba8f175222643a85693f33d26e9c8a015f599c216509b1a6894af675d34"
dependencies = [
"adler",
]
[[package]] [[package]]
name = "nb" name = "nb"
version = "0.1.3" version = "0.1.3"

View file

@ -53,3 +53,9 @@ name = "recursive"
path = "fuzz_targets/recursive.rs" path = "fuzz_targets/recursive.rs"
test = false test = false
doc = false doc = false
[[bin]]
name = "encode"
path = "fuzz_targets/encode.rs"
test = false
doc = false

View file

@ -0,0 +1,8 @@
#![no_main]
use libfuzzer_sys::fuzz_target;
use loro_core::fuzz::{test_single_client_encode, Action};
fuzz_target!(|data: Vec<Action>| {
// fuzzed code goes here
test_single_client_encode(data)
});

View file

@ -411,7 +411,7 @@ impl Container for ListContainer {
values.into() values.into()
} }
fn to_export(&mut self, op: &mut RemoteOp, gc: bool) { fn to_export(&mut self, op: &mut RemoteOp, _gc: bool) {
for content in op.contents.iter_mut() { for content in op.contents.iter_mut() {
if let Some((slice, _pos)) = content if let Some((slice, _pos)) = content
.as_normal_mut() .as_normal_mut()

View file

@ -189,7 +189,7 @@ impl Container for MapContainer {
todo!() todo!()
} }
fn to_export(&mut self, _op: &mut RemoteOp, gc: bool) {} fn to_export(&mut self, _op: &mut RemoteOp, _gc: bool) {}
fn to_import(&mut self, _op: &mut RemoteOp) {} fn to_import(&mut self, _op: &mut RemoteOp) {}
} }

View file

@ -265,6 +265,40 @@ pub fn test_single_client(mut actions: Vec<Action>) {
} }
} }
pub fn test_single_client_encode(mut actions: Vec<Action>) {
let mut store = LoroCore::new(Default::default(), None);
let mut text_container = store.get_text("hello");
let mut ground_truth = String::new();
let mut applied = Vec::new();
for action in actions
.iter_mut()
.filter(|x| x.as_del().is_some() || x.as_ins().is_some())
{
ground_truth.preprocess(action);
applied.push(action.clone());
// println!("{}", (&applied).table());
ground_truth.apply_action(action);
match action {
Action::Ins { content, pos, .. } => {
text_container.insert(&store, *pos, &content.to_string());
}
Action::Del { pos, len, .. } => {
if text_container.text_len() == 0 {
return;
}
text_container.delete(&store, *pos, *len);
}
_ => {}
}
}
let encode_bytes = store.encode_snapshot();
let store2 =
LoroCore::decode_snapshot(&encode_bytes, None, crate::configure::Configure::default());
let encode_bytes2 = store2.encode_snapshot();
assert_eq!(encode_bytes, encode_bytes2);
}
pub fn minify_error<T, F, N>(site_num: u8, actions: Vec<T>, f: F, normalize: N) pub fn minify_error<T, F, N>(site_num: u8, actions: Vec<T>, f: F, normalize: N)
where where
F: Fn(u8, &mut [T]), F: Fn(u8, &mut [T]),
@ -616,4 +650,13 @@ mod test {
fn mini() { fn mini() {
minify_error(8, vec![], test_multi_sites, normalize) minify_error(8, vec![], test_multi_sites, normalize)
} }
#[test]
fn case_encode() {
test_single_client_encode(vec![Ins {
content: 49087,
pos: 4631600097073807295,
site: 191,
}])
}
} }

View file

@ -1,4 +1,4 @@
use std::{collections::HashSet, fmt::Debug, panic::UnwindSafe, time::Instant}; use std::{collections::HashSet, fmt::Debug};
use arbitrary::Arbitrary; use arbitrary::Arbitrary;
use enum_as_inner::EnumAsInner; use enum_as_inner::EnumAsInner;

View file

@ -51,9 +51,11 @@ struct OpEncoding {
#[columnar(strategy = "Rle", original_type = "u32")] #[columnar(strategy = "Rle", original_type = "u32")]
container: ContainerIdx, container: ContainerIdx,
/// key index or insert/delete pos /// key index or insert/delete pos
prop: usize, #[columnar(strategy = "DeltaRle")]
prop: usize, // 18225 bytes
// TODO: can be compressed // TODO: can be compressed
gc: usize, gc: usize,
// #[columnar(compress(level = 0))]
value: LoroValue, value: LoroValue,
} }
@ -85,7 +87,6 @@ struct Encoded {
#[columnar(type = "vec")] #[columnar(type = "vec")]
deps: Vec<DepsEncoding>, deps: Vec<DepsEncoding>,
clients: Clients, clients: Clients,
// TODO: can be compressed
containers: Containers, containers: Containers,
keys: Vec<InternalString>, keys: Vec<InternalString>,
} }
@ -206,13 +207,25 @@ fn decode_changes(
} = encoded; } = encoded;
if change_encodings.is_empty() { if change_encodings.is_empty() {
return LogStore::new(cfg, None); let store = LogStore::new(cfg, None);
if !containers.is_empty() {
let mut s = store.write().unwrap();
for container in containers.iter() {
s.get_or_create_container(container);
}
drop(s);
}
return store;
} }
let mut container_reg = ContainerRegistry::new(); let mut container_reg = ContainerRegistry::new();
let mut op_iter = ops.into_iter(); let mut op_iter = ops.into_iter();
let mut changes = FxHashMap::default(); let mut changes = FxHashMap::default();
let mut deps_iter = deps.into_iter(); let mut deps_iter = deps.into_iter();
for container in containers.iter() {
container_reg.get_or_create(container);
}
for change_encoding in change_encodings { for change_encoding in change_encodings {
let ChangeEncoding { let ChangeEncoding {
client_idx, client_idx,
@ -242,8 +255,6 @@ fn decode_changes(
} = op; } = op;
let container_id = containers[container as usize].clone(); let container_id = containers[container as usize].clone();
container_reg.get_or_create(&container_id);
let container_type = container_id.container_type(); let container_type = container_id.container_type();
let content = match container_type { let content = match container_type {
ContainerType::Map => { ContainerType::Map => {