1use std::sync::Mutex;
9
10const ALPHABET: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
11
12struct Last {
13 ms: u64,
14 rand: u128, }
16
17static LAST: Mutex<Last> = Mutex::new(Last { ms: 0, rand: 0 });
18
19pub fn new() -> String {
21 let ms = super::now_ms();
22 let mut last = LAST.lock().unwrap_or_else(|e| e.into_inner());
23 let rand = if ms == last.ms {
24 (last.rand + 1) & ((1u128 << 80) - 1)
26 } else {
27 random80()
28 };
29 last.ms = ms;
30 last.rand = rand;
31 encode(ms, rand)
32}
33
34pub fn timestamp_ms(ulid: &str) -> Option<u64> {
36 if ulid.len() != 26 {
37 return None;
38 }
39 let mut ts: u64 = 0;
40 for c in ulid.bytes().take(10) {
41 let v = decode_char(c)?;
42 ts = (ts << 5) | v as u64;
43 }
44 Some(ts)
45}
46
47fn encode(ms: u64, rand: u128) -> String {
48 let mut out = [0u8; 26];
49 let mut t = ms;
50 for i in (0..10).rev() {
51 out[i] = ALPHABET[(t & 31) as usize];
52 t >>= 5;
53 }
54 let mut r = rand;
55 for i in (10..26).rev() {
56 out[i] = ALPHABET[(r & 31) as usize];
57 r >>= 5;
58 }
59 String::from_utf8_lossy(&out).into_owned()
60}
61
62fn decode_char(c: u8) -> Option<u8> {
63 ALPHABET
64 .iter()
65 .position(|&a| a == c.to_ascii_uppercase())
66 .map(|p| p as u8)
67}
68
69fn random80() -> u128 {
70 let mut buf = [0u8; 16];
71 let ok = std::fs::File::open("/dev/urandom")
72 .and_then(|mut f| std::io::Read::read_exact(&mut f, &mut buf))
73 .is_ok();
74 if !ok {
75 static CTR: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
78 let seed = super::now_ms()
79 ^ (std::process::id() as u64).rotate_left(32)
80 ^ CTR.fetch_add(0x9E37_79B9_7F4A_7C15, std::sync::atomic::Ordering::Relaxed);
81 let mut z = seed.wrapping_add(0x9E37_79B9_7F4A_7C15);
82 let mut mix = || {
83 z = z.wrapping_add(0x9E37_79B9_7F4A_7C15);
84 let mut x = z;
85 x = (x ^ (x >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
86 x = (x ^ (x >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
87 x ^ (x >> 31)
88 };
89 let a = mix();
90 let b = mix();
91 buf[..8].copy_from_slice(&a.to_le_bytes());
92 buf[8..].copy_from_slice(&b.to_le_bytes());
93 }
94 u128::from_le_bytes(buf) & ((1u128 << 80) - 1)
95}
96
97#[cfg(test)]
98mod tests {
99 use super::*;
100
101 #[test]
102 fn ulids_are_26_chars_sortable_and_unique() {
103 let a = new();
104 let b = new();
105 assert_eq!(a.len(), 26);
106 assert!(a.bytes().all(|c| ALPHABET.contains(&c)));
107 assert_ne!(a, b);
108 assert!(a < b, "monotonic within a process: {a} < {b}");
109 let ts = timestamp_ms(&a).unwrap();
110 let now = super::super::now_ms();
111 assert!(now >= ts && now - ts < 5_000);
112 assert_eq!(timestamp_ms("short"), None);
113 let mut prev = new();
115 for _ in 0..1000 {
116 let n = new();
117 assert!(n > prev);
118 prev = n;
119 }
120 }
121}