use std::cell::Cell;
use std::sync::atomic::{AtomicU64, Ordering};
static RAND_BITS: AtomicU64 = AtomicU64::new(14);
static CHECK_BITS: AtomicU64 = AtomicU64::new(6);
static BODY_BITS: AtomicU64 = AtomicU64::new(20);
static RAND_MASK: AtomicU64 = AtomicU64::new((1 << 14) - 1);
static CHECK_MASK: AtomicU64 = AtomicU64::new((1 << 6) - 1);
static SECRET_PEPPER: AtomicU64 = AtomicU64::new(0x5161_7C0D_E5EE_DCA1);
static THREAD_SEED_DISPENSER: AtomicU64 = AtomicU64::new(0x1234_5678_9ABC_DEF0);
thread_local! {
static RNG_STATE: Cell<u64> = Cell::new({
let mut seed = THREAD_SEED_DISPENSER.fetch_add(0x9E3779B97F4A7C15, Ordering::Relaxed);
if seed == 0 {
seed = 1;
}
seed
});
}
#[inline]
fn fast_rand() -> u64 {
let mask = RAND_MASK.load(Ordering::Relaxed);
RNG_STATE.with(|state| {
let mut x = state.get();
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
state.set(x);
x & mask
})
}
#[cfg(target_os = "linux")]
#[inline]
fn coarse_millis() -> u64 {
let mut ts = libc::timespec {
tv_sec: 0,
tv_nsec: 0,
};
unsafe {
libc::clock_gettime(libc::CLOCK_REALTIME_COARSE, &mut ts);
}
(ts.tv_sec as u64) * 1_000 + (ts.tv_nsec as u64) / 1_000_000
}
#[cfg(not(target_os = "linux"))]
#[inline]
fn coarse_millis() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("System time went backwards")
.as_millis() as u64
}
#[inline]
fn checksum(body: u64) -> u64 {
let pepper = SECRET_PEPPER.load(Ordering::Relaxed);
let mask = CHECK_MASK.load(Ordering::Relaxed);
let mut x = body ^ pepper;
x ^= x << 13;
x ^= x >> 7;
x ^= x << 17;
x & mask
}
pub fn configure(rand_bits: u64, check_bits: u64) {
let body_bits = rand_bits + check_bits;
assert!(body_bits < 64, "Bit allocation exceeds 64-bit capacity");
RAND_BITS.store(rand_bits, Ordering::Relaxed);
CHECK_BITS.store(check_bits, Ordering::Relaxed);
BODY_BITS.store(body_bits, Ordering::Relaxed);
RAND_MASK.store((1u64 << rand_bits) - 1, Ordering::Relaxed);
CHECK_MASK.store((1u64 << check_bits) - 1, Ordering::Relaxed);
}
pub fn set_pepper(pepper: u64) {
SECRET_PEPPER.store(pepper, Ordering::Relaxed);
}
#[inline]
pub fn generate() -> u64 {
let timestamp = coarse_millis();
let rand = fast_rand();
let check_b = CHECK_BITS.load(Ordering::Relaxed);
let body_b = BODY_BITS.load(Ordering::Relaxed);
let body = (timestamp << body_b) | (rand << check_b);
let check = checksum(body);
body | check
}
#[inline]
pub fn is_valid(id: u64) -> bool {
let mask = CHECK_MASK.load(Ordering::Relaxed);
let body = id & !mask;
checksum(body) == (id & mask)
}
#[inline]
pub fn timestamp_millis(id: u64) -> u64 {
id >> BODY_BITS.load(Ordering::Relaxed)
}
#[inline]
pub fn random_part(id: u64) -> u64 {
(id >> CHECK_BITS.load(Ordering::Relaxed)) & RAND_MASK.load(Ordering::Relaxed)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generated_id_is_valid() {
for _ in 0..10_000 {
assert!(is_valid(generate()));
}
}
#[test]
fn tampered_id_is_invalid() {
let id = generate();
assert!(!is_valid(id ^ 1));
assert!(!is_valid(id ^ 0x3F));
}
#[test]
fn timestamp_is_recent() {
let id = generate();
let ts = timestamp_millis(id);
let now = coarse_millis() & ((1u64 << (64 - BODY_BITS.load(Ordering::Relaxed))) - 1);
let diff = now.wrapping_sub(ts);
assert!(diff < 1_000);
}
#[test]
fn ids_are_unique() {
let ids: std::collections::HashSet<u64> = (0..1_000).map(|_| generate()).collect();
assert!(ids.len() > 900);
}
#[test]
fn fake_id_rejection_rate() {
let passed = (0u64..10_000)
.filter(|&i| is_valid(i.wrapping_mul(0x9E3779B97F4A7C15)))
.count();
assert!(passed < 250, "Too many fake IDs passed: {passed}");
}
}