Skip to main content

causal_length/
register.rs

1use super::*;
2use std::cmp::max;
3
4#[cfg(feature = "serialization")]
5use serde_derive::{Deserialize, Serialize};
6
7/// Causal Length Register
8///
9/// Register implements a single member for the set described in the paper, with the addition of a
10/// tag. Sort of acts like a CRDT Option type. Register doesn't directly use the tag, but it also
11/// acts as a delta for the other CRDT's in this crate.
12#[derive(Clone, Debug, Default, Eq, PartialEq)]
13#[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))]
14pub struct Register<T, Tag, CL>
15where
16    T: Key,
17    Tag: TagT,
18    CL: CausalLength,
19{
20    pub(crate) item: T,
21    pub(crate) tag: Tag,
22    pub(crate) length: CL,
23}
24
25impl<T, Tag, CL> Register<T, Tag, CL>
26where
27    T: Key,
28    Tag: TagT,
29    CL: CausalLength,
30{
31    /// Create a new `Register`
32    pub fn new(item: T, tag: Tag) -> Register<T, Tag, CL> {
33        Register {
34            item,
35            tag,
36            length: CL::one(),
37        }
38    }
39
40    pub(crate) fn make<I>(item: I, tag: Tag, length: CL) -> Register<T, Tag, CL>
41    where
42        I: Into<T>,
43    {
44        Register {
45            item: item.into(),
46            tag,
47            length,
48        }
49    }
50
51    /// Returns `None` if the register is empty. If present returns `Some(&T, Tag)`
52    pub fn get(&self) -> Option<(&T, Tag)> {
53        match &self {
54            Register { item, tag, length } if length.is_odd() => Some((item, *tag)),
55            _ => None,
56        }
57    }
58
59    /// Set value
60    pub fn set(&mut self, item: T, tag: Tag) {
61        self.item = item;
62        self.tag = max(self.tag, tag);
63
64        if self.length.is_odd() {
65            self.length = self.length + CL::one() + CL::one();
66        } else {
67            self.length = self.length + CL::one();
68        }
69    }
70
71    /// Clear value
72    pub fn clear(&mut self, tag: Tag) {
73        if self.length.is_odd() {
74            self.length = self.length + CL::one();
75            self.tag = max(self.tag, tag);
76        }
77    }
78
79    // Accessor for tag
80    pub fn item(&self) -> &T {
81        &self.item
82    }
83
84    // Accessor for tag
85    pub fn tag(&self) -> Tag {
86        self.tag
87    }
88
89    // Accessor for length
90    pub fn length(&self) -> CL {
91        self.length
92    }
93}
94
95impl<T, Tag, CL> Register<T, Tag, CL>
96where
97    T: Key + Ord,
98    Tag: TagT,
99    CL: CausalLength,
100{
101    /// Merge two register values
102    pub fn merge(&mut self, other: &Register<T, Tag, CL>) {
103        if other.length > self.length && other.length.is_odd() {
104            self.item = other.item.clone();
105            self.tag = other.tag;
106        }
107        if other.length == self.length {
108            if other.tag > self.tag {
109                self.item = other.item.clone();
110                self.tag = max(self.tag, other.tag);
111            } else if other.tag == self.tag && other.item > self.item {
112                self.item = other.item.clone();
113            }
114        }
115        self.length = max(self.length, other.length);
116    }
117}
118
119#[cfg(test)]
120use quickcheck::{Arbitrary, Gen};
121#[cfg(test)]
122impl<T, Tag, CL> Arbitrary for Register<T, Tag, CL>
123where
124    T: Key + Arbitrary,
125    Tag: TagT + Arbitrary,
126    CL: CausalLength + Arbitrary,
127{
128    fn arbitrary(g: &mut Gen) -> Register<T, Tag, CL> {
129        Register::make(T::arbitrary(g), Tag::arbitrary(g), CL::arbitrary(g))
130    }
131}
132
133#[cfg(test)]
134mod tests {
135    use super::*;
136    use quickcheck_macros::quickcheck;
137
138    #[test]
139    fn test_merge() {
140        let mut reg1: Register<&str, u32, u16> = Register::new("foo", 0);
141        let mut reg2 = reg1.clone();
142        let mut reg3 = reg1.clone();
143
144        reg3.clear(2);
145        reg2.set("bar", 2);
146
147        reg1.merge(&reg2);
148        reg1.merge(&reg3);
149
150        assert_eq!(reg1, reg2);
151        assert_eq!(reg1.length, 3);
152        assert_eq!(reg1.get(), Some((&"bar", 2)));
153    }
154
155    #[cfg(feature = "serialization")]
156    #[test]
157    fn test_serialization() {
158        let reg1: Register<&str, u32, u16> = Register::new("foo", 0);
159        let data = serde_json::to_string(&reg1).unwrap();
160        assert_eq!(&data, r#"{"item":"foo","tag":0,"length":1}"#);
161    }
162
163    fn merge(mut acc: Register<u8, u8, u8>, el: &Register<u8, u8, u8>) -> Register<u8, u8, u8> {
164        acc.merge(el);
165        acc
166    }
167
168    #[quickcheck]
169    fn is_merge_commutative(xs: Vec<Register<u8, u8, u8>>) -> bool {
170        let left = xs.iter().fold(Register::default(), merge);
171        let right = xs.iter().rfold(Register::default(), merge);
172        left.get() == right.get()
173    }
174
175    #[test]
176    fn test_fup() {
177        let xs = vec![
178            Register {
179                item: 255,
180                tag: 174,
181                length: 1,
182            },
183            Register {
184                item: 9,
185                tag: 162,
186                length: 176,
187            },
188        ];
189        let left = xs.iter().fold(Register::default(), merge);
190        let right = xs.iter().rfold(Register::default(), merge);
191        assert_eq!(left.get(), right.get());
192    }
193}