fast_uuid_v7/
sequential.rs1use rand::rngs::SmallRng;
7use rand::{Rng, SeedableRng};
8
9use crate::{clock, format_uuid, uuid_v7_from_parts, UuidString, COUNTER_MAX, COUNTER_SEED_MASK};
10
11pub struct SequentialGenerator {
64 rng: SmallRng,
65 clock: clock::Clock,
66 last_ms: u64,
67 counter: u32,
68}
69
70impl SequentialGenerator {
71 #[must_use]
72 pub fn new() -> Self {
73 Self {
74 rng: SmallRng::from_rng(&mut rand::rng()),
75 clock: clock::Clock::new(),
76 last_ms: 0,
77 counter: 0,
78 }
79 }
80
81 #[inline(always)]
85 fn seed_counter(&mut self) -> u32 {
86 self.rng.next_u32() & COUNTER_SEED_MASK
87 }
88
89 #[inline]
91 #[must_use]
92 pub fn next_id(&mut self) -> u128 {
93 let advanced = if self.last_ms == 0 || self.clock.should_refresh() {
94 let sample = self.clock.refresh_timestamp();
95 if sample.ms > self.last_ms {
98 self.last_ms = sample.ms;
99 self.counter = self.seed_counter();
100 true
101 } else {
102 false
103 }
104 } else {
105 false
106 };
107
108 if !advanced {
109 if self.counter >= COUNTER_MAX {
110 self.last_ms += 1;
111 self.counter = self.seed_counter();
112 } else {
113 self.counter += 1;
114 }
115 debug_assert!(self.last_ms < (1 << 48), "timestamp exceeded 48 bits");
119 }
120
121 let rand_a = ((self.counter >> 6) & 0x0FFF) as u16;
124 let rand_b_high = (self.counter & 0x3F) as u64;
125 let rand_b_low = self.rng.next_u64() & 0x00FF_FFFF_FFFF_FFFF;
126
127 uuid_v7_from_parts(self.last_ms, rand_a, (rand_b_high << 56) | rand_b_low)
128 }
129
130 #[inline]
132 #[must_use]
133 pub fn next_id_str(&mut self) -> UuidString {
134 format_uuid(self.next_id())
135 }
136
137 #[inline]
139 #[must_use]
140 pub fn next_id_string(&mut self) -> String {
141 self.next_id_str().to_string()
142 }
143}
144
145impl Default for SequentialGenerator {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151impl Iterator for SequentialGenerator {
154 type Item = u128;
155
156 #[inline]
157 fn next(&mut self) -> Option<u128> {
158 Some(self.next_id())
159 }
160
161 #[inline]
162 fn size_hint(&self) -> (usize, Option<usize>) {
163 (usize::MAX, None)
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn ids_are_strictly_increasing() {
173 let mut gen = SequentialGenerator::new();
174 let mut previous = 0u128;
175 for _ in 0..1_000_000 {
176 let id = gen.next_id();
177 assert!(id > previous);
178 previous = id;
179 }
180 }
181
182 #[test]
183 fn strings_sort_like_the_ids() {
184 let mut gen = SequentialGenerator::new();
185 let ids: Vec<_> = (0..10_000).map(|_| gen.next_id_str()).collect();
186 let mut sorted = ids.clone();
187 sorted.sort();
188 assert_eq!(ids, sorted);
189 }
190
191 #[test]
192 fn counter_exhaustion_advances_timestamp() {
193 let mut gen = SequentialGenerator::new();
194 let first = gen.next_id();
195 gen.last_ms = 0xF000_0000_0000;
198 gen.counter = COUNTER_MAX;
199 let ms_before = gen.last_ms;
200 let second = gen.next_id();
201 assert_eq!(gen.last_ms, ms_before + 1);
202 assert!(gen.counter <= COUNTER_SEED_MASK);
203 assert!(second > first);
204 }
205
206 #[test]
207 fn counter_is_reseeded_on_a_new_millisecond() {
208 let mut gen = SequentialGenerator::new();
209 let first = gen.next_id();
210 let ms_before = gen.last_ms;
211 gen.counter = COUNTER_SEED_MASK + 1000;
214 std::thread::sleep(std::time::Duration::from_millis(2));
215 let second = gen.next_id();
216 assert!(gen.last_ms > ms_before);
217 assert!(gen.counter <= COUNTER_SEED_MASK);
218 assert!(second > first);
219 }
220
221 #[test]
222 fn instances_do_not_share_one_counter_sequence() {
223 let counters: Vec<u32> = (0..8)
224 .map(|_| {
225 let mut gen = SequentialGenerator::new();
226 let _ = gen.next_id();
227 gen.counter
228 })
229 .collect();
230 assert!(counters.iter().all(|c| *c <= COUNTER_SEED_MASK));
231 assert!(counters.iter().any(|c| *c != counters[0]));
233 }
234
235 #[test]
236 fn version_and_variant_survive_counter_rollover() {
237 let mut gen = SequentialGenerator::new();
238 gen.last_ms = 0xF000_0000_0000;
241 let mut previous = 0u128;
242 for _ in 0..1000 {
243 gen.counter = COUNTER_MAX;
244 let id = gen.next_id();
245 assert_eq!((id >> 76) & 0xF, 7);
246 assert_eq!((id >> 62) & 0x3, 0b10);
247 assert!(id > previous);
248 previous = id;
249 }
250 }
251
252 #[test]
253 fn iterator_yields_the_same_increasing_sequence() {
254 let mut gen = SequentialGenerator::new();
255 let ids: Vec<u128> = gen.by_ref().take(10_000).collect();
256 assert_eq!(ids.len(), 10_000);
257 assert!(ids.windows(2).all(|pair| pair[0] < pair[1]));
258 assert!(gen.next_id() > ids[ids.len() - 1]);
259 }
260
261 #[test]
262 fn next_id_string_matches_next_id_str() {
263 let mut gen = SequentialGenerator::new();
264 let owned = gen.next_id_string();
265 assert_eq!(owned.len(), 36);
266 assert!(owned.as_str() < gen.next_id_str().as_str());
267 }
268
269 #[test]
270 fn backwards_clock_does_not_break_ordering() {
271 let mut gen = SequentialGenerator::new();
272 let first = gen.next_id();
273 gen.last_ms += 60_000;
276 let mut previous = gen.next_id();
277 assert!(previous > first);
278 for _ in 0..10_000 {
279 let id = gen.next_id();
280 assert!(id > previous);
281 previous = id;
282 }
283 }
284
285 #[test]
286 fn timestamp_matches_wall_clock() {
287 let now_ms = std::time::SystemTime::now()
288 .duration_since(std::time::UNIX_EPOCH)
289 .unwrap()
290 .as_millis() as u64;
291 let id = SequentialGenerator::new().next_id();
292 let timestamp = (id >> 80) as u64;
293 assert!(timestamp.abs_diff(now_ms) < 1000);
294 }
295
296 #[test]
297 fn instances_are_independent() {
298 let (mut first, mut second) = (SequentialGenerator::new(), SequentialGenerator::new());
299 assert_ne!(first.next_id(), second.next_id());
300 }
301
302 #[test]
303 fn string_and_u128_orderings_agree() {
304 let mut gen = SequentialGenerator::new();
305 let ids: Vec<u128> = (0..10_000).map(|_| gen.next_id()).collect();
306 for pair in ids.windows(2) {
307 assert!(format_uuid(pair[0]).as_str() < format_uuid(pair[1]).as_str());
308 }
309 }
310
311 #[test]
312 fn version_and_variant_are_preserved() {
313 let mut gen = SequentialGenerator::new();
314 for _ in 0..1000 {
315 let id = gen.next_id();
316 assert_eq!((id >> 76) & 0xF, 7);
317 assert_eq!((id >> 62) & 0x3, 0b10);
318 }
319 }
320}