radiate_utils/buff/
versioned.rs1use std::fmt::Debug;
2
3#[derive(Clone, Copy, Default, Debug, PartialEq, Eq)]
4struct Slot {
5 version: u64,
6 count: u32,
7}
8
9#[derive(Clone)]
20pub struct VersionedCounts {
21 buckets: Vec<Slot>,
22 current: u64,
23}
24
25impl VersionedCounts {
26 pub fn new() -> Self {
27 Self {
28 buckets: Vec::new(),
29 current: 0,
30 }
31 }
32
33 pub fn with_capacity(capacity: usize) -> Self {
34 let mut s = Self::new();
35 s.begin(capacity);
36 s
37 }
38
39 #[inline]
40 pub fn unique_count(&self) -> usize {
41 self.iter_live().count()
42 }
43
44 #[inline]
47 pub fn begin(&mut self, capacity: usize) {
48 if self.buckets.len() < capacity {
49 self.buckets.resize(capacity, Slot::default());
50 }
51 self.current = self.current.wrapping_add(1);
52 }
53
54 #[inline]
57 pub fn bump(&mut self, idx: usize) -> u32 {
58 let slot = &mut self.buckets[idx];
59 if slot.version != self.current {
60 slot.version = self.current;
61 slot.count = 1;
62 } else {
63 slot.count += 1;
64 }
65 slot.count
66 }
67
68 #[inline]
71 pub fn get(&self, idx: usize) -> u32 {
72 match self.buckets.get(idx) {
73 Some(s) if s.version == self.current => s.count,
74 _ => 0,
75 }
76 }
77
78 #[inline]
80 pub fn iter_live(&self) -> impl Iterator<Item = (usize, u32)> + '_ {
81 let cur = self.current;
82 self.buckets
83 .iter()
84 .enumerate()
85 .filter_map(move |(i, s)| (s.version == cur).then_some((i, s.count)))
86 }
87
88 #[inline]
92 pub fn iter_live_rev(&self) -> impl Iterator<Item = (usize, u32)> + '_ {
93 let cur = self.current;
94 let len = self.buckets.len();
95 (0..len).rev().filter_map(move |i| {
96 let s = &self.buckets[i];
97 (s.version == cur).then_some((i, s.count))
98 })
99 }
100
101 #[inline]
106 pub fn iter_pair_live_rev<'a>(
107 &'a self,
108 other: &'a Self,
109 ) -> impl Iterator<Item = (usize, u32, u32)> + 'a {
110 let cur_self = self.current;
111 let cur_other = other.current;
112 let len = self.buckets.len().max(other.buckets.len());
113 (0..len).rev().filter_map(move |i| {
114 let a = self
115 .buckets
116 .get(i)
117 .filter(|s| s.version == cur_self)
118 .map(|s| s.count)
119 .unwrap_or(0);
120 let b = other
121 .buckets
122 .get(i)
123 .filter(|s| s.version == cur_other)
124 .map(|s| s.count)
125 .unwrap_or(0);
126 if a == 0 && b == 0 {
127 None
128 } else {
129 Some((i, a, b))
130 }
131 })
132 }
133}
134
135impl Default for VersionedCounts {
136 fn default() -> Self {
137 Self::new()
138 }
139}
140
141impl Debug for VersionedCounts {
142 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
143 f.debug_struct("VersionedCounts")
144 .field("capacity", &self.buckets.len())
145 .field("current", &self.current)
146 .field("live", &self.iter_live().collect::<Vec<_>>())
147 .finish()
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn fresh_session_is_empty() {
157 let mut c = VersionedCounts::new();
158 c.begin(8);
159 assert_eq!(c.iter_live().count(), 0);
160 }
161
162 #[test]
163 fn bump_counts_per_session() {
164 let mut c = VersionedCounts::new();
165 c.begin(8);
166 assert_eq!(c.bump(3), 1);
167 assert_eq!(c.bump(3), 2);
168 assert_eq!(c.bump(5), 1);
169 assert_eq!(c.bump(3), 3);
170
171 let mut live: Vec<_> = c.iter_live().collect();
172 live.sort_by_key(|(i, _)| *i);
173 assert_eq!(live, vec![(3, 3), (5, 1)]);
174 }
175
176 #[test]
177 fn begin_clears_previous_session() {
178 let mut c = VersionedCounts::new();
179 c.begin(8);
180 c.bump(3);
181 c.bump(7);
182 assert_eq!(c.iter_live().count(), 2);
183
184 c.begin(8);
185 assert_eq!(c.iter_live().count(), 0);
186
187 c.bump(2);
188 let live: Vec<_> = c.iter_live().collect();
189 assert_eq!(live, vec![(2, 1)]);
190 }
191
192 #[test]
193 fn iter_live_rev_yields_descending() {
194 let mut c = VersionedCounts::new();
195 c.begin(10);
196 c.bump(7);
197 c.bump(2);
198 c.bump(5);
199 c.bump(2);
200
201 let rev: Vec<_> = c.iter_live_rev().collect();
202 assert_eq!(rev, vec![(7, 1), (5, 1), (2, 2)]);
203 }
204
205 #[test]
206 fn begin_grows_capacity_but_keeps_history() {
207 let mut c = VersionedCounts::new();
208 c.begin(4);
209 c.bump(0);
210 c.begin(16);
211 assert_eq!(c.iter_live().count(), 0);
212 c.bump(10);
213 assert_eq!(c.iter_live().collect::<Vec<_>>(), vec![(10, 1)]);
214 }
215
216 #[test]
217 fn iter_pair_live_rev_yields_union_descending() {
218 let mut a = VersionedCounts::new();
219 let mut b = VersionedCounts::new();
220 a.begin(10);
221 b.begin(10);
222
223 a.bump(5);
225 a.bump(5);
226 a.bump(7);
227 b.bump(5);
228 b.bump(2);
229 b.bump(2);
230 b.bump(2);
231
232 let pairs: Vec<_> = a.iter_pair_live_rev(&b).collect();
233 assert_eq!(pairs, vec![(7, 1, 0), (5, 2, 1), (2, 0, 3)]);
234 }
235
236 #[test]
237 fn iter_pair_live_rev_handles_stale_other() {
238 let mut a = VersionedCounts::new();
239 let mut b = VersionedCounts::new();
240 a.begin(8);
241 b.begin(8);
242 a.bump(4);
243 b.bump(4);
244
245 b.begin(8);
247
248 let pairs: Vec<_> = a.iter_pair_live_rev(&b).collect();
249 assert_eq!(pairs, vec![(4, 1, 0)]);
250 }
251}