Skip to content

Commit

Permalink
feat: implementing Counter and expose to js side (#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
Leeeon233 authored Jun 13, 2024
1 parent a941cdd commit afac347
Show file tree
Hide file tree
Showing 36 changed files with 420 additions and 213 deletions.
6 changes: 6 additions & 0 deletions .changeset/silly-schools-arrive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"loro-wasm": patch
"loro-crdt": patch
---

feat: implement `Counter` and expose it to js side
30 changes: 15 additions & 15 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions crates/fuzz/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ publish = false
loro-without-counter = { path = "../loro", package = "loro" }
loro = { git = "https://github.com/loro-dev/loro.git", features = [
"counter",
], rev = "83938290ab2666d85c0c72169127611585a05cf9" }
], rev = "052ca29dc398eba150c8d9a4cebd6f506f1fb2e7" }
loro-common = { git = "https://github.com/loro-dev/loro.git", features = [
"counter",
], rev = "83938290ab2666d85c0c72169127611585a05cf9" }
], rev = "052ca29dc398eba150c8d9a4cebd6f506f1fb2e7" }
# loro = { path = "../loro", package = "loro", features = ["counter"] }
# loro-common = { path = "../loro-common", package = "loro-common", features = [
# "counter",
# ] }
# loro-without-counter = { git = "https://github.com/loro-dev/loro.git", rev = "eb6daf4f064238cbc5c3d357615f5ed73767e98c", package = "loro" }
# loro-without-counter = { git = "https://github.com/loro-dev/loro.git", rev = "43cc07daf7aada060d01505da5efcbc4e6cc2de8", package = "loro" }
fxhash = { workspace = true }
enum_dispatch = { workspace = true }
enum-as-inner = { workspace = true }
Expand Down
4 changes: 2 additions & 2 deletions crates/fuzz/src/actor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) {
continue;
}

if !eq(v, b.get(k).unwrap_or(&LoroValue::I64(0))) {
if !eq(v, b.get(k).unwrap_or(&LoroValue::Double(0.))) {
return false;
}
}
Expand All @@ -378,7 +378,7 @@ pub fn assert_value_eq(a: &LoroValue, b: &LoroValue) {
continue;
}

if !eq(v, a.get(k).unwrap_or(&LoroValue::I64(0))) {
if !eq(v, a.get(k).unwrap_or(&LoroValue::Double(0.))) {
return false;
}
}
Expand Down
8 changes: 4 additions & 4 deletions crates/fuzz/src/container/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ impl Actionable for CounterAction {
fn apply(&self, actor: &mut ActionExecutor, container: usize) -> Option<Container> {
let actor = actor.as_counter_actor_mut().unwrap();
let counter = actor.containers.get(container).unwrap();
counter.increment(self.0 as i64).unwrap();
counter.increment(self.0 as f64).unwrap();
None
}

Expand Down Expand Up @@ -145,13 +145,13 @@ impl FromGenericAction for CounterAction {

#[derive(Debug)]
pub struct CounterTracker {
v: i64,
v: f64,
id: ContainerID,
}

impl ApplyDiff for CounterTracker {
fn empty(id: ContainerID) -> Self {
Self { v: 0, id }
Self { v: 0., id }
}

fn id(&self) -> &ContainerID {
Expand All @@ -165,6 +165,6 @@ impl ApplyDiff for CounterTracker {
}

fn to_value(&self) -> LoroValue {
LoroValue::I64(self.v)
LoroValue::Double(self.v)
}
}
14 changes: 6 additions & 8 deletions crates/fuzz/tests/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ fn unknown_json() {
let doc = loro::LoroDoc::new();
let doc_with_unknown = loro_without_counter::LoroDoc::new();
let counter = doc.get_counter("counter");
counter.increment(5).unwrap();
counter.increment(1).unwrap();
counter.increment(5.).unwrap();
counter.increment(1.).unwrap();
// json format with counter
let json = doc.export_json_updates(&Default::default());
// Test1: old version import newer version json
Expand Down Expand Up @@ -46,13 +46,11 @@ fn unknown_json() {
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
// Test4: newer version import older version json with counter unknown
// TODO: need one more test case for binary unknown
new_doc
.import_json_updates(serde_json::to_string(&unknown_json_from_snapshot).unwrap())
.is_ok()
{
panic!("json schema don't support forward compatibility");
}
.unwrap();
}

#[test]
Expand Down
3 changes: 1 addition & 2 deletions crates/fuzz/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5580,14 +5580,13 @@ fn unknown_container() {
&list.id(),
Arc::new(|e| {
assert_eq!(e.events.len(), 2);
assert!(e.events[1].is_unknown)
}),
);

let doc2 = LoroDoc::new();
let list2 = doc2.get_list("list");
let counter = list2.insert_container(0, LoroCounter::new()).unwrap();
counter.increment(2).unwrap();
counter.increment(2.).unwrap();

doc.import(&doc2.export_snapshot()).unwrap();
}
Expand Down
4 changes: 3 additions & 1 deletion crates/loro-common/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ impl ContainerType {
ContainerType::Tree => LoroValue::List(Arc::new(Default::default())),
ContainerType::MovableList => LoroValue::List(Arc::new(Default::default())),
#[cfg(feature = "counter")]
ContainerType::Counter => LoroValue::I64(0),
ContainerType::Counter => LoroValue::Double(0.),
ContainerType::Unknown(_) => unreachable!(),
}
}
Expand Down Expand Up @@ -386,6 +386,8 @@ mod container {
"Text" | "text" => Ok(ContainerType::Text),
"Tree" | "tree" => Ok(ContainerType::Tree),
"MovableList" | "movableList" => Ok(ContainerType::MovableList),
#[cfg(feature = "counter")]
"Counter" | "counter" => Ok(ContainerType::Counter),
a => {
if a.ends_with(')') {
let start = a.find('(').ok_or_else(|| {
Expand Down
4 changes: 2 additions & 2 deletions crates/loro-internal/src/diff_calc/counter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use super::DiffCalculatorTrait;
#[derive(Debug)]
pub(crate) struct CounterDiffCalculator {
idx: ContainerIdx,
ops: BTreeMap<ID, i64>,
ops: BTreeMap<ID, f64>,
}

impl CounterDiffCalculator {
Expand Down Expand Up @@ -46,7 +46,7 @@ impl DiffCalculatorTrait for CounterDiffCalculator {
to: &crate::VersionVector,
_on_new_container: impl FnMut(&ContainerID),
) -> InternalDiff {
let mut diff = 0;
let mut diff = 0.;
let (b, a) = from.diff_iter(to);

for sub in b {
Expand Down
24 changes: 18 additions & 6 deletions crates/loro-internal/src/encoding/encode_reordered.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ use super::{
ImportBlobMetadata,
};

#[allow(unused_imports)]
use super::value::FutureValue;

/// If any section of the document is longer than this, we will not decode it.
/// It will return an data corruption error instead.
pub(super) const MAX_DECODED_SIZE: usize = 1 << 30;
Expand Down Expand Up @@ -862,7 +865,7 @@ fn decode_snapshot_states(
mode: crate::encoding::EncodeMode::Snapshot,
peers: &peers.peer_ids,
},
);
)?;
}

let s = take(&mut state.states);
Expand Down Expand Up @@ -1138,7 +1141,7 @@ mod encode {
fn get_future_op_prop(op: &FutureInnerContent) -> i32 {
match &op {
#[cfg(feature = "counter")]
FutureInnerContent::Counter(c) => *c as i32,
FutureInnerContent::Counter(_) => 0,
FutureInnerContent::Unknown { prop, .. } => *prop,
}
}
Expand Down Expand Up @@ -1269,7 +1272,14 @@ mod encode {
}
crate::op::InnerContent::Future(f) => match f {
#[cfg(feature = "counter")]
FutureInnerContent::Counter(_) => Value::Future(FutureValue::Counter),
FutureInnerContent::Counter(c) => {
let c_abs = c.abs();
if c_abs.fract() < std::f64::EPSILON && (c_abs as i64) < (2 << 26) {
Value::I64(*c as i64)
} else {
Value::F64(*c)
}
}
FutureInnerContent::Unknown { prop: _, value } => Value::from_owned(value),
},
};
Expand Down Expand Up @@ -1443,9 +1453,11 @@ fn decode_op(
}
}
#[cfg(feature = "counter")]
ContainerType::Counter => {
crate::op::InnerContent::Future(FutureInnerContent::Counter(prop as i64))
}
ContainerType::Counter => match value {
Value::F64(c) => crate::op::InnerContent::Future(FutureInnerContent::Counter(c)),
Value::I64(c) => crate::op::InnerContent::Future(FutureInnerContent::Counter(c as f64)),
_ => unreachable!(),
},
// NOTE: The future container type need also try to parse the unknown type
ContainerType::Unknown(_) => crate::op::InnerContent::Future(FutureInnerContent::Unknown {
prop,
Expand Down
Loading

0 comments on commit afac347

Please sign in to comment.