Skip to main content

ps_uuid/methods/
from_parts_v3.rs

1use crate::UUID;
2
3// ────────────────────────────────────────────────────────────────────────────
4// v3 constructor from a pre-computed MD5 digest
5// ────────────────────────────────────────────────────────────────────────────
6
7impl UUID {
8    /// Builds an RFC-4122 Version-3 UUID from a raw 16-byte MD5 digest.
9    ///
10    /// The caller must supply the digest that results from hashing
11    /// `namespace.bytes || name`.  The function overwrites the version and
12    /// variant fields via `.with_version(3)` and returns the finished UUID.
13    #[must_use]
14    pub const fn from_parts_v3(digest: [u8; 16]) -> Self {
15        Self { bytes: digest }.with_version(3)
16    }
17}
18
19// ────────────────────────────────────────────────────────────────────────────
20// Tests
21// ────────────────────────────────────────────────────────────────────────────
22#[cfg(test)]
23mod tests {
24    #![allow(clippy::cast_possible_truncation, clippy::expect_used)]
25    use std::str::FromStr;
26
27    use crate::{md5, UUID};
28
29    // Helper: RFC-4122 variant check (two MSBs = 10)
30    const fn is_rfc4122_variant(b: u8) -> bool {
31        (b & 0b1100_0000) == 0b1000_0000
32    }
33
34    #[test]
35    fn sets_version_and_variant_correctly() {
36        for &input in &[[0u8; 16], [0xFFu8; 16]] {
37            let uuid = UUID::from_parts_v3(input);
38            assert_eq!(uuid.get_version(), Some(3));
39            assert!(is_rfc4122_variant(uuid.bytes[8]));
40        }
41    }
42
43    #[test]
44    fn preserves_all_other_bits() {
45        let mut digest = [0u8; 16];
46
47        for (i, item) in digest.iter_mut().enumerate() {
48            *item = i as u8;
49        }
50
51        let uuid = UUID::from_parts_v3(digest);
52
53        for i in 0..16 {
54            match i {
55                // Upper nibble of byte 6 carries the version.
56                6 => assert_eq!(uuid.bytes[6] & 0x0F, digest[6] & 0x0F),
57                // Upper two bits of byte 8 carry the variant.
58                8 => assert_eq!(uuid.bytes[8] & 0x3F, digest[8] & 0x3F),
59                _ => assert_eq!(uuid.bytes[i], digest[i]),
60            }
61        }
62    }
63
64    #[test]
65    fn matches_gen_v3_reference_implementation() {
66        // Reference data from RFC 4122, Appendix C:
67        // namespace = DNS (6ba7b810-9dad-11d1-80b4-00c04fd430c8)
68        // name      = "python.org"
69        let ns = UUID::from_str("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
70            .expect("failed to parse namespace UUID test vector");
71        let name = b"python.org";
72
73        // v3 via the public constructor
74        let via_api = UUID::new_v3(&ns, name);
75
76        // Compute digest directly and call `from_parts_v3`
77        let digest = md5(&[&ns.bytes[..], name].concat());
78        let via_parts = UUID::from_parts_v3(digest);
79
80        assert_eq!(via_parts.bytes, via_api.bytes);
81    }
82}