ps_uuid/methods/
gen_v7.rs1use std::time::{SystemTime, UNIX_EPOCH};
2
3use rand::random;
4
5use crate::{UuidConstructionError, STATE, UUID};
6
7impl UUID {
8 pub fn gen_v7() -> Result<Self, UuidConstructionError> {
24 let timestamp = {
26 let mut guard = STATE.lock();
27 let ts = guard.next_v7(SystemTime::now());
28 drop(guard);
29 ts
30 };
31
32 let duration = timestamp
34 .duration_since(UNIX_EPOCH)
35 .map_err(|_| UuidConstructionError::TimestampBeforeEpoch)?;
36
37 #[allow(clippy::items_after_statements)]
38 const MAX_MILLIS: u128 = 1u128 << 48; if duration.as_millis() >= MAX_MILLIS {
40 return Err(UuidConstructionError::TimestampOverflow);
41 }
42
43 let random_bytes: [u8; 8] = random();
45
46 Ok(Self::new_v7(duration, random_bytes))
48 }
49}
50
51#[cfg(test)]
52mod tests {
53 #![allow(clippy::expect_used)]
54 use super::*;
55 use std::{
56 collections::HashSet,
57 sync::{Arc, Mutex},
58 thread,
59 };
60
61 const fn version(b: &[u8; 16]) -> u8 {
62 b[6] >> 4
63 }
64 const fn variant(b: &[u8; 16]) -> u8 {
65 b[8] >> 6
66 }
67
68 #[test]
69 fn gen_v7_produces_valid_uuid() {
70 let uuid = UUID::gen_v7().expect("generation must succeed");
71 let bytes = uuid.as_bytes();
72 assert_eq!(version(bytes), 0b0111);
73 assert_eq!(variant(bytes), 0b10);
74 }
75
76 #[test]
77 fn gen_v7_uniqueness_single_thread() {
78 const N: usize = 10_000;
79 let mut seen = HashSet::with_capacity(N);
80 for _ in 0..N {
81 let s = UUID::gen_v7().expect("generation must succeed").to_string();
82 assert!(seen.insert(s), "duplicate UUID generated");
83 }
84 }
85
86 #[test]
87 fn gen_v7_thread_safety_and_uniqueness() {
88 const THREADS: usize = 8;
89 const PER_THREAD: usize = 2_000;
90
91 let global: Arc<Mutex<HashSet<UUID>>> =
92 Arc::new(Mutex::new(HashSet::with_capacity(THREADS * PER_THREAD)));
93
94 let mut handles = Vec::with_capacity(THREADS);
95 for _ in 0..THREADS {
96 let global = Arc::clone(&global);
97 handles.push(thread::spawn(move || {
98 for _ in 0..PER_THREAD {
99 let id = UUID::gen_v7().expect("generation must succeed");
100 let mut guard = global.lock().expect("state mutex should not be poisoned");
101 assert!(guard.insert(id), "duplicate across threads");
102 drop(guard);
103 }
104 }));
105 }
106 for h in handles {
107 h.join().expect("thread panicked");
108 }
109 }
110}