use std::collections::hash_map::RandomState;
use std::hash::{BuildHasher, Hasher};
use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
pub struct MessageIdGenerator {
state: AtomicU64,
key: u64,
}
impl Default for MessageIdGenerator {
fn default() -> Self {
Self::new()
}
}
impl MessageIdGenerator {
pub fn new() -> Self {
let mut h = RandomState::new().build_hasher();
h.write_u128(
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos(),
);
h.write_u32(std::process::id());
let seed = h.finish();
let mut h2 = RandomState::new().build_hasher();
h2.write_u64(seed);
MessageIdGenerator {
state: AtomicU64::new(seed),
key: h2.finish() | 1,
}
}
fn next_u64(&self) -> u64 {
let z = self
.state
.fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed);
let mut z = z ^ self.key;
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
z ^ (z >> 31)
}
pub fn next_id(&self) -> String {
let hi = (self.next_u64() & 0xFFFF_FFFF_FFFF_0FFF) | 0x0000_0000_0000_4000;
let lo = (self.next_u64() & 0x3FFF_FFFF_FFFF_FFFF) | 0x8000_0000_0000_0000;
format!(
"{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
hi >> 32,
(hi >> 16) & 0xFFFF,
hi & 0xFFFF,
lo >> 48,
lo & 0xFFFF_FFFF_FFFF
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn unique_and_well_formed() {
let g = MessageIdGenerator::new();
let mut seen = HashSet::new();
for _ in 0..100_000 {
let id = g.next_id();
assert_eq!(id.len(), 36);
assert_eq!(&id[14..15], "4");
assert!(matches!(&id[19..20], "8" | "9" | "a" | "b"));
assert!(seen.insert(id));
}
}
}