y_octo/doc/common/
state.rs

1use std::ops::{Deref, DerefMut};
2
3use super::{
4    Client, ClientMap, Clock, CrdtRead, CrdtReader, CrdtWrite, CrdtWriter, HASHMAP_SAFE_CAPACITY, HashMapExt, Id,
5    JwstCodecResult,
6};
7
8#[derive(Default, Debug, PartialEq, Clone)]
9pub struct StateVector(ClientMap<Clock>);
10
11impl StateVector {
12    pub fn set_max(&mut self, client: Client, clock: Clock) {
13        self.entry(client)
14            .and_modify(|m_clock| {
15                if *m_clock < clock {
16                    *m_clock = clock;
17                }
18            })
19            .or_insert(clock);
20    }
21
22    pub fn get(&self, client: &Client) -> Clock {
23        *self.0.get(client).unwrap_or(&0)
24    }
25
26    pub fn contains(&self, id: &Id) -> bool {
27        id.clock <= self.get(&id.client)
28    }
29
30    pub fn set_min(&mut self, client: Client, clock: Clock) {
31        self.entry(client)
32            .and_modify(|m_clock| {
33                if *m_clock > clock {
34                    *m_clock = clock;
35                }
36            })
37            .or_insert(clock);
38    }
39
40    pub fn iter(&self) -> impl Iterator<Item = (&Client, &Clock)> {
41        self.0.iter()
42    }
43
44    pub fn merge_with(&mut self, other: &Self) {
45        for (client, clock) in other.iter() {
46            self.set_min(*client, *clock);
47        }
48    }
49}
50
51impl Deref for StateVector {
52    type Target = ClientMap<Clock>;
53
54    fn deref(&self) -> &Self::Target {
55        &self.0
56    }
57}
58
59impl DerefMut for StateVector {
60    fn deref_mut(&mut self) -> &mut Self::Target {
61        &mut self.0
62    }
63}
64
65impl<const N: usize> From<[(Client, Clock); N]> for StateVector {
66    fn from(value: [(Client, Clock); N]) -> Self {
67        let mut map = ClientMap::with_capacity(N);
68
69        for (client, clock) in value {
70            map.insert(client, clock);
71        }
72
73        Self(map)
74    }
75}
76
77impl<R: CrdtReader> CrdtRead<R> for StateVector {
78    fn read(decoder: &mut R) -> JwstCodecResult<Self> {
79        let len = decoder.read_var_u64()? as usize;
80
81        // See: [HASHMAP_SAFE_CAPACITY]
82        let mut map = ClientMap::with_capacity(len.min(HASHMAP_SAFE_CAPACITY));
83        for _ in 0..len {
84            let client = decoder.read_var_u64()?;
85            let clock = decoder.read_var_u64()?;
86            map.insert(client, clock);
87        }
88
89        map.shrink_to_fit();
90        Ok(Self(map))
91    }
92}
93
94impl<W: CrdtWriter> CrdtWrite<W> for StateVector {
95    fn write(&self, encoder: &mut W) -> JwstCodecResult {
96        encoder.write_var_u64(self.len() as u64)?;
97
98        for (client, clock) in self.iter() {
99            encoder.write_var_u64(*client)?;
100            encoder.write_var_u64(*clock)?;
101        }
102
103        Ok(())
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn test_state_vector_basic() {
113        let mut state_vector = StateVector::from([(1, 1), (2, 2), (3, 3)]);
114        assert_eq!(state_vector.len(), 3);
115        assert_eq!(state_vector.get(&1), 1);
116
117        state_vector.set_min(1, 0);
118        assert_eq!(state_vector.get(&1), 0);
119
120        state_vector.set_max(1, 4);
121        assert_eq!(state_vector.get(&1), 4);
122
123        // set inexistent client
124        state_vector.set_max(4, 1);
125        assert_eq!(state_vector.get(&4), 1);
126
127        // same client with larger clock
128        assert!(!state_vector.contains(&(1, 5).into()));
129    }
130
131    #[test]
132    fn test_state_vector_merge() {
133        let mut state_vector = StateVector::from([(1, 1), (2, 2), (3, 3)]);
134        let other_state_vector = StateVector::from([(1, 5), (2, 6), (3, 7)]);
135        state_vector.merge_with(&other_state_vector);
136        assert_eq!(state_vector, StateVector::from([(3, 3), (1, 1), (2, 2)]));
137    }
138}