Skip to main content

ps_uuid/methods/
gen_v4.rs

1use rand::fill;
2
3use crate::UUID;
4
5impl UUID {
6    /// Generates a random (v4) UUID.
7    #[must_use]
8    pub fn gen_v4() -> Self {
9        let mut uuid = Self::nil();
10
11        fill(&mut uuid.bytes);
12
13        uuid.with_version(4)
14    }
15}
16
17#[cfg(test)]
18mod tests {
19    use super::UUID;
20    use std::collections::HashSet;
21
22    #[test]
23    fn version_and_variant_are_set() {
24        for _ in 0..100 {
25            let uuid = UUID::gen_v4();
26            // Version: high nibble of byte 6 must be 0b0100
27            assert_eq!(uuid.bytes[6] >> 4, 0b0100, "Version must be 4");
28            // Variant: two MSBs of byte 8 must be 0b10
29            assert_eq!(
30                uuid.bytes[8] & 0b1100_0000,
31                0b1000_0000,
32                "Variant must be RFC 4122"
33            );
34        }
35    }
36
37    #[test]
38    fn randomness_produces_unique_uuids() {
39        let mut set = HashSet::new();
40        for _ in 0..1000 {
41            let uuid = UUID::gen_v4();
42            assert!(set.insert(uuid.bytes), "Duplicate UUID generated!");
43        }
44    }
45
46    #[test]
47    fn all_other_bits_are_random() {
48        // Generate a bunch of UUIDs and check that at least one bit in each
49        // non-fixed field is both 0 and 1 across the sample.
50        let mut seen = [0u8; 16];
51        let mut seen_inv = [0xFFu8; 16];
52
53        for _ in 0..1000 {
54            let uuid = UUID::gen_v4();
55            for i in 0..16 {
56                seen[i] |= uuid.bytes[i];
57                seen_inv[i] &= uuid.bytes[i];
58            }
59        }
60
61        // Byte 6: lower 4 bits are random, upper 4 bits are version
62        assert_ne!(
63            seen[6] & 0x0F,
64            0,
65            "At least one lower bit in byte 6 should be 1"
66        );
67        assert_ne!(
68            seen_inv[6] & 0x0F,
69            0x0F,
70            "At least one lower bit in byte 6 should be 0"
71        );
72        // Byte 8: lower 6 bits are random, upper 2 bits are variant
73        assert_ne!(
74            seen[8] & 0x3F,
75            0,
76            "At least one lower bit in byte 8 should be 1"
77        );
78        assert_ne!(
79            seen_inv[8] & 0x3F,
80            0x3F,
81            "At least one lower bit in byte 8 should be 0"
82        );
83        // All other bytes: all bits are random
84        for i in 0..16 {
85            if i == 6 {
86                // upper 4 bits fixed
87                assert_eq!(seen[6] & 0xF0, 0x40, "Version bits must be set to 4");
88            } else if i == 8 {
89                // upper 2 bits fixed
90                assert_eq!(seen[8] & 0xC0, 0x80, "Variant bits must be set to RFC 4122");
91            } else {
92                assert_ne!(seen[i], 0, "At least one bit in byte {i} should be 1");
93                assert_ne!(
94                    seen_inv[i], 0xFF,
95                    "At least one bit in byte {i} should be 0"
96                );
97            }
98        }
99    }
100
101    #[test]
102    fn version_and_variant_methods_report_correctly() {
103        let uuid = UUID::gen_v4();
104        assert_eq!(uuid.get_version(), Some(4));
105        // If you have a variant() method, you can check it here as well.
106    }
107}