1use std::cmp::Ordering;
25use std::sync::Arc;
26
27#[derive(Debug, Clone)]
34pub struct VersionedPointer<T> {
35 version: u64,
36 target: Arc<T>,
37}
38
39impl<T> VersionedPointer<T> {
40 pub const SIGNATURE: subetha_core::AxisMask = subetha_core::AxisMask::from_axes(
44 &[subetha_core::Axis::Version],
45 );
46
47 pub const fn new(target: Arc<T>, version: u64) -> Self {
48 Self { version, target }
49 }
50
51 #[inline]
52 pub const fn version(&self) -> u64 { self.version }
53
54 #[inline]
55 pub fn target(&self) -> &Arc<T> { &self.target }
56
57 #[inline]
61 pub const fn visible_at(&self, snapshot_version: u64) -> bool {
62 self.version <= snapshot_version
63 }
64
65 pub fn read_at(&self, snapshot_version: u64) -> Option<&T> {
67 if self.visible_at(snapshot_version) {
68 Some(&self.target)
69 } else {
70 None
71 }
72 }
73
74 pub fn replace(&mut self, new_target: Arc<T>, new_version: u64) -> u64 {
78 assert!(
79 new_version > self.version,
80 "MVCC version must be strictly monotonic: {} -> {}",
81 self.version, new_version
82 );
83 let old = self.version;
84 self.version = new_version;
85 self.target = new_target;
86 old
87 }
88}
89
90impl<T> PartialEq for VersionedPointer<T> {
91 fn eq(&self, other: &Self) -> bool {
92 self.version == other.version && Arc::ptr_eq(&self.target, &other.target)
93 }
94}
95
96impl<T> PartialOrd for VersionedPointer<T> {
97 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
98 Some(self.version.cmp(&other.version))
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
116pub struct HybridLogicalClock {
117 pub physical: u64,
118 pub logical: u64,
119}
120
121impl HybridLogicalClock {
122 pub const fn new(physical: u64, logical: u64) -> Self {
123 Self { physical, logical }
124 }
125
126 pub fn now() -> Self {
129 let now = std::time::SystemTime::now()
130 .duration_since(std::time::UNIX_EPOCH)
131 .map(|d| d.as_micros() as u64)
132 .unwrap_or(0);
133 Self { physical: now, logical: 0 }
134 }
135
136 pub fn advance(&self, new_physical: u64) -> Self {
140 if new_physical > self.physical {
141 Self { physical: new_physical, logical: 0 }
142 } else {
143 Self { physical: self.physical, logical: self.logical + 1 }
144 }
145 }
146
147 pub fn merge(&self, received: &Self, local_physical: u64) -> Self {
150 let max_phys = self.physical.max(received.physical).max(local_physical);
151 let new_logical = if max_phys == self.physical && max_phys == received.physical {
152 self.logical.max(received.logical) + 1
153 } else if max_phys == self.physical {
154 self.logical + 1
155 } else if max_phys == received.physical {
156 received.logical + 1
157 } else {
158 0
159 };
160 Self { physical: max_phys, logical: new_logical }
161 }
162}
163
164impl PartialOrd for HybridLogicalClock {
165 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
166 Some(self.cmp(other))
167 }
168}
169
170impl Ord for HybridLogicalClock {
171 fn cmp(&self, other: &Self) -> Ordering {
172 self.physical.cmp(&other.physical)
174 .then(self.logical.cmp(&other.logical))
175 }
176}
177
178#[derive(Debug, Clone)]
183pub struct HlcVersionedPointer<T> {
184 clock: HybridLogicalClock,
185 target: Arc<T>,
186}
187
188impl<T> HlcVersionedPointer<T> {
189 pub const SIGNATURE: subetha_core::AxisMask = subetha_core::AxisMask::from_axes(
193 &[subetha_core::Axis::Version],
194 );
195
196 pub const fn new(target: Arc<T>, clock: HybridLogicalClock) -> Self {
197 Self { clock, target }
198 }
199
200 #[inline]
201 pub const fn clock(&self) -> HybridLogicalClock { self.clock }
202
203 #[inline]
204 pub fn target(&self) -> &Arc<T> { &self.target }
205
206 pub fn visible_at(&self, snapshot: HybridLogicalClock) -> bool {
207 self.clock <= snapshot
208 }
209
210 pub fn read_at(&self, snapshot: HybridLogicalClock) -> Option<&T> {
211 if self.visible_at(snapshot) { Some(&self.target) } else { None }
212 }
213}
214
215#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
225pub struct VectorClock<const N: usize> {
226 pub clock: [u64; N],
227}
228
229impl<const N: usize> VectorClock<N> {
230 pub const fn zero() -> Self { Self { clock: [0; N] } }
231
232 pub fn increment(&mut self, node_idx: usize) {
233 self.clock[node_idx] += 1;
234 }
235
236 pub fn causal_cmp(&self, other: &Self) -> Option<Ordering> {
241 let mut all_le = true;
242 let mut all_ge = true;
243 let mut strict = false;
244 for i in 0..N {
245 match self.clock[i].cmp(&other.clock[i]) {
246 Ordering::Less => { all_ge = false; strict = true; }
247 Ordering::Greater => { all_le = false; strict = true; }
248 Ordering::Equal => {}
249 }
250 }
251 match (all_le, all_ge, strict) {
252 (true, true, false) => Some(Ordering::Equal),
253 (true, false, true) => Some(Ordering::Less),
254 (false, true, true) => Some(Ordering::Greater),
255 _ => None,
256 }
257 }
258
259 pub fn merge(&self, other: &Self) -> Self {
260 let mut out = Self::zero();
261 for i in 0..N {
262 out.clock[i] = self.clock[i].max(other.clock[i]);
263 }
264 out
265 }
266}
267
268#[derive(Debug, Clone)]
272pub struct VectorClockPointer<T, const N: usize> {
273 clock: VectorClock<N>,
274 target: Arc<T>,
275}
276
277impl<T, const N: usize> VectorClockPointer<T, N> {
278 pub const fn new(target: Arc<T>, clock: VectorClock<N>) -> Self {
279 Self { clock, target }
280 }
281
282 pub fn clock(&self) -> VectorClock<N> { self.clock }
283 pub fn target(&self) -> &Arc<T> { &self.target }
284
285 pub fn read_at(&self, snapshot: VectorClock<N>) -> Option<&T> {
288 match self.clock.causal_cmp(&snapshot) {
289 Some(Ordering::Less | Ordering::Equal) => Some(&self.target),
290 _ => None,
291 }
292 }
293}
294
295pub struct VersionedChain<T: Clone> {
303 head: parking_lot::RwLock<Option<Arc<VersionNode<T>>>>,
304}
305
306struct VersionNode<T> {
307 version: u64,
308 value: T,
309 older: Option<Arc<VersionNode<T>>>,
310}
311
312impl<T: Clone> VersionedChain<T> {
313 pub fn new() -> Self {
314 Self { head: parking_lot::RwLock::new(None) }
315 }
316
317 pub fn push(&self, value: T, new_version: u64) {
320 let mut h = self.head.write();
321 if let Some(cur) = h.as_ref() {
322 assert!(
323 new_version > cur.version,
324 "MVCC chain version must be strictly monotonic: {} -> {}",
325 cur.version, new_version
326 );
327 }
328 let older = h.take();
329 *h = Some(Arc::new(VersionNode { version: new_version, value, older }));
330 }
331
332 pub fn read_at(&self, snapshot_version: u64) -> Option<T> {
335 let h = self.head.read();
336 let mut cur = h.clone();
337 while let Some(node) = cur {
338 if node.version <= snapshot_version {
339 return Some(node.value.clone());
340 }
341 cur = node.older.clone();
342 }
343 None
344 }
345
346 pub fn current(&self) -> Option<(u64, T)> {
348 self.head.read().as_ref().map(|n| (n.version, n.value.clone()))
349 }
350
351 pub fn len(&self) -> usize {
353 let h = self.head.read();
354 let mut cur = h.clone();
355 let mut n = 0;
356 while let Some(node) = cur {
357 n += 1;
358 cur = node.older.clone();
359 }
360 n
361 }
362
363 pub fn is_empty(&self) -> bool { self.head.read().is_none() }
364}
365
366impl<T: Clone> Default for VersionedChain<T> {
367 fn default() -> Self { Self::new() }
368}
369
370#[cfg(test)]
371mod tests {
372 use super::*;
373
374 #[test]
375 fn versioned_pointer_visibility() {
376 let p = VersionedPointer::new(Arc::new("hello".to_string()), 100);
377 assert!(p.visible_at(100));
378 assert!(p.visible_at(101));
379 assert!(!p.visible_at(99));
380 assert_eq!(p.read_at(99), None);
381 assert_eq!(p.read_at(150).map(|s| s.as_str()), Some("hello"));
382 }
383
384 #[test]
385 fn versioned_pointer_replace_enforces_monotonic() {
386 let mut p = VersionedPointer::new(Arc::new(1u64), 10);
387 let old = p.replace(Arc::new(2), 11);
388 assert_eq!(old, 10);
389 assert_eq!(p.version(), 11);
390 assert_eq!(**p.target(), 2);
391 }
392
393 #[test]
394 #[should_panic(expected = "monotonic")]
395 fn versioned_pointer_replace_rejects_non_monotonic() {
396 let mut p = VersionedPointer::new(Arc::new(1u64), 10);
397 let _val = p.replace(Arc::new(2), 5);
398 }
399
400 #[test]
401 fn hlc_advances_logical_within_physical() {
402 let h0 = HybridLogicalClock::new(1000, 0);
403 let h1 = h0.advance(1000);
404 assert_eq!(h1.physical, 1000);
405 assert_eq!(h1.logical, 1);
406 let h2 = h1.advance(1001);
407 assert_eq!(h2.physical, 1001);
408 assert_eq!(h2.logical, 0);
409 }
410
411 #[test]
412 fn hlc_lexicographic_ordering() {
413 let a = HybridLogicalClock::new(100, 5);
414 let b = HybridLogicalClock::new(100, 7);
415 let c = HybridLogicalClock::new(101, 0);
416 assert!(a < b);
417 assert!(b < c);
418 assert!(a < c);
419 assert!(c > a);
420 }
421
422 #[test]
423 fn hlc_merge_takes_max_then_bumps_logical() {
424 let local = HybridLogicalClock::new(100, 5);
425 let received = HybridLogicalClock::new(100, 8);
426 let merged = local.merge(&received, 100);
427 assert_eq!(merged.physical, 100);
428 assert_eq!(merged.logical, 9);
429
430 let received2 = HybridLogicalClock::new(200, 0);
431 let merged2 = local.merge(&received2, 100);
432 assert_eq!(merged2.physical, 200);
433 assert_eq!(merged2.logical, 1);
434 }
435
436 #[test]
437 fn hlc_pointer_visibility() {
438 let snapshot = HybridLogicalClock::new(1000, 5);
439 let p1 = HlcVersionedPointer::new(Arc::new(1u64), HybridLogicalClock::new(999, 99));
440 let p2 = HlcVersionedPointer::new(Arc::new(2u64), HybridLogicalClock::new(1000, 5));
441 let p3 = HlcVersionedPointer::new(Arc::new(3u64), HybridLogicalClock::new(1000, 6));
442 assert_eq!(p1.read_at(snapshot).copied(), Some(1));
443 assert_eq!(p2.read_at(snapshot).copied(), Some(2));
444 assert_eq!(p3.read_at(snapshot), None);
445 }
446
447 #[test]
448 fn vector_clock_causal_ordering() {
449 let a = VectorClock::<3> { clock: [1, 0, 0] };
451 let b = VectorClock::<3> { clock: [1, 1, 0] };
452 let c = VectorClock::<3> { clock: [0, 0, 1] };
453 assert_eq!(a.causal_cmp(&b), Some(Ordering::Less));
455 assert_eq!(b.causal_cmp(&a), Some(Ordering::Greater));
456 assert_eq!(a.causal_cmp(&a), Some(Ordering::Equal));
458 assert_eq!(a.causal_cmp(&c), None);
460 assert_eq!(c.causal_cmp(&a), None);
461 }
462
463 #[test]
464 fn vector_clock_merge_takes_pointwise_max() {
465 let a = VectorClock::<3> { clock: [1, 0, 0] };
466 let b = VectorClock::<3> { clock: [0, 0, 1] };
467 let m = a.merge(&b);
468 assert_eq!(m.clock, [1, 0, 1]);
469 }
470
471 #[test]
472 fn vector_clock_pointer_concurrent_read_is_none() {
473 let snapshot = VectorClock::<3> { clock: [1, 1, 1] };
474 let visible = VectorClockPointer::new(
475 Arc::new(1u64),
476 VectorClock::<3> { clock: [1, 0, 0] },
477 );
478 let future = VectorClockPointer::new(
479 Arc::new(2u64),
480 VectorClock::<3> { clock: [2, 0, 0] },
481 );
482 assert!(visible.read_at(snapshot).is_some());
483 assert!(future.read_at(snapshot).is_none());
484 }
485
486 #[test]
487 fn versioned_chain_basic_push_and_read() {
488 let chain = VersionedChain::<u64>::new();
489 chain.push(10, 1);
490 chain.push(20, 2);
491 chain.push(30, 3);
492 assert_eq!(chain.len(), 3);
493 assert_eq!(chain.read_at(0), None);
495 assert_eq!(chain.read_at(1), Some(10));
496 assert_eq!(chain.read_at(2), Some(20));
497 assert_eq!(chain.read_at(3), Some(30));
498 assert_eq!(chain.read_at(99), Some(30));
499 assert_eq!(chain.current(), Some((3, 30)));
500 }
501
502 #[test]
503 #[should_panic(expected = "monotonic")]
504 fn versioned_chain_push_rejects_non_monotonic() {
505 let chain = VersionedChain::<u64>::new();
506 chain.push(10, 5);
507 chain.push(20, 3); }
509
510 #[test]
511 fn versioned_cascade_outer_inner_pattern() {
512 let inner = VersionedPointer::new(Arc::new(42u64), 100);
516 let outer = VersionedPointer::new(Arc::new(inner.clone()), 1);
517
518 assert!(outer.visible_at(5));
520 let inner_ref = outer.read_at(5).unwrap();
521 assert!(inner_ref.visible_at(101));
523 assert!(!inner_ref.visible_at(99));
524 }
525}