use super::replica::{generate_replica_id, ReplicaId};
use super::Merge;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GCounter {
counts: HashMap<ReplicaId, u64>,
replica_id: ReplicaId,
}
impl GCounter {
pub fn new() -> Self {
Self {
counts: HashMap::new(),
replica_id: generate_replica_id(),
}
}
pub fn with_replica_id(id: ReplicaId) -> Self {
Self {
counts: HashMap::new(),
replica_id: id,
}
}
pub fn increment(&mut self, amount: u64) {
*self.counts.entry(self.replica_id).or_insert(0) += amount;
}
pub fn value(&self) -> u64 {
self.counts.values().sum()
}
pub fn replica_id(&self) -> ReplicaId {
self.replica_id
}
}
impl Default for GCounter {
fn default() -> Self {
Self::new()
}
}
impl Merge for GCounter {
fn merge(&mut self, other: &Self) {
for (&replica, &count) in &other.counts {
let entry = self.counts.entry(replica).or_insert(0);
*entry = (*entry).max(count);
}
}
}
impl PartialEq<u64> for GCounter {
fn eq(&self, other: &u64) -> bool {
self.value() == *other
}
}
impl PartialEq<i32> for GCounter {
fn eq(&self, other: &i32) -> bool {
self.value() == (*other as u64)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gcounter_new() {
let c = GCounter::new();
assert_eq!(c.value(), 0);
}
#[test]
fn test_gcounter_increment() {
let mut c = GCounter::with_replica_id(1);
c.increment(5);
c.increment(3);
assert_eq!(c.value(), 8);
}
#[test]
fn test_gcounter_merge_disjoint() {
let mut c1 = GCounter::with_replica_id(1);
let mut c2 = GCounter::with_replica_id(2);
c1.increment(5);
c2.increment(3);
c1.merge(&c2);
assert_eq!(c1.value(), 8);
}
#[test]
fn test_gcounter_merge_commutative() {
let mut c1 = GCounter::with_replica_id(1);
let mut c2 = GCounter::with_replica_id(2);
c1.increment(5);
c2.increment(3);
let mut c1_copy = c1.clone();
let mut c2_copy = c2.clone();
c1_copy.merge(&c2);
c2_copy.merge(&c1);
assert_eq!(c1_copy.value(), c2_copy.value());
}
#[test]
fn test_gcounter_merge_idempotent() {
let mut c1 = GCounter::with_replica_id(1);
c1.increment(5);
let before = c1.value();
c1.merge(&c1.clone());
assert_eq!(c1.value(), before);
}
#[test]
fn test_gcounter_merge_same_replica() {
let mut c1 = GCounter::with_replica_id(1);
let mut c2 = GCounter::with_replica_id(1);
c1.increment(5);
c2.increment(3);
c1.merge(&c2);
assert_eq!(c1.value(), 5);
}
}