mirror of
https://github.com/loro-dev/loro.git
synced 2025-01-23 13:39:12 +00:00
fix: encode when only create container but no op
This commit is contained in:
parent
33edd89e6e
commit
f468e3b57b
10 changed files with 122 additions and 18 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
35
crates/loro-core/fuzz/Cargo.lock
generated
35
crates/loro-core/fuzz/Cargo.lock
generated
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
8
crates/loro-core/fuzz/fuzz_targets/encode.rs
Normal file
8
crates/loro-core/fuzz/fuzz_targets/encode.rs
Normal 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)
|
||||||
|
});
|
|
@ -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()
|
||||||
|
|
|
@ -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) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
}])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
Loading…
Reference in a new issue