use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
thread_local! {
static RNG: Cell<u64> = Cell::new(seed());
}
const HEX: &[u8; 16] = b"0123456789abcdef";
pub fn new_uuid_v4() -> String {
let seq = COUNTER.fetch_add(1, Ordering::Relaxed);
let r0 = next_u64();
let r1 = next_u64() ^ seq.rotate_left(17);
let mut bytes = [0u8; 16];
for (i, b) in bytes[..8].iter_mut().enumerate() {
*b = (r0 >> (i * 8)) as u8;
}
for (i, b) in bytes[8..].iter_mut().enumerate() {
*b = (r1 >> (i * 8)) as u8;
}
bytes[6] = (bytes[6] & 0x0f) | 0x40;
bytes[8] = (bytes[8] & 0x3f) | 0x80;
format_uuid(&bytes)
}
fn format_uuid(b: &[u8; 16]) -> String {
let mut s = String::with_capacity(36);
for (i, byte) in b.iter().enumerate() {
if i == 4 || i == 6 || i == 8 || i == 10 {
s.push('-');
}
s.push(HEX[(byte >> 4) as usize] as char);
s.push(HEX[(byte & 0x0f) as usize] as char);
}
s
}
fn seed() -> u64 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0x9E3779B97F4A7C15);
splitmix64(nanos)
}
fn next_u64() -> u64 {
RNG.with(|cell| {
let mut x = cell.get();
if x == 0 {
x = seed();
}
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
cell.set(x);
x
})
}
fn splitmix64(mut z: u64) -> u64 {
z = z.wrapping_add(0x9E3779B97F4A7C15);
z = (z ^ (z >> 30)).wrapping_mul(0xBF58476D1CE4E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D049BB133111EB);
z ^ (z >> 31)
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn test_uuid_format() {
let id = new_uuid_v4();
assert_eq!(id.len(), 36, "UUID must be 36 chars");
let parts: Vec<&str> = id.split('-').collect();
assert_eq!(parts.len(), 5);
assert_eq!(parts[0].len(), 8);
assert_eq!(parts[1].len(), 4);
assert_eq!(parts[2].len(), 4);
assert_eq!(parts[3].len(), 4);
assert_eq!(parts[4].len(), 12);
}
#[test]
fn test_uuid_version_and_variant() {
for _ in 0..1000 {
let id = new_uuid_v4();
let bytes = id.as_bytes();
assert_eq!(bytes[14], b'4', "version nibble must be 4: {}", id);
assert!(
matches!(bytes[19], b'8' | b'9' | b'a' | b'b'),
"variant bits invalid: {}",
id
);
}
}
#[test]
fn test_uuid_uniqueness() {
let mut seen = HashSet::new();
for _ in 0..100_000 {
assert!(seen.insert(new_uuid_v4()), "duplicate id generated");
}
}
}