Implement `LWWReg` in terms of `Max`

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-11-22 15:02:26 +01:00
parent b11826b113
commit 0482757ce9
No known key found for this signature in database
3 changed files with 54 additions and 21 deletions

View File

@ -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<S>(i: impl IntoIterator<Item = S>) -> S
where
S: Semilattice,
S: Semilattice + Default,
{
i.into_iter().fold(S::default(), S::join)
}

View File

@ -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<T, C> {
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<T: PartialOrd, C: PartialOrd> LWWReg<T, C> {
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),
}
}
}

View File

@ -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>(T);
impl<T> Max<T> {
pub fn get(&self) -> &T {
&self.0
}
pub fn into_inner(self) -> T {
self.0
}
}
impl<T: PartialOrd> Max<T> {
pub fn merge(&mut self, other: Self) {
if other.0 > self.0 {
self.0 = other.0;
}
}
}
impl<T: num_traits::SaturatingAdd + num_traits::One> Max<T> {
pub fn incr(&mut self) {
self.0 = self.0.saturating_add(&T::one());
@ -13,7 +34,7 @@ impl<T: num_traits::SaturatingAdd + num_traits::One> Max<T> {
impl<T> Default for Max<T>
where
T: num_traits::bounds::Bounded,
T: Bounded,
{
fn default() -> Self {
Self(T::min_value())
@ -34,13 +55,23 @@ impl<T> From<T> for Max<T> {
}
}
impl<T: PartialOrd> Semilattice for Max<T> {
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<T>(pub T);
impl<T> Default for Min<T>
where
T: num_traits::bounds::Bounded,
T: Bounded,
{
fn default() -> Self {
Self(T::max_value())