OXYGENHUB
# 5d695d869fbf spectra / crates / spectra-core / src / ids.rs
1.2 KiB · Rust · last changed The ledger: hash-chained events in the frozen envelope, signed checkpoints, admin verify, check and rebuild (#3) 2 days ago source blame history raw
1
//! Identifiers (spec §9.4): ULIDs, 26 characters of Crockford base32, time
2
//! ordered and globally unique, so an id made on one machine merges into a
3
//! hub without renaming.
4
5
use rand::RngCore;
6
7
const CROCKFORD: &[u8; 32] = b"0123456789ABCDEFGHJKMNPQRSTVWXYZ";
8
9
/// A new ULID for the current time.
10
pub fn ulid() -> String {
11
    let mut r = [0u8; 16];
12
    rand::rngs::OsRng.fill_bytes(&mut r);
13
    ulid_from(now_ms(), u128::from_be_bytes(r))
14
}
15
16
/// A ULID from a millisecond time and 80 bits taken from `random`.
17
pub fn ulid_from(ms: u64, random: u128) -> String {
18
    let v: u128 = ((ms as u128 & ((1 << 48) - 1)) << 80) | (random & ((1u128 << 80) - 1));
19
    (0..26).map(|i| CROCKFORD[((v >> (125 - 5 * i)) & 31) as usize] as char).collect()
20
}
21
22
/// Milliseconds since the Unix epoch.
23
pub fn now_ms() -> u64 {
24
    std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0)
25
}
26
27
#[cfg(test)]
28
mod tests {
29
    use super::*;
30
31
    #[test]
32
    fn ulids_are_26_chars_and_sort_by_time() {
33
        let a = ulid_from(1_000, 5);
34
        let b = ulid_from(2_000, 0);
35
        assert_eq!(a.len(), 26);
36
        assert!(a < b);
37
        assert_eq!(ulid_from(0, 0), "00000000000000000000000000");
38
        assert_ne!(ulid(), ulid());
39
    }
40
}