1use std::collections::VecDeque;
2
3use crate::SizeGauge;
4
5#[derive(Debug)]
15pub struct Ring<T> {
16 buf: VecDeque<T>,
17 capacity: usize,
18 gauge: SizeGauge,
19}
20
21impl<T: Clone> Clone for Ring<T> {
22 fn clone(&self) -> Self {
23 let gauge = SizeGauge::new();
26 gauge.set(self.buf.len());
27 Self {
28 buf: self.buf.clone(),
29 capacity: self.capacity,
30 gauge,
31 }
32 }
33}
34
35impl<T> Ring<T> {
36 pub fn with_capacity(capacity: usize, gauge: SizeGauge) -> Self {
37 Self {
38 buf: VecDeque::new(),
39 capacity,
40 gauge,
41 }
42 }
43
44 pub fn push(&mut self, item: T) -> Option<T> {
48 if self.capacity == 0 {
49 return Some(item);
50 }
51 let evicted = if self.buf.len() >= self.capacity {
52 self.buf.pop_front()
53 } else {
54 None
55 };
56 self.buf.push_back(item);
57 self.gauge.set(self.buf.len());
58 evicted
59 }
60
61 pub fn iter(&self) -> impl Iterator<Item = &T> {
62 self.buf.iter()
63 }
64
65 pub fn len(&self) -> usize {
66 self.buf.len()
67 }
68
69 pub fn is_empty(&self) -> bool {
70 self.buf.is_empty()
71 }
72
73 pub fn capacity(&self) -> usize {
74 self.capacity
75 }
76}
77
78#[cfg(test)]
79mod tests {
80 use crate::{Ring, SizeGauge};
81
82 #[test]
83 fn push_never_exceeds_capacity_and_returns_evicted() {
84 let gauge = SizeGauge::new();
85 let mut ring: Ring<u32> = Ring::with_capacity(3, gauge.clone());
86 assert_eq!(ring.push(1), None);
87 assert_eq!(ring.push(2), None);
88 assert_eq!(ring.push(3), None);
89 assert_eq!(ring.len(), 3);
90 assert_eq!(gauge.get(), 3);
91 assert_eq!(ring.push(4), Some(1));
93 assert_eq!(ring.len(), 3);
94 assert_eq!(gauge.get(), 3);
95 let items: Vec<u32> = ring.iter().copied().collect();
96 assert_eq!(items, vec![2, 3, 4]);
97 }
98
99 #[test]
100 fn clone_has_independent_gauge_so_snapshot_pushes_do_not_corrupt_owner() {
101 let owner_gauge = SizeGauge::new();
102 let mut ring: Ring<u32> = Ring::with_capacity(4, owner_gauge.clone());
103 ring.push(1);
104 ring.push(2);
105 assert_eq!(owner_gauge.get(), 2);
106
107 let mut snapshot = ring.clone();
109 assert_eq!(snapshot.len(), 2);
110 snapshot.push(3);
111 snapshot.push(4);
112 assert_eq!(snapshot.len(), 4);
114 assert_eq!(
115 owner_gauge.get(),
116 2,
117 "owner gauge corrupted by a push to a snapshot clone"
118 );
119 }
120
121 #[test]
122 fn zero_capacity_stores_nothing_and_hands_item_back() {
123 let gauge = SizeGauge::new();
124 let mut ring: Ring<u32> = Ring::with_capacity(0, gauge.clone());
125 assert_eq!(ring.push(7), Some(7));
126 assert_eq!(ring.len(), 0);
127 assert!(ring.is_empty());
128 assert_eq!(gauge.get(), 0);
129 }
130}