Skip to main content

ps_uuid/methods/
new_v4.rs

1use rand::RngCore;
2
3use crate::UUID;
4
5impl UUID {
6    /// Generates a random (v4) UUID using the provided random number generator.
7    #[must_use]
8    pub fn new_v4<R: RngCore + ?Sized>(rng: &mut R) -> Self {
9        let mut uuid = Self::nil();
10
11        rng.fill_bytes(&mut uuid.bytes);
12
13        uuid.with_version(4)
14    }
15}
16
17#[cfg(test)]
18mod tests {
19    use std::collections::HashSet;
20
21    use rand::{rngs::StdRng, SeedableRng};
22
23    use crate::UUID;
24
25    #[test]
26    fn version_and_variant_are_set() {
27        let mut rng = StdRng::seed_from_u64(42);
28        for _ in 0..100 {
29            let uuid = UUID::new_v4(&mut rng);
30            // Version: high nibble of byte 6 must be 0b0100
31            assert_eq!(uuid.bytes[6] >> 4, 0b0100, "Version must be 4");
32            // Variant: two MSBs of byte 8 must be 0b10
33            assert_eq!(
34                uuid.bytes[8] & 0b1100_0000,
35                0b1000_0000,
36                "Variant must be RFC 4122"
37            );
38        }
39    }
40
41    #[test]
42    fn produces_unique_uuids() {
43        let mut rng = StdRng::seed_from_u64(12345);
44        let mut set = HashSet::new();
45        for _ in 0..1000 {
46            let uuid = UUID::new_v4(&mut rng);
47            assert!(set.insert(uuid.bytes), "Duplicate UUID generated!");
48        }
49    }
50
51    #[test]
52    fn all_other_bits_are_random() {
53        let mut rng = StdRng::seed_from_u64(98765);
54        let mut seen = [0u8; 16];
55        let mut seen_inv = [0xFFu8; 16];
56
57        for _ in 0..1000 {
58            let uuid = UUID::new_v4(&mut rng);
59            for i in 0..16 {
60                seen[i] |= uuid.bytes[i];
61                seen_inv[i] &= uuid.bytes[i];
62            }
63        }
64
65        // Byte 6: lower 4 bits are random, upper 4 bits are version
66        assert_ne!(
67            seen[6] & 0x0F,
68            0,
69            "At least one lower bit in byte 6 should be 1"
70        );
71        assert_ne!(
72            seen_inv[6] & 0x0F,
73            0x0F,
74            "At least one lower bit in byte 6 should be 0"
75        );
76        // Byte 8: lower 6 bits are random, upper 2 bits are variant
77        assert_ne!(
78            seen[8] & 0x3F,
79            0,
80            "At least one lower bit in byte 8 should be 1"
81        );
82        assert_ne!(
83            seen_inv[8] & 0x3F,
84            0x3F,
85            "At least one lower bit in byte 8 should be 0"
86        );
87        // All other bytes: all bits are random
88        for i in 0..16 {
89            if i == 6 {
90                // upper 4 bits fixed
91                assert_eq!(seen[6] & 0xF0, 0x40, "Version bits must be set to 4");
92            } else if i == 8 {
93                // upper 2 bits fixed
94                assert_eq!(seen[8] & 0xC0, 0x80, "Variant bits must be set to RFC 4122");
95            } else {
96                assert_ne!(seen[i], 0, "At least one bit in byte {i} should be 1");
97                assert_ne!(
98                    seen_inv[i], 0xFF,
99                    "At least one bit in byte {i} should be 0"
100                );
101            }
102        }
103    }
104
105    #[test]
106    fn version_and_variant_methods_report_correctly() {
107        let mut rng = StdRng::seed_from_u64(5555);
108        let uuid = UUID::new_v4(&mut rng);
109        assert_eq!(uuid.get_version(), Some(4));
110        // If you have a variant() method, you can check it here as well.
111    }
112
113    #[test]
114    fn deterministic_with_seeded_rng() {
115        let mut rng1 = StdRng::seed_from_u64(1);
116        let mut rng2 = StdRng::seed_from_u64(1);
117
118        let uuid1 = UUID::new_v4(&mut rng1);
119        let uuid2 = UUID::new_v4(&mut rng2);
120
121        assert_eq!(
122            uuid1.bytes, uuid2.bytes,
123            "Same seed should produce same UUID"
124        );
125    }
126}