Skip to content

Commit 46cdf8a

Browse files
committed
Merge branch 'main' into doc-wasm
2 parents 1c0460c + fdd24bd commit 46cdf8a

File tree

10 files changed

+110
-57
lines changed

10 files changed

+110
-57
lines changed

crates/loro-internal/examples/encoding.rs

+35-29
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,49 @@
1-
use std::time::Instant;
2-
31
use bench_utils::TextAction;
42
use loro_internal::LoroDoc;
53

64
fn main() {
75
let actions = bench_utils::get_automerge_actions();
86

7+
// let loro = LoroDoc::default();
8+
// let loro_b = LoroDoc::default();
9+
// let text = loro.get_text("text");
10+
// let mut i = 0;
11+
// let start = Instant::now();
12+
// for TextAction { pos, ins, del } in actions.iter() {
13+
// {
14+
// let mut txn = loro.txn().unwrap();
15+
// text.delete(&mut txn, *pos, *del).unwrap();
16+
// text.insert(&mut txn, *pos, ins).unwrap();
17+
// }
18+
19+
// loro_b
20+
// .import(&loro.export_from(&loro_b.oplog_vv()))
21+
// .unwrap();
22+
// i += 1;
23+
// if i == 30000 {
24+
// break;
25+
// }
26+
// }
27+
28+
// println!("{}ms", start.elapsed().as_millis());
29+
930
let loro = LoroDoc::default();
10-
let loro_b = LoroDoc::default();
1131
let text = loro.get_text("text");
12-
let mut i = 0;
13-
let start = Instant::now();
32+
1433
for TextAction { pos, ins, del } in actions.iter() {
15-
{
16-
let mut txn = loro.txn().unwrap();
17-
text.delete(&mut txn, *pos, *del).unwrap();
18-
text.insert(&mut txn, *pos, ins).unwrap();
19-
}
20-
21-
loro_b
22-
.import(&loro.export_from(&loro_b.oplog_vv()))
23-
.unwrap();
24-
i += 1;
25-
if i == 30000 {
26-
break;
27-
}
34+
let mut txn = loro.txn().unwrap();
35+
text.delete(&mut txn, *pos, *del).unwrap();
36+
text.insert(&mut txn, *pos, ins).unwrap();
37+
txn.commit().unwrap();
2838
}
2939

30-
println!("{}ms", start.elapsed().as_millis());
31-
32-
// let loro = LoroDoc::default();
33-
// let text = loro.get_text("text");
34-
35-
// for TextAction { pos, ins, del } in actions.iter() {
36-
// let mut txn = loro.txn().unwrap();
37-
// text.delete(&mut txn, *pos, *del).unwrap();
38-
// text.insert(&mut txn, *pos, ins).unwrap();
39-
// txn.commit().unwrap();
40-
// }
40+
let snapshot = loro.export_snapshot();
41+
let output = miniz_oxide::deflate::compress_to_vec(&snapshot, 6);
42+
println!(
43+
"snapshot size {} after compression {}",
44+
snapshot.len(),
45+
output.len()
46+
);
4147

4248
// {
4349
// // Delta encoding

crates/loro-internal/src/container/richtext/richtext_state.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1547,7 +1547,7 @@ impl RichtextState {
15471547
"pos: {}, len: {}, self.len(): {}",
15481548
pos,
15491549
len,
1550-
&self.to_string()
1550+
&self.len_entity(),
15511551
);
15521552
// PERF: may use cache to speed up
15531553
self.cursor_cache.invalidate();

crates/loro-internal/src/encoding/encode_enhanced.rs

+8-13
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ use loro_common::{HasCounterSpan, HasIdSpan, HasLamportSpan, TreeID};
55
use rle::{HasLength, RleVec, Sliceable};
66
use serde_columnar::{columnar, iter_from_bytes, to_vec};
77
use std::{borrow::Cow, ops::Deref, sync::Arc};
8-
use zerovec::{vecs::Index32, VarZeroVec};
98

109
use crate::{
1110
change::{Change, Timestamp},
@@ -27,12 +26,9 @@ use crate::{
2726

2827
type PeerIdx = u32;
2928

30-
#[zerovec::make_varule(RootContainerULE)]
31-
#[zerovec::derive(Serialize, Deserialize)]
3229
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)]
33-
struct RootContainer<'a> {
34-
#[serde(borrow)]
35-
name: Cow<'a, str>,
30+
struct RootContainer {
31+
name: InternalString,
3632
type_: ContainerType,
3733
}
3834

@@ -149,8 +145,7 @@ struct DocEncoding<'a> {
149145
style_info: Cow<'a, [u8]>,
150146
style_key: Vec<usize>,
151147
style_values: Vec<LoroValue>,
152-
#[columnar(borrow)]
153-
root_containers: VarZeroVec<'a, RootContainerULE, Index32>,
148+
root_containers: Vec<RootContainer>,
154149
start_counter: Vec<Counter>,
155150
values: Vec<Option<LoroValue>>,
156151
clients: Vec<PeerID>,
@@ -379,7 +374,7 @@ pub fn encode_oplog_v2(oplog: &OpLog, vv: &VersionVector) -> Vec<u8> {
379374
clients: peers,
380375
keys,
381376
start_counter,
382-
root_containers: VarZeroVec::from(&root_containers),
377+
root_containers,
383378
normal_containers,
384379
values,
385380
style_key: style_key_idx,
@@ -401,7 +396,7 @@ fn extract_containers(
401396
peer_id_to_idx: &mut FxHashMap<PeerID, PeerIdx>,
402397
peers: &mut Vec<PeerID>,
403398
) -> (
404-
Vec<RootContainer<'static>>,
399+
Vec<RootContainer>,
405400
FxHashMap<ContainerIdx, usize>,
406401
Vec<NormalContainer>,
407402
) {
@@ -427,7 +422,7 @@ fn extract_containers(
427422
} => {
428423
container_idx2index.insert(container, root_containers.len());
429424
root_containers.push(RootContainer {
430-
name: Cow::Owned(name.to_string()),
425+
name,
431426
type_: container_type,
432427
});
433428
}
@@ -505,8 +500,8 @@ pub fn decode_oplog_v2(oplog: &mut OpLog, input: &[u8]) -> Result<(), LoroError>
505500
return None;
506501
};
507502
Some(ContainerID::Root {
508-
name: container.name.into(),
509-
container_type: ContainerType::from_u8(container.type_),
503+
name: container.name.clone(),
504+
container_type: container.type_,
510505
})
511506
} else {
512507
let Some(container) = normal_containers.get(idx - root_containers.len()) else {

crates/loro-internal/src/event.rs

+19-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use enum_as_inner::EnumAsInner;
2+
use fxhash::FxHasher64;
23
use serde::{Deserialize, Serialize};
34
use smallvec::SmallVec;
45

@@ -10,7 +11,10 @@ use crate::{
1011
InternalString, LoroValue,
1112
};
1213

13-
use std::borrow::Cow;
14+
use std::{
15+
borrow::Cow,
16+
hash::{Hash, Hasher},
17+
};
1418

1519
use loro_common::{ContainerID, TreeID};
1620

@@ -43,9 +47,21 @@ pub struct DocDiff {
4347
pub to: Frontiers,
4448
pub origin: InternalString,
4549
pub local: bool,
50+
/// Whether the diff is created from the checkout operation.
51+
pub from_checkout: bool,
4652
pub diff: Vec<ContainerDiff>,
4753
}
4854

55+
impl DocDiff {
56+
/// Get the unique id of the diff.
57+
pub fn id(&self) -> u64 {
58+
let mut hasher = FxHasher64::default();
59+
self.from.hash(&mut hasher);
60+
self.to.hash(&mut hasher);
61+
hasher.finish()
62+
}
63+
}
64+
4965
#[derive(Debug, Clone)]
5066
pub(crate) struct InternalContainerDiff {
5167
pub(crate) idx: ContainerIdx,
@@ -71,6 +87,7 @@ pub(crate) enum DiffVariant {
7187
pub(crate) struct InternalDocDiff<'a> {
7288
pub(crate) origin: InternalString,
7389
pub(crate) local: bool,
90+
pub(crate) from_checkout: bool,
7491
pub(crate) diff: Cow<'a, [InternalContainerDiff]>,
7592
pub(crate) new_version: Cow<'a, Frontiers>,
7693
}
@@ -80,6 +97,7 @@ impl<'a> InternalDocDiff<'a> {
8097
InternalDocDiff {
8198
origin: self.origin,
8299
local: self.local,
100+
from_checkout: self.from_checkout,
83101
diff: Cow::Owned((*self.diff).to_owned()),
84102
new_version: Cow::Owned((*self.new_version).to_owned()),
85103
}

crates/loro-internal/src/loro.rs

+9-12
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,18 @@ pub struct LoroDoc {
5252
arena: SharedArena,
5353
observer: Arc<Observer>,
5454
diff_calculator: Arc<Mutex<DiffCalculator>>,
55+
// when dropping the doc, the txn will be commited
5556
txn: Arc<Mutex<Option<Transaction>>>,
5657
auto_commit: bool,
5758
detached: bool,
5859
}
5960

61+
impl Default for LoroDoc {
62+
fn default() -> Self {
63+
Self::new()
64+
}
65+
}
66+
6067
impl LoroDoc {
6168
pub fn new() -> Self {
6269
let oplog = OpLog::new();
@@ -397,6 +404,7 @@ impl LoroDoc {
397404
origin,
398405
local: false,
399406
diff: (diff).into(),
407+
from_checkout: false,
400408
new_version: Cow::Owned(oplog.frontiers().clone()),
401409
});
402410
}
@@ -620,6 +628,7 @@ impl LoroDoc {
620628
origin: "checkout".into(),
621629
local: true,
622630
diff: Cow::Owned(diff),
631+
from_checkout: true,
623632
new_version: Cow::Owned(frontiers.clone()),
624633
});
625634
let events = state.take_events();
@@ -664,18 +673,6 @@ fn parse_encode_header(bytes: &[u8]) -> Result<(&[u8], EncodeMode), LoroError> {
664673
Ok((&input[1..], mode))
665674
}
666675

667-
impl Default for LoroDoc {
668-
fn default() -> Self {
669-
Self::new()
670-
}
671-
}
672-
673-
impl Drop for LoroDoc {
674-
fn drop(&mut self) {
675-
self.abort_txn();
676-
}
677-
}
678-
679676
#[cfg(test)]
680677
mod test {
681678
use loro_common::ID;

crates/loro-internal/src/state.rs

+3
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@ impl DocState {
429429
self.record_diff(InternalDocDiff {
430430
origin: Default::default(),
431431
local: false,
432+
from_checkout: false,
432433
diff,
433434
new_version: Cow::Borrowed(&frontiers),
434435
});
@@ -664,6 +665,7 @@ impl DocState {
664665
let to = (*diffs.last().unwrap().new_version).to_owned();
665666
let origin = diffs[0].origin.clone();
666667
let local = diffs[0].local;
668+
let from_checkout = diffs[0].from_checkout;
667669
for diff in diffs {
668670
#[allow(clippy::unnecessary_to_owned)]
669671
for container_diff in diff.diff.into_owned() {
@@ -714,6 +716,7 @@ impl DocState {
714716
from,
715717
to,
716718
origin,
719+
from_checkout,
717720
local,
718721
diff,
719722
}

crates/loro-internal/src/txn.rs

+1
Original file line numberDiff line numberDiff line change
@@ -321,6 +321,7 @@ impl Transaction {
321321
})
322322
.collect(),
323323
),
324+
from_checkout: false,
324325
new_version: Cow::Borrowed(oplog.frontiers()),
325326
}),
326327
);

crates/loro-internal/tests/test.rs

+23-1
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,33 @@
1-
use std::sync::{Arc, Mutex};
1+
use std::sync::{atomic::AtomicBool, Arc, Mutex};
22

33
use loro_common::{ContainerID, ContainerType, LoroValue, ID};
44
use loro_internal::{
55
container::richtext::TextStyleInfoFlag, version::Frontiers, ApplyDiff, LoroDoc, ToJson,
66
};
77
use serde_json::json;
88

9+
#[test]
10+
fn event_from_checkout() {
11+
let mut a = LoroDoc::new_auto_commit();
12+
let sub_id = a.subscribe_root(Arc::new(|event| {
13+
assert!(!event.doc.from_checkout);
14+
}));
15+
a.get_text("text").insert_(0, "hello").unwrap();
16+
a.commit_then_renew();
17+
let version = a.oplog_frontiers();
18+
a.get_text("text").insert_(0, "hello").unwrap();
19+
a.commit_then_renew();
20+
a.unsubscribe(sub_id);
21+
let ran = Arc::new(AtomicBool::new(false));
22+
let ran_cloned = ran.clone();
23+
a.subscribe_root(Arc::new(move |event| {
24+
assert!(event.doc.from_checkout);
25+
ran.store(true, std::sync::atomic::Ordering::Relaxed);
26+
}));
27+
a.checkout(&version).unwrap();
28+
assert!(ran_cloned.load(std::sync::atomic::Ordering::Relaxed));
29+
}
30+
931
#[test]
1032
fn out_of_bound_test() {
1133
let a = LoroDoc::new_auto_commit();

crates/loro-wasm/src/lib.rs

+4
Original file line numberDiff line numberDiff line change
@@ -873,6 +873,7 @@ fn call_subscriber(ob: observer::Observer, e: DiffEvent) {
873873
origin: e.doc.origin.to_string(),
874874
target: e.container.id.clone(),
875875
diff: e.container.diff.to_owned(),
876+
from_checkout: e.doc.from_checkout,
876877
}
877878
// PERF: converting the events into js values may hurt performance
878879
.into_js();
@@ -890,6 +891,7 @@ fn call_after_micro_task(ob: observer::Observer, e: DiffEvent) {
890891
let copy = drop_handler.clone();
891892
let event = Event {
892893
from_children: e.from_children,
894+
from_checkout: e.doc.from_checkout,
893895
local: e.doc.local,
894896
origin: e.doc.origin.to_string(),
895897
target: e.container.id.clone(),
@@ -924,6 +926,7 @@ pub struct Event {
924926
pub from_children: bool,
925927
origin: String,
926928
target: ContainerID,
929+
from_checkout: bool,
927930
diff: Diff,
928931
path: JsValue,
929932
}
@@ -932,6 +935,7 @@ impl Event {
932935
fn into_js(self) -> JsValue {
933936
let obj = js_sys::Object::new();
934937
Reflect::set(&obj, &"local".into(), &self.local.into()).unwrap();
938+
Reflect::set(&obj, &"fromCheckout".into(), &self.from_checkout.into()).unwrap();
935939
Reflect::set(&obj, &"fromChildren".into(), &self.from_children.into()).unwrap();
936940
Reflect::set(&obj, &"origin".into(), &self.origin.into()).unwrap();
937941
Reflect::set(&obj, &"target".into(), &self.target.to_string().into()).unwrap();

loro-js/src/index.ts

+7
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ export type TreeDiff = {
9494

9595
export type Diff = ListDiff | TextDiff | MapDiff | TreeDiff;
9696

97+
export interface LoroEvent {
98+
local: boolean;
99+
origin?: string;
100+
diff: Diff;
101+
target: ContainerID;
102+
path: Path;
103+
}
97104

98105
interface Listener {
99106
(event: LoroEvent): void;

0 commit comments

Comments
 (0)