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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use crate::{UuidConstructionError, UUID};
impl UUID {
/// Generate v2 UUID (DCE Security)
///
/// # Errors
/// - `TimestampBeforeEpoch` is returned if the current time predates 1582-10-15.
/// - `TimestampOverflow` is returned if the current time exceeds 5236-03-31.
pub fn gen_v2(domain: u8, local_id: u32) -> Result<Self, UuidConstructionError> {
let mut uuid = Self::gen_v1()?;
// Replace time_low with local_id (in big-endian order)
uuid.bytes[0..4].copy_from_slice(&local_id.to_be_bytes());
uuid.bytes[9] = domain;
Ok(uuid.with_version(2))
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::expect_used)]
use super::*;
// ---------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------
/// Convenience wrapper that panics on construction failure.
fn v2(domain: u8, local_id: u32) -> UUID {
UUID::gen_v2(domain, local_id).expect("failed to build v2 UUID")
}
/// Extract the 32-bit value stored in the first four bytes
/// (\( \text{time\_low} \) / Local-ID field).
fn local_id(u: &UUID) -> u32 {
u32::from_be_bytes(
u.bytes[0..4]
.try_into()
.expect("UUID timestamp slice should be exactly 4 bytes"),
)
}
/// True iff the RFC-4122 variant bits are `10`.
const fn is_rfc4122_variant(u: &UUID) -> bool {
(u.bytes[8] & 0b1100_0000) == 0b1000_0000
}
// ---------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------
#[test]
fn version_is_always_2() {
for &domain in &[0, 1, 2, 42] {
let u = v2(domain, 0xDEAD_BEEF);
assert_eq!(u.get_version(), Some(2));
}
}
#[test]
fn variant_bits_remain_rfc4122() {
let u = v2(1, 123);
assert!(is_rfc4122_variant(&u));
}
#[test]
fn local_id_is_encoded_big_endian() {
for &id in &[0, 1, 0x1234_5678, u32::MAX] {
let u = v2(2, id);
assert_eq!(local_id(&u), id);
}
}
#[test]
fn domain_is_written_to_clock_seq_low() {
for domain in 0u8..=10 {
let u = v2(domain, 7);
assert_eq!(u.bytes[9], domain);
}
}
#[test]
fn different_local_ids_produce_distinct_uuids() {
let u1 = v2(1, 0xAAAA_BBBB);
let u2 = v2(1, 0xCCCC_DDDD);
assert_ne!(
u1.bytes, u2.bytes,
"UUIDs must differ when the Local-ID field changes"
);
}
#[test]
fn different_domains_produce_distinct_uuids() {
let u1 = v2(0, 42);
let u2 = v2(7, 42);
assert_ne!(
u1.bytes, u2.bytes,
"UUIDs must differ when the Domain field changes"
);
}
}