hybrid_logical_clock/lib.rs
1#![no_std]
2
3use core::cmp::{max, Ordering};
4
5/// Represents a Hybrid Logical Clock (HLC).
6#[derive(Debug, Clone, Copy, PartialEq, Eq)]
7pub struct HybridLogicalClock {
8 /// The physical component of the clock, often referred to as the "wallclock" time.
9 pub physical: u64,
10 /// The logical component of the clock, used to distinguish events occurring at the same physical time.
11 pub logical: u32,
12}
13
14impl HybridLogicalClock {
15 /// Creates a new HybridLogicalClock with the given physical time.
16 /// The logical time is initialized to 0.
17 ///
18 /// # Arguments
19 ///
20 /// * `physical` - The physical time.
21 ///
22 /// # Returns
23 ///
24 /// A new HybridLogicalClock with the given physical time and logical time set to 0.
25 ///
26 /// # Example
27 ///
28 /// ```
29 /// use hybrid_logical_clock::HybridLogicalClock;
30 ///
31 /// let hlc = HybridLogicalClock::new(1000);
32 /// assert_eq!(hlc.physical, 1000);
33 /// assert_eq!(hlc.logical, 0);
34 /// ```
35 pub fn new(physical: u64) -> Self {
36 Self {
37 physical,
38 logical: 0,
39 }
40 }
41
42 /// Creates a new HybridLogicalClock with both physical and logical components.
43 ///
44 /// # Arguments
45 ///
46 /// * `physical` - The physical time.
47 /// * `logical` - The logical time.
48 ///
49 /// # Returns
50 ///
51 /// A new HybridLogicalClock with the given physical and logical times.
52 ///
53 /// # Example
54 ///
55 /// ```
56 /// use hybrid_logical_clock::HybridLogicalClock;
57 ///
58 /// let hlc = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(1000, 5);
59 /// assert_eq!(hlc.physical, 1000);
60 /// assert_eq!(hlc.logical, 5);
61 /// ```
62 pub fn new_with_both_physical_and_logical_clock_time(physical: u64, logical: u32) -> Self {
63 Self { physical, logical }
64 }
65
66 /// Updates the clock based on a received timestamp.
67 ///
68 /// # Arguments
69 ///
70 /// * `received` - The received HybridLogicalClock.
71 /// * `now` - The current physical time.
72 ///
73 /// # Example
74 ///
75 /// ```
76 /// use hybrid_logical_clock::HybridLogicalClock;
77 ///
78 /// let mut hlc = HybridLogicalClock::new(100);
79 /// let received = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(150, 10);
80 /// hlc.update(&received, 200);
81 ///
82 /// assert_eq!(hlc.physical, 200);
83 /// assert_eq!(hlc.logical, 1);
84 /// ```
85 pub fn update(&mut self, received: &Self, now: u64) {
86 // Update the physical time to the maximum of the current physical time, the received physical time, and the current time.
87 self.physical = max(max(self.physical, received.physical), now);
88 // If the physical time is the same as the received physical time and the current time, update the logical time.
89 if self.physical == received.physical && self.physical == now {
90 self.logical = max(self.logical, received.logical) + 1;
91 } else if self.physical == received.physical {
92 self.logical = max(self.logical, received.logical) + 1;
93 } else if self.physical == now {
94 self.logical += 1;
95 } else {
96 self.logical = 0;
97 }
98 }
99
100 /// Checks if this clock is concurrent with another hybrid logical clock.
101 ///
102 /// This method is crucial for determining the causal relationship between events in a distributed system.
103 /// It's particularly useful when you need to detect and handle concurrent operations or updates.
104 ///
105 /// Use this method when:
106 /// 1. Implementing conflict resolution strategies in distributed databases or systems.
107 /// 2. Detecting potential conflicts in collaborative editing systems.
108 /// 3. Ensuring consistency in replicated state machines.
109 /// 4. Implementing version control systems that need to handle parallel changes.
110 ///
111 /// The importance of this method lies in its ability to identify events that happened without knowledge
112 /// of each other. In distributed systems, this information is vital for maintaining consistency and
113 /// correctly merging or reconciling divergent states.
114 ///
115 /// # Arguments
116 ///
117 /// * `other` - The other HybridLogicalClock to compare with.
118 ///
119 /// # Returns
120 ///
121 /// `true` if the clocks are concurrent, `false` otherwise.
122 ///
123 /// # Example
124 ///
125 /// ```
126 /// use hybrid_logical_clock::HybridLogicalClock;
127 ///
128 /// let hlc1 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 5);
129 /// let hlc2 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 10);
130 /// assert!(hlc1.is_concurrent(&hlc2));
131 ///
132 /// // Non-concurrent example
133 /// let hlc3 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(101, 1);
134 /// assert!(!hlc1.is_concurrent(&hlc3));
135 /// ```
136 pub fn is_concurrent(&self, other: &Self) -> bool {
137 self.physical == other.physical && self.logical != other.logical
138 }
139
140}
141
142impl PartialOrd for HybridLogicalClock {
143 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
144 Some(self.cmp(other))
145 }
146}
147
148impl Ord for HybridLogicalClock {
149 fn cmp(&self, other: &Self) -> Ordering {
150 match self.physical.cmp(&other.physical) {
151 Ordering::Equal => self.logical.cmp(&other.logical),
152 other => other,
153 }
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 #[test]
162 fn test_new() {
163 let hlc = HybridLogicalClock::new(100);
164 assert_eq!(hlc.physical, 100);
165 assert_eq!(hlc.logical, 0);
166 }
167
168 #[test]
169 fn test_new_with_both() {
170 let hlc = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 50);
171 assert_eq!(hlc.physical, 100);
172 assert_eq!(hlc.logical, 50);
173 }
174
175 #[test]
176 fn test_update() {
177 let mut hlc1 = HybridLogicalClock::new(100);
178 let hlc2 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(150, 10);
179 hlc1.update(&hlc2, 200);
180 assert_eq!(hlc1.physical, 200);
181 assert_eq!(hlc1.logical, 1);
182 }
183
184 #[test]
185 fn test_is_concurrent() {
186 let hlc1 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 5);
187 let hlc2 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 10);
188 assert!(hlc1.is_concurrent(&hlc2));
189 }
190
191 #[test]
192 fn test_ordering() {
193 let hlc1 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 5);
194 let hlc2 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(100, 10);
195 let hlc3 = HybridLogicalClock::new_with_both_physical_and_logical_clock_time(150, 0);
196 assert!(hlc1 < hlc2);
197 assert!(hlc2 < hlc3);
198 }
199}