logicaffeine_data/crdt/
gcounter.rs1use super::replica::{generate_replica_id, ReplicaId};
10use super::Merge;
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
20pub struct GCounter {
21 counts: HashMap<ReplicaId, u64>,
23 replica_id: ReplicaId,
25}
26
27impl GCounter {
28 pub fn new() -> Self {
30 Self {
31 counts: HashMap::new(),
32 replica_id: generate_replica_id(),
33 }
34 }
35
36 pub fn with_replica_id(id: ReplicaId) -> Self {
38 Self {
39 counts: HashMap::new(),
40 replica_id: id,
41 }
42 }
43
44 pub fn increment(&mut self, amount: u64) {
46 *self.counts.entry(self.replica_id).or_insert(0) += amount;
47 }
48
49 pub fn value(&self) -> u64 {
51 self.counts.values().sum()
52 }
53
54 pub fn replica_id(&self) -> ReplicaId {
56 self.replica_id
57 }
58}
59
60impl Default for GCounter {
61 fn default() -> Self {
62 Self::new()
63 }
64}
65
66impl Merge for GCounter {
67 fn merge(&mut self, other: &Self) {
72 for (&replica, &count) in &other.counts {
73 let entry = self.counts.entry(replica).or_insert(0);
74 *entry = (*entry).max(count);
75 }
76 }
77}
78
79impl PartialEq<u64> for GCounter {
86 fn eq(&self, other: &u64) -> bool {
87 self.value() == *other
88 }
89}
90
91impl PartialEq<i32> for GCounter {
95 fn eq(&self, other: &i32) -> bool {
96 self.value() == (*other as u64)
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn test_gcounter_new() {
106 let c = GCounter::new();
107 assert_eq!(c.value(), 0);
108 }
109
110 #[test]
111 fn test_gcounter_increment() {
112 let mut c = GCounter::with_replica_id(1);
113 c.increment(5);
114 c.increment(3);
115 assert_eq!(c.value(), 8);
116 }
117
118 #[test]
119 fn test_gcounter_merge_disjoint() {
120 let mut c1 = GCounter::with_replica_id(1);
121 let mut c2 = GCounter::with_replica_id(2);
122
123 c1.increment(5);
124 c2.increment(3);
125
126 c1.merge(&c2);
127 assert_eq!(c1.value(), 8);
128 }
129
130 #[test]
131 fn test_gcounter_merge_commutative() {
132 let mut c1 = GCounter::with_replica_id(1);
133 let mut c2 = GCounter::with_replica_id(2);
134
135 c1.increment(5);
136 c2.increment(3);
137
138 let mut c1_copy = c1.clone();
139 let mut c2_copy = c2.clone();
140
141 c1_copy.merge(&c2);
142 c2_copy.merge(&c1);
143
144 assert_eq!(c1_copy.value(), c2_copy.value());
145 }
146
147 #[test]
148 fn test_gcounter_merge_idempotent() {
149 let mut c1 = GCounter::with_replica_id(1);
150 c1.increment(5);
151
152 let before = c1.value();
153 c1.merge(&c1.clone());
154 assert_eq!(c1.value(), before);
155 }
156
157 #[test]
158 fn test_gcounter_merge_same_replica() {
159 let mut c1 = GCounter::with_replica_id(1);
161 let mut c2 = GCounter::with_replica_id(1);
162
163 c1.increment(5);
164 c2.increment(3);
165
166 c1.merge(&c2);
168 assert_eq!(c1.value(), 5);
169 }
170}