Radish alpha
h
Radicle Heartwood Protocol & Stack
Radicle
Git (anonymous pull)
Log in to clone via SSH
crdt: Implement Grow-only Map (`GMap`)
Alexis Sellier committed 3 years ago
commit bcef784ad4d1efd995021efbdb6e447196a474c4
parent eb92ac9036937feae8ed022f692d4178290565b4
2 files changed +110 -0
added radicle-crdt/src/gmap.rs
@@ -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);
+
    }
+
}
modified radicle-crdt/src/lib.rs
@@ -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;