Skip to main content

ps_uuid/methods/
gen_v6.rs

1use crate::{UuidConstructionError, STATE, UUID};
2use std::time::SystemTime;
3
4impl UUID {
5    /// Generate an RFC 4122 version-6 (time-ordered) UUID.
6    ///
7    /// The current system time together with the process-wide `NodeId`
8    /// and clock sequence held in the global `STATE` are used.
9    ///
10    /// # Errors
11    /// - `TimestampBeforeEpoch` if the current time predates 1582-10-15.
12    /// - `TimestampOverflow`    if the current time exceeds 5236-03-31.
13    pub fn gen_v6() -> Result<Self, UuidConstructionError> {
14        let mut guard = STATE.lock();
15
16        let (timestamp, clock_seq) = guard.next(SystemTime::now());
17        let node_id = guard.node_id;
18
19        drop(guard);
20
21        Self::new_v6(timestamp, clock_seq, *node_id)
22    }
23}
24
25#[cfg(test)]
26mod tests {
27    #![allow(clippy::expect_used)]
28    use super::*;
29    use std::{
30        collections::HashSet,
31        sync::{Arc, Mutex},
32        thread,
33    };
34
35    /// Extract the RFC 4122 version (upper 4 bits of byte 6).
36    const fn check_version(bytes: &[u8; 16]) -> u8 {
37        bytes[6] >> 4
38    }
39
40    /// Extract the RFC 4122 variant (upper 2 bits of byte 8).
41    const fn check_variant(bytes: &[u8; 16]) -> u8 {
42        bytes[8] >> 6
43    }
44
45    #[test]
46    fn gen_v6_produces_valid_rfc4122_id() {
47        let uuid = UUID::gen_v6().expect("generation must succeed");
48        let bytes = uuid.as_bytes();
49
50        assert_eq!(
51            check_version(bytes),
52            0b0110,
53            "high-order nibble of byte 6 must equal version 6"
54        );
55        assert_eq!(
56            check_variant(bytes),
57            0b10,
58            "high-order two bits of byte 8 must equal the RFC 4122 variant"
59        );
60    }
61
62    /// With a reasonable sample size we should observe no duplicates.
63    #[test]
64    fn gen_v6_is_unique() {
65        const N: usize = 10_000;
66
67        let mut set = HashSet::with_capacity(N);
68
69        for _ in 0..N {
70            let id = UUID::gen_v6().expect("generation must succeed").to_string();
71            assert!(
72                set.insert(id),
73                "duplicate UUID generated – monotonicity/clock-seq buggy?"
74            );
75        }
76    }
77
78    /// Ensure the generator is `Send + Sync` and collision-free when
79    /// hammered from several threads at once.
80    #[test]
81    fn gen_v6_thread_safety_and_uniqueness() {
82        const THREADS: usize = 8;
83        const PER_THREAD: usize = 2_000;
84
85        let global: Arc<Mutex<HashSet<UUID>>> =
86            Arc::new(Mutex::new(HashSet::with_capacity(THREADS * PER_THREAD)));
87
88        let mut handles = Vec::with_capacity(THREADS);
89        for _ in 0..THREADS {
90            let global = Arc::clone(&global);
91            handles.push(thread::spawn(move || {
92                for _ in 0..PER_THREAD {
93                    let id = UUID::gen_v6().expect("generation should succeed");
94                    let mut guard = global.lock().expect("state mutex should not be poisoned");
95                    assert!(guard.insert(id), "duplicate across threads");
96                    drop(guard);
97                }
98            }));
99        }
100
101        for h in handles {
102            h.join().expect("thread panicked");
103        }
104    }
105}