use std::time::{SystemTime, UNIX_EPOCH};
pub const TRAILER: &str = "Stable-Commit-Id";
const CROCKFORD: &[u8; 32] = b"0123456789abcdefghjkmnpqrstvwxyz";
pub fn new_id() -> String {
encode(&random_bytes())
}
fn random_bytes() -> [u8; 16] {
use std::io::Read;
let mut out = [0u8; 16];
if let Ok(mut f) = std::fs::File::open("/dev/urandom") {
if f.read_exact(&mut out).is_ok() {
return out;
}
}
fallback_random()
}
fn fallback_random() -> [u8; 16] {
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
static COUNTER: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
let mut h = DefaultHasher::new();
SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0)
.hash(&mut h);
std::process::id().hash(&mut h);
COUNTER
.fetch_add(1, std::sync::atomic::Ordering::Relaxed)
.hash(&mut h);
let a = h.finish();
h.write_u64(a);
let b = h.finish();
let mut out = [0u8; 16];
out[..8].copy_from_slice(&a.to_be_bytes());
out[8..].copy_from_slice(&b.to_be_bytes());
out
}
fn encode(bytes: &[u8]) -> String {
let mut acc: u128 = 0;
for b in bytes {
acc = (acc << 8) | *b as u128;
}
let mut chars = [b'0'; 26];
for i in (0..26).rev() {
chars[i] = CROCKFORD[(acc & 31) as usize];
acc >>= 5;
}
format!("q-{}", std::str::from_utf8(&chars).unwrap())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ids_are_unique_wellformed_and_prefix_diverse() {
let ids: Vec<String> = (0..64).map(|_| new_id()).collect();
for id in &ids {
assert_eq!(id.len(), 28, "{id}");
assert!(id.starts_with("q-"));
assert!(id[2..].bytes().all(|c| CROCKFORD.contains(&c)), "{id}");
}
let unique: std::collections::HashSet<&String> = ids.iter().collect();
assert_eq!(unique.len(), ids.len(), "ids must be unique");
let prefixes: std::collections::HashSet<&str> = ids.iter().map(|i| &i[..10]).collect();
assert_eq!(prefixes.len(), ids.len(), "prefixes collided: {ids:?}");
}
}