1use core::fmt;
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct Ulid([u8; 16]);
23
24impl Ulid {
25 pub fn new() -> Self {
30 use std::sync::atomic::{AtomicU64, Ordering};
31 use std::time::{SystemTime, UNIX_EPOCH};
32 static COUNTER: AtomicU64 = AtomicU64::new(0);
33 let ms = SystemTime::now()
34 .duration_since(UNIX_EPOCH)
35 .map(|d| d.as_millis() as u64)
36 .unwrap_or(0);
37 let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
38 let mut bytes = [0u8; 16];
39 let ms_bytes = ms.to_be_bytes();
41 bytes[0..6].copy_from_slice(&ms_bytes[2..8]);
42 bytes[6..14].copy_from_slice(&counter.to_be_bytes());
44 Self(bytes)
45 }
46
47 pub fn as_bytes(&self) -> &[u8; 16] {
49 &self.0
50 }
51}
52
53impl Default for Ulid {
54 fn default() -> Self {
55 Self::new()
56 }
57}
58
59impl fmt::Display for Ulid {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 const ALPHABET: &[u8] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
63 let mut n: u128 = 0;
65 for &b in &self.0 {
66 n = (n << 8) | (b as u128);
67 }
68 let mut out = [0u8; 26];
69 for i in (0..26).rev() {
70 out[i] = ALPHABET[(n & 31) as usize];
71 n >>= 5;
72 }
73 f.write_str(core::str::from_utf8(&out).unwrap_or(""))
74 }
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn display_length_26() {
83 let id = Ulid::new();
84 assert_eq!(id.to_string().len(), 26);
85 }
86
87 #[test]
88 fn unique() {
89 let a = Ulid::new();
90 let b = Ulid::new();
91 assert_ne!(a, b);
92 }
93
94 #[test]
95 fn time_ordered() {
96 let a = Ulid::new();
97 let b = Ulid::new();
98 assert!(b.as_bytes() >= a.as_bytes());
101 }
102}