Skip to content

Commit

Permalink
feat: export any range version with json schema (#383)
Browse files Browse the repository at this point in the history
  • Loading branch information
Leeeon233 authored Jun 11, 2024
1 parent 881167b commit a941cdd
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 36 deletions.
6 changes: 4 additions & 2 deletions crates/fuzz/tests/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ fn unknown_json() {
let doc3_without_counter = loro_without_counter::LoroDoc::new();
// Test2: older version import newer version snapshot with counter
doc3_without_counter.import(&snapshot_with_counter).unwrap();
let unknown_json_from_snapshot = doc3_without_counter.export_json_updates(&Default::default());
let unknown_json_from_snapshot = doc3_without_counter
.export_json_updates(&Default::default(), &doc3_without_counter.oplog_vv());
// {
// "container": "cid:root-counter:Unknown(5)",
// "content": {
Expand All @@ -42,7 +43,8 @@ fn unknown_json() {
// "counter": 0
// }
// Test3: older version export json with binary unknown
let _json_with_binary_unknown = doc3_without_counter.export_json_updates(&Default::default());
let _json_with_binary_unknown = doc3_without_counter
.export_json_updates(&Default::default(), &doc3_without_counter.oplog_vv());
let new_doc = loro::LoroDoc::new();
// Test4: newer version import older version json with binary unknown
if new_doc
Expand Down
4 changes: 2 additions & 2 deletions crates/loro-internal/benches/encode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,12 @@ mod run {
b.bench_function("B4_encode_json_update", |b| {
ensure_ran();
b.iter(|| {
let _ = loro.export_json_updates(&Default::default());
let _ = loro.export_json_updates(&Default::default(), &loro.oplog_vv());
})
});
b.bench_function("B4_decode_json_update", |b| {
ensure_ran();
let json = loro.export_json_updates(&Default::default());
let json = loro.export_json_updates(&Default::default(), &loro.oplog_vv());
b.iter(|| {
let store2 = LoroDoc::default();
store2.import_json_updates(json.clone()).unwrap();
Expand Down
3 changes: 2 additions & 1 deletion crates/loro-internal/examples/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ fn main() {
);

let json_updates =
serde_json::to_string(&loro.export_json_updates(&Default::default())).unwrap();
serde_json::to_string(&loro.export_json_updates(&Default::default(), &loro.oplog_vv()))
.unwrap();
let output = miniz_oxide::deflate::compress_to_vec(json_updates.as_bytes(), 6);
println!(
"json updates size {} after compression {}",
Expand Down
3 changes: 2 additions & 1 deletion crates/loro-internal/examples/encoding_refactored.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ fn log_size() {
let snapshot = loro.export_snapshot();
let updates = loro.export_from(&Default::default());
let json_updates =
serde_json::to_string(&loro.export_json_updates(&Default::default())).unwrap();
serde_json::to_string(&loro.export_json_updates(&Default::default(), &loro.oplog_vv()))
.unwrap();
println!("\n");
println!("Snapshot size={}", snapshot.len());
println!("Updates size={}", updates.len());
Expand Down
83 changes: 62 additions & 21 deletions crates/loro-internal/src/encoding/json_schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,27 +22,34 @@ use op::{JsonOpContent, JsonSchema};

const SCHEMA_VERSION: u8 = 1;

pub(crate) fn export_json<'a, 'c: 'a>(oplog: &'c OpLog, vv: &VersionVector) -> JsonSchema {
let actual_start_vv: VersionVector = vv
.iter()
.filter_map(|(&peer, &end_counter)| {
if end_counter == 0 {
return None;
}

let this_end = oplog.vv().get(&peer).cloned().unwrap_or(0);
if this_end <= end_counter {
return Some((peer, this_end));
}
fn refine_vv(vv: &VersionVector, oplog: &OpLog) -> VersionVector {
let mut refined = VersionVector::new();
for (peer, counter) in vv.iter() {
if counter == &0 {
continue;
}
let end = oplog.vv().get(peer).copied().unwrap_or(0);
if end <= *counter {
refined.insert(*peer, end);
} else {
refined.insert(*peer, *counter);
}
}
refined
}

Some((peer, end_counter))
})
.collect();
pub(crate) fn export_json<'a, 'c: 'a>(
oplog: &'c OpLog,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> JsonSchema {
let actual_start_vv = refine_vv(start_vv, oplog);
let actual_end_vv = refine_vv(end_vv, oplog);

let frontiers = oplog.dag.vv_to_frontiers(&actual_start_vv);

let mut peer_register = ValueRegister::<PeerID>::new();
let diff_changes = init_encode(oplog, &actual_start_vv);
let diff_changes = init_encode(oplog, &actual_start_vv, &actual_end_vv);
let changes = encode_changes(&diff_changes, &oplog.arena, &mut peer_register);
JsonSchema {
changes,
Expand All @@ -62,15 +69,24 @@ pub(crate) fn import_json(oplog: &mut OpLog, json: JsonSchema) -> LoroResult<()>
Ok(())
}

fn init_encode<'s, 'a: 's>(oplog: &'a OpLog, vv: &'_ VersionVector) -> Vec<Cow<'s, Change>> {
let self_vv = oplog.vv();
let start_vv = vv.trim(oplog.vv());
fn init_encode<'s, 'a: 's>(
oplog: &'a OpLog,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> Vec<Cow<'s, Change>> {
let mut diff_changes: Vec<Cow<'a, Change>> = Vec::new();
for change in oplog.iter_changes_peer_by_peer(&start_vv, self_vv) {
for change in oplog.iter_changes_peer_by_peer(start_vv, end_vv) {
let start_cnt = start_vv.get(&change.id.peer).copied().unwrap_or(0);
let end_cnt = end_vv.get(&change.id.peer).copied().unwrap_or(0);
if change.id.counter < start_cnt {
let offset = start_cnt - change.id.counter;
diff_changes.push(Cow::Owned(change.slice(offset as usize, change.atom_len())));
let to = change
.atom_len()
.min((end_cnt - change.id.counter) as usize);
diff_changes.push(Cow::Owned(change.slice(offset as usize, to)));
} else if change.id.counter + change.atom_len() as i32 > end_cnt {
let len = end_cnt - change.id.counter;
diff_changes.push(Cow::Owned(change.slice(0, len as usize)));
} else {
diff_changes.push(Cow::Borrowed(change));
}
Expand Down Expand Up @@ -1173,3 +1189,28 @@ pub mod op {
}
}
}

#[cfg(test)]
mod tests {
use crate::{LoroDoc, VersionVector};

#[test]
fn json_range_version() {
let doc = LoroDoc::new_auto_commit();
doc.set_peer_id(0).unwrap();
let list = doc.get_list("list");
list.insert(0, "a").unwrap();
list.insert(0, "b").unwrap();
list.insert(0, "c").unwrap();
let json = doc.export_json_updates(
&VersionVector::from_iter(vec![(0, 1)]),
&VersionVector::from_iter(vec![(0, 2)]),
);
assert_eq!(json.changes[0].ops.len(), 1);
let json = doc.export_json_updates(
&VersionVector::from_iter(vec![(0, 0)]),
&VersionVector::from_iter(vec![(0, 2)]),
);
assert_eq!(json.changes[0].ops.len(), 2);
}
}
8 changes: 6 additions & 2 deletions crates/loro-internal/src/loro.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,10 +586,14 @@ impl LoroDoc {
Ok(())
}

pub fn export_json_updates(&self, vv: &VersionVector) -> JsonSchema {
pub fn export_json_updates(
&self,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> JsonSchema {
self.commit_then_stop();
let oplog = self.oplog.lock().unwrap();
let json = crate::encoding::json_schema::export_json(&oplog, vv);
let json = crate::encoding::json_schema::export_json(&oplog, start_vv, end_vv);
drop(oplog);
self.renew_txn_if_auto_commit();
json
Expand Down
18 changes: 13 additions & 5 deletions crates/loro-wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -879,12 +879,20 @@ impl Loro {

/// Export updates from the specific version to the current version with JSON format.
#[wasm_bindgen(js_name = "exportJsonUpdates")]
pub fn export_json_updates(&self, vv: Option<VersionVector>) -> JsResult<JsJsonSchema> {
let mut json_vv = Default::default();
if let Some(vv) = vv {
json_vv = vv.0;
pub fn export_json_updates(
&self,
start_vv: Option<VersionVector>,
end_vv: Option<VersionVector>,
) -> JsResult<JsJsonSchema> {
let mut json_start_vv = Default::default();
if let Some(vv) = start_vv {
json_start_vv = vv.0;
}
let mut json_end_vv = self.oplog_version().0;
if let Some(vv) = end_vv {
json_end_vv = vv.0;
}
let json_schema = self.0.export_json_updates(&json_vv);
let json_schema = self.0.export_json_updates(&json_start_vv, &json_end_vv);
let s = serde_wasm_bindgen::Serializer::new();
let v = json_schema
.serialize(&s)
Expand Down
8 changes: 6 additions & 2 deletions crates/loro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,8 +298,12 @@ impl LoroDoc {
}

/// Export the current state with json-string format of the document.
pub fn export_json_updates(&self, vv: &VersionVector) -> JsonSchema {
self.doc.export_json_updates(vv)
pub fn export_json_updates(
&self,
start_vv: &VersionVector,
end_vv: &VersionVector,
) -> JsonSchema {
self.doc.export_json_updates(start_vv, end_vv)
}

/// Export all the ops not included in the given `VersionVector`
Expand Down

0 comments on commit a941cdd

Please sign in to comment.