use std::process;
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
static COUNTER: AtomicU64 = AtomicU64::new(0);
static PROCESS_SALT: AtomicU64 = AtomicU64::new(0);
pub fn unique_u64() -> u64 {
let pid = process::id() as u64;
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
let salt = process_salt();
let mixed = pid
.wrapping_mul(0x9E37_79B9_7F4A_7C15)
.wrapping_add(nanos.wrapping_mul(0xBF58_476D_1CE4_E5B9))
.wrapping_add(salt.wrapping_mul(0x94D0_49BB_1331_11EB))
^ counter.wrapping_mul(0xDA94_2042_E4DD_58B5);
stafford_mix13(mixed)
}
pub fn unique_name(len: usize) -> String {
encode_base32(len)
}
pub fn unique_base32(len: usize) -> String {
encode_base32(len)
}
pub fn unique_hex(len: usize) -> String {
const ALPHABET: &[u8; 16] = b"0123456789abcdef";
let mut out = String::with_capacity(len);
let mut state = unique_u64();
let mut bits_left = 64;
while out.len() < len {
if bits_left < 4 {
state = unique_u64();
bits_left = 64;
}
out.push(ALPHABET[(state & 0xF) as usize] as char);
state >>= 4;
bits_left -= 4;
}
out
}
fn encode_base32(len: usize) -> String {
const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
let mut out = String::with_capacity(len);
let mut state = unique_u64();
let mut bits_left = 64;
while out.len() < len {
if bits_left < 5 {
state = unique_u64();
bits_left = 64;
}
out.push(ALPHABET[(state & 0x1F) as usize] as char);
state >>= 5;
bits_left -= 5;
}
out
}
#[inline]
fn stafford_mix13(mut z: u64) -> u64 {
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
fn process_salt() -> u64 {
let current = PROCESS_SALT.load(Ordering::Relaxed);
if current != 0 {
return current;
}
let pid = process::id() as u64;
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos() as u64)
.unwrap_or(0);
let candidate = stafford_mix13(
pid.wrapping_mul(0x9E37_79B9_7F4A_7C15) ^ nanos.wrapping_mul(0xC2B2_AE3D_27D4_EB4F),
)
.max(1);
match PROCESS_SALT.compare_exchange(0, candidate, Ordering::Relaxed, Ordering::Relaxed) {
Ok(_) => candidate,
Err(existing) => existing,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn two_calls_differ() {
assert_ne!(unique_u64(), unique_u64());
}
#[test]
fn name_meets_exact_length() {
for len in [1, 4, 8, 16, 32, 64, 128] {
let n = unique_name(len);
assert_eq!(n.len(), len, "length {len}");
}
}
#[test]
fn name_uses_crockford_alphabet() {
let n = unique_name(256);
for c in n.chars() {
assert!(
c.is_ascii_digit() || c.is_ascii_uppercase(),
"char {c:?} outside Crockford alphabet"
);
assert!(!matches!(c, 'I' | 'L' | 'O' | 'U'), "ambiguous char {c}");
}
}
#[test]
fn names_are_unique_across_calls() {
let mut set = HashSet::with_capacity(10_000);
for _ in 0..10_000 {
assert!(set.insert(unique_name(16)));
}
}
#[test]
fn unique_u64_collision_free_at_scale() {
let n = 1_000_000;
let mut set = HashSet::with_capacity(n);
for _ in 0..n {
assert!(set.insert(unique_u64()));
}
assert_eq!(set.len(), n);
}
#[test]
fn unique_hex_exact_length_and_alphabet() {
for len in [1, 7, 8, 9, 16, 32, 64, 128] {
let s = unique_hex(len);
assert_eq!(s.len(), len, "length {len}");
assert!(s.chars().all(|c| c.is_ascii_hexdigit()));
}
}
#[test]
fn unique_base32_exact_length() {
for len in [1, 5, 8, 13, 32, 64] {
let s = unique_base32(len);
assert_eq!(s.len(), len);
}
}
#[test]
fn alphabet_distribution_is_reasonable() {
let sample: String = (0..100_000)
.map(|_| unique_base32(1).chars().next().unwrap())
.collect();
let mut counts = [0u32; 32];
const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
for c in sample.chars() {
let idx = ALPHABET
.iter()
.position(|&b| b as char == c)
.expect("char from Crockford alphabet");
counts[idx] += 1;
}
let n = sample.len() as f64;
let expected = n / 32.0;
let chi: f64 = counts
.iter()
.map(|&c| {
let diff = c as f64 - expected;
diff * diff / expected
})
.sum();
assert!(chi < 100.0, "chi-squared {chi} too high (alphabet skew)");
}
#[test]
fn hex_distribution_is_reasonable() {
let sample: String = unique_hex(100_000);
let mut counts = [0u32; 16];
for c in sample.chars() {
let v = c.to_digit(16).unwrap() as usize;
counts[v] += 1;
}
let n = sample.len() as f64;
let expected = n / 16.0;
let chi: f64 = counts
.iter()
.map(|&c| {
let diff = c as f64 - expected;
diff * diff / expected
})
.sum();
assert!(chi < 60.0, "chi-squared {chi} too high (hex skew)");
}
#[test]
fn zero_length_yields_empty_string() {
assert_eq!(unique_name(0), "");
assert_eq!(unique_hex(0), "");
assert_eq!(unique_base32(0), "");
}
#[test]
fn process_salt_is_stable_within_process() {
let a = process_salt();
let b = process_salt();
assert_eq!(a, b);
assert_ne!(a, 0);
}
}