summaryrefslogtreecommitdiff
path: root/rust/crates/animation/src/id.rs
blob: 9528866055a21449a000fb1d21cc97e9b3aff0ea (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
use std::cell::Cell;
use std::time::{SystemTime, UNIX_EPOCH};

thread_local! {
    static RNG_STATE: Cell<u64> = const { Cell::new(0) };
}

fn next_u64() -> u64 {
    RNG_STATE.with(|state| {
        let mut value = state.get();
        if value == 0 {
            let nanos = SystemTime::now()
                .duration_since(UNIX_EPOCH)
                .map(|duration| duration.as_nanos() as u64)
                .unwrap_or(0x9e37_79b9_7f4a_7c15);
            value = nanos
                ^ (std::process::id() as u64).rotate_left(32)
                ^ 0x9e37_79b9_7f4a_7c15;
        }
        value ^= value << 13;
        value ^= value >> 7;
        value ^= value << 17;
        state.set(value);
        value
    })
}

pub fn generate_uuid() -> String {
    let mut bytes = [0u8; 16];
    for chunk in bytes.chunks_mut(8) {
        let random = next_u64().to_le_bytes();
        chunk.copy_from_slice(&random[..chunk.len()]);
    }
    bytes[6] = (bytes[6] & 0x0f) | 0x40;
    bytes[8] = (bytes[8] & 0x3f) | 0x80;
    let hex: Vec<String> = bytes.iter().map(|byte| format!("{byte:02x}")).collect();
    format!(
        "{}-{}-{}-{}-{}",
        hex[0..4].join(""),
        hex[4..6].join(""),
        hex[6..8].join(""),
        hex[8..10].join(""),
        hex[10..16].join("")
    )
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn generates_uuid_shaped_unique_ids() {
        let id = generate_uuid();
        let parts: Vec<&str> = id.split('-').collect();
        assert_eq!(parts.len(), 5);
        assert_eq!(parts[0].len(), 8);
        assert_eq!(parts[1].len(), 4);
        assert_eq!(parts[4].len(), 12);
        assert_ne!(generate_uuid(), generate_uuid());
    }
}