use serde::{Deserialize, Serialize};
use std::fmt;
use std::sync::Mutex;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct Uid(pub u128);
const LOW_BITS: u32 = 80;
const LOW_MASK: u128 = (1u128 << LOW_BITS) - 1;
static LAST: Mutex<(u64, u128)> = Mutex::new((0, 0));
fn random_low() -> u128 {
let mut bytes = [0u8; 10];
rand::Rng::fill(&mut rand::thread_rng(), &mut bytes[..]);
let mut low: u128 = 0;
for b in bytes {
low = (low << 8) | b as u128;
}
low & (LOW_MASK >> 1)
}
impl Uid {
pub fn generate() -> Self {
let now_ms = crate::time::now_micros() / 1000;
let mut last = LAST.lock().expect("uid lock poisoned");
let (last_ms, last_low) = *last;
let (ms, low) = if now_ms > last_ms {
(now_ms, random_low())
} else {
let next = (last_low + 1) & LOW_MASK;
if next == 0 {
(last_ms + 1, random_low())
} else {
(last_ms, next)
}
};
*last = (ms, low);
Uid(((ms as u128) << LOW_BITS) | low)
}
pub fn timestamp_ms(&self) -> u64 {
(self.0 >> 80) as u64
}
pub fn to_hex(&self) -> String {
format!("{:032x}", self.0)
}
pub fn from_hex(s: &str) -> Option<Self> {
u128::from_str_radix(s, 16).ok().map(Uid)
}
}
impl fmt::Display for Uid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_hex())
}
}
pub mod hex_serde {
use super::Uid;
use serde::{Deserialize, Deserializer, Serializer};
pub fn serialize<S: Serializer>(uid: &Uid, s: S) -> Result<S::Ok, S::Error> {
s.serialize_str(&uid.to_hex())
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Uid, D::Error> {
let s = String::deserialize(d)?;
Uid::from_hex(&s).ok_or_else(|| serde::de::Error::custom(format!("invalid uid hex: {s}")))
}
}
impl fmt::Debug for Uid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Uid({})", self.to_hex())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn uids_are_unique_and_ordered() {
let a = Uid::generate();
std::thread::sleep(std::time::Duration::from_millis(2));
let b = Uid::generate();
assert!(a < b);
assert_ne!(a, b);
assert_eq!(Uid::from_hex(&a.to_hex()), Some(a));
}
#[test]
fn same_millisecond_burst_is_strictly_monotonic() {
let ids: Vec<Uid> = (0..10_000).map(|_| Uid::generate()).collect();
for w in ids.windows(2) {
assert!(w[0] < w[1], "ids not strictly increasing within a burst");
}
let same_ms = ids
.windows(2)
.filter(|w| w[0].timestamp_ms() == w[1].timestamp_ms())
.count();
assert!(same_ms > 100, "burst did not share milliseconds: {same_ms}");
}
}