diff --git a/radicle-crdt/src/lib.rs b/radicle-crdt/src/lib.rs index 871798e5..7840ebaa 100644 --- a/radicle-crdt/src/lib.rs +++ b/radicle-crdt/src/lib.rs @@ -13,7 +13,7 @@ mod test; //////////////////////////////////////////////////////////////////////////////// /// A join-semilattice. -pub trait Semilattice: Default { +pub trait Semilattice { /// Join or "merge" two semilattices into one. fn join(self, other: Self) -> Self; } @@ -21,7 +21,7 @@ pub trait Semilattice: Default { /// Reduce an iterator of semilattice values to its least upper bound. pub fn fold(i: impl IntoIterator) -> S where - S: Semilattice, + S: Semilattice + Default, { i.into_iter().fold(S::default(), S::join) } diff --git a/radicle-crdt/src/lwwreg.rs b/radicle-crdt/src/lwwreg.rs index 92edf698..7f6dd67e 100644 --- a/radicle-crdt/src/lwwreg.rs +++ b/radicle-crdt/src/lwwreg.rs @@ -1,40 +1,41 @@ +use crate::ord::Max; use crate::Semilattice; /// Last-Write-Wins Register. /// /// In case of conflict, biased towards larger values. -#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] pub struct LWWReg { - value: T, - clock: C, + /// Nb. the order of the tuple types ensures we bias towards the clock before the value. + inner: Max<(C, T)>, } impl LWWReg { pub fn new(value: T, clock: C) -> Self { - Self { value, clock } - } - - pub fn set(&mut self, value: T, clock: C) { - if clock > self.clock || (clock == self.clock && value > self.value) { - self.value = value; - self.clock = clock; + Self { + inner: Max::from((clock, value)), } } + pub fn set(&mut self, value: T, clock: C) { + self.inner.merge(Max::from((clock, value))); + } + pub fn get(&self) -> &T { - &self.value + &self.inner.get().1 } pub fn clock(&self) -> &C { - &self.clock + &self.inner.get().0 } pub fn into_inner(self) -> (T, C) { - (self.value, self.clock) + let (t, c) = self.inner.into_inner(); + (c, t) } pub fn merge(&mut self, other: Self) { - self.set(other.value, other.clock); + self.inner.merge(other.inner); } } @@ -43,9 +44,10 @@ where T: PartialOrd + Default, C: PartialOrd + Default, { - fn join(mut self, other: Self) -> Self { - self.merge(other); - self + fn join(self, other: Self) -> Self { + Self { + inner: self.inner.join(other.inner), + } } } diff --git a/radicle-crdt/src/ord.rs b/radicle-crdt/src/ord.rs index 7271cd94..6338c5aa 100644 --- a/radicle-crdt/src/ord.rs +++ b/radicle-crdt/src/ord.rs @@ -1,10 +1,31 @@ use std::{cmp, ops}; +use num_traits::Bounded; use serde::{Deserialize, Serialize}; +use crate::Semilattice; + #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct Max(T); +impl Max { + pub fn get(&self) -> &T { + &self.0 + } + + pub fn into_inner(self) -> T { + self.0 + } +} + +impl Max { + pub fn merge(&mut self, other: Self) { + if other.0 > self.0 { + self.0 = other.0; + } + } +} + impl Max { pub fn incr(&mut self) { self.0 = self.0.saturating_add(&T::one()); @@ -13,7 +34,7 @@ impl Max { impl Default for Max where - T: num_traits::bounds::Bounded, + T: Bounded, { fn default() -> Self { Self(T::min_value()) @@ -34,13 +55,23 @@ impl From for Max { } } +impl Semilattice for Max { + fn join(self, other: Self) -> Self { + if other.0 > self.0 { + other + } else { + self + } + } +} + #[allow(clippy::derive_ord_xor_partial_ord)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, Serialize, Deserialize)] pub struct Min(pub T); impl Default for Min where - T: num_traits::bounds::Bounded, + T: Bounded, { fn default() -> Self { Self(T::max_value())