use std::collections::hash_map::RandomState;
use std::hash::BuildHasher;
use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::OnceLock;
use std::time::{SystemTime, UNIX_EPOCH};
const CROCKFORD_LOWER: &[u8; 32] = b"0123456789abcdefghjkmnpqrstvwxyz";
static COUNTER: AtomicU64 = AtomicU64::new(0);
fn hash_states() -> &'static (RandomState, RandomState) {
static STATES: OnceLock<(RandomState, RandomState)> = OnceLock::new();
STATES.get_or_init(|| (RandomState::new(), RandomState::new()))
}
fn entropy80() -> u128 {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or_default();
let count = COUNTER.fetch_add(1, Ordering::Relaxed);
let pid = std::process::id();
let (a, b) = hash_states();
let hi = a.hash_one((nanos, count, pid));
let lo = b.hash_one((count, pid, nanos));
(((hi as u128) << 64) | (lo as u128)) & ((1u128 << 80) - 1)
}
pub fn mint() -> String {
let ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or_default()
& 0xFFFF_FFFF_FFFF; encode(((ms as u128) << 80) | entropy80())
}
fn encode(value: u128) -> String {
let mut out = String::with_capacity(26);
for i in 0..26 {
let shift = 125 - 5 * i;
out.push(CROCKFORD_LOWER[((value >> shift) & 0x1F) as usize] as char);
}
out
}
pub fn is_ulid(s: &str) -> bool {
let bytes = s.as_bytes();
bytes.len() == 26
&& matches!(bytes[0], b'0'..=b'7')
&& bytes.iter().all(|b| CROCKFORD_LOWER.contains(b))
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashSet;
#[test]
fn mint_is_wellformed_lowercase_ulid() {
for _ in 0..100 {
let id = mint();
assert!(is_ulid(&id), "minted id {id:?} is not a lowercase ULID");
assert_eq!(id.len(), 26);
assert_eq!(id, id.to_lowercase());
}
}
#[test]
fn mint_is_unique_across_a_burst() {
let ids: HashSet<String> = (0..10_000).map(|_| mint()).collect();
assert_eq!(ids.len(), 10_000, "duplicate ULIDs in a same-ms burst");
}
#[test]
fn mint_is_time_sortable_across_ms_boundaries() {
let a = mint();
std::thread::sleep(std::time::Duration::from_millis(3));
let b = mint();
assert!(a < b, "ULIDs did not time-sort: {a} !< {b}");
}
#[test]
fn timestamp_prefix_decodes_to_now() {
let before_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let id = mint();
let after_ms = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
let mut ts: u64 = 0;
for b in id.as_bytes().iter().take(10) {
let digit = CROCKFORD_LOWER.iter().position(|c| c == b).unwrap() as u64;
ts = (ts << 5) | digit;
}
assert!(
(before_ms..=after_ms).contains(&ts),
"decoded ts {ts} outside [{before_ms}, {after_ms}]"
);
}
#[test]
fn is_ulid_accepts_only_canonical_lowercase() {
assert!(is_ulid("01j5qc3v9k4ym8rwbn2tqe6f7d"));
assert!(is_ulid("00000000000000000000000000"));
assert!(is_ulid("7zzzzzzzzzzzzzzzzzzzzzzzzz")); assert!(!is_ulid(""));
assert!(!is_ulid("01j5qc3v9k4ym8rwbn2tqe6f7"));
assert!(!is_ulid("01j5qc3v9k4ym8rwbn2tqe6f7dd"));
assert!(!is_ulid("01J5QC3V9K4YM8RWBN2TQE6F7D"));
assert!(!is_ulid("01j5qc3v9k4ym8rwbn2tqe6fil"));
assert!(!is_ulid("01j5qc3v9k4ym8rwbn2tqe6f7-"));
assert!(!is_ulid("8zzzzzzzzzzzzzzzzzzzzzzzzz"));
assert!(!is_ulid("sarah-chen"));
}
}