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