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, Default, 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 Merge for GCounter {
61 fn merge(&mut self, other: &Self) {
66 for (&replica, &count) in &other.counts {
67 let entry = self.counts.entry(replica).or_insert(0);
68 *entry = (*entry).max(count);
69 }
70 }
71}
72
73impl PartialEq<u64> for GCounter {
80 fn eq(&self, other: &u64) -> bool {
81 self.value() == *other
82 }
83}
84
85impl PartialEq<i32> for GCounter {
89 fn eq(&self, other: &i32) -> bool {
90 self.value() == (*other as u64)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn test_gcounter_new() {
100 let c = GCounter::new();
101 assert_eq!(c.value(), 0);
102 }
103
104 #[test]
105 fn test_gcounter_increment() {
106 let mut c = GCounter::with_replica_id(1);
107 c.increment(5);
108 c.increment(3);
109 assert_eq!(c.value(), 8);
110 }
111
112 #[test]
113 fn test_gcounter_merge_disjoint() {
114 let mut c1 = GCounter::with_replica_id(1);
115 let mut c2 = GCounter::with_replica_id(2);
116
117 c1.increment(5);
118 c2.increment(3);
119
120 c1.merge(&c2);
121 assert_eq!(c1.value(), 8);
122 }
123
124 #[test]
125 fn test_gcounter_merge_commutative() {
126 let mut c1 = GCounter::with_replica_id(1);
127 let mut c2 = GCounter::with_replica_id(2);
128
129 c1.increment(5);
130 c2.increment(3);
131
132 let mut c1_copy = c1.clone();
133 let mut c2_copy = c2.clone();
134
135 c1_copy.merge(&c2);
136 c2_copy.merge(&c1);
137
138 assert_eq!(c1_copy.value(), c2_copy.value());
139 }
140
141 #[test]
142 fn test_gcounter_merge_idempotent() {
143 let mut c1 = GCounter::with_replica_id(1);
144 c1.increment(5);
145
146 let before = c1.value();
147 c1.merge(&c1.clone());
148 assert_eq!(c1.value(), before);
149 }
150
151 #[test]
152 fn test_gcounter_merge_same_replica() {
153 let mut c1 = GCounter::with_replica_id(1);
155 let mut c2 = GCounter::with_replica_id(1);
156
157 c1.increment(5);
158 c2.increment(3);
159
160 c1.merge(&c2);
162 assert_eq!(c1.value(), 5);
163 }
164}