crdt: Implement Grow-only Map (`GMap`)

Signed-off-by: Alexis Sellier <alexis@radicle.xyz>
This commit is contained in:
Alexis Sellier 2022-12-01 14:17:46 +01:00
parent eb92ac9036
commit bcef784ad4
No known key found for this signature in database
2 changed files with 110 additions and 0 deletions

108
radicle-crdt/src/gmap.rs Normal file
View File

@ -0,0 +1,108 @@
use std::collections::btree_map::{Entry, IntoIter};
use std::collections::BTreeMap;
use std::ops::Deref;
use crate::Semilattice;
/// Grow-only map.
///
/// Conflicting elements are merged via the [`Semilattice`] instance.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GMap<K, V> {
inner: BTreeMap<K, V>,
}
impl<K: Ord, V: Semilattice> GMap<K, V> {
pub fn singleton(key: K, value: V) -> Self {
Self {
inner: BTreeMap::from_iter([(key, value)]),
}
}
pub fn insert(&mut self, key: K, value: V) {
match self.inner.entry(key) {
Entry::Occupied(mut e) => {
e.get_mut().merge(value);
}
Entry::Vacant(e) => {
e.insert(value);
}
}
}
}
impl<K: Ord, V: Semilattice> FromIterator<(K, V)> for GMap<K, V> {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
let mut map = GMap::default();
for (k, v) in iter.into_iter() {
map.insert(k, v);
}
map
}
}
impl<K: Ord, V: Semilattice> Extend<(K, V)> for GMap<K, V> {
fn extend<I: IntoIterator<Item = (K, V)>>(&mut self, iter: I) {
for (k, v) in iter.into_iter() {
self.insert(k, v);
}
}
}
impl<K, V> IntoIterator for GMap<K, V> {
type Item = (K, V);
type IntoIter = IntoIter<K, V>;
fn into_iter(self) -> Self::IntoIter {
self.inner.into_iter()
}
}
impl<K, V> Default for GMap<K, V> {
fn default() -> Self {
Self {
inner: BTreeMap::default(),
}
}
}
impl<K: Ord, V: Semilattice> Semilattice for GMap<K, V> {
fn merge(&mut self, other: Self) {
for (k, v) in other.into_iter() {
self.insert(k, v);
}
}
}
impl<K, V> Deref for GMap<K, V> {
type Target = BTreeMap<K, V>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
#[cfg(test)]
mod tests {
use quickcheck_macros::quickcheck;
use super::*;
use crate::ord::Max;
#[quickcheck]
fn prop_semilattice(
a: Vec<(u8, Max<u8>)>,
b: Vec<(u8, Max<u8>)>,
c: Vec<(u8, Max<u8>)>,
mix: Vec<(u8, Max<u8>)>,
) {
let mut a = GMap::from_iter(a);
let mut b = GMap::from_iter(b);
let c = GMap::from_iter(c);
a.extend(mix.clone());
b.extend(mix);
crate::test::assert_laws(&a, &b, &c);
}
}

View File

@ -4,6 +4,7 @@
#![allow(clippy::type_complexity)]
pub mod change;
pub mod clock;
pub mod gmap;
pub mod lwwmap;
pub mod lwwreg;
pub mod lwwset;
@ -17,6 +18,7 @@ pub mod test;
pub use change::*;
pub use clock::Lamport;
pub use gmap::GMap;
pub use lwwmap::LWWMap;
pub use lwwreg::LWWReg;
pub use lwwset::LWWSet;