Skip to main content

reddb_server/crypto/
uuid.rs

1use crate::crypto::os_random;
2use std::fmt;
3
4#[derive(Clone, Copy, Eq, PartialEq, Hash)]
5pub struct Uuid([u8; 16]);
6
7#[derive(Debug, Clone, Copy, Eq, PartialEq)]
8pub struct UuidParseError;
9
10impl Uuid {
11    pub fn new_v4() -> Self {
12        let mut bytes = [0u8; 16];
13        os_random::fill_bytes(&mut bytes).expect("OS CSPRNG unavailable");
14
15        bytes[6] = (bytes[6] & 0x0f) | 0x40;
16        bytes[8] = (bytes[8] & 0x3f) | 0x80;
17
18        Self(bytes)
19    }
20
21    /// UUID v7: time-sortable. Bytes 0-5 = 48-bit Unix ms timestamp,
22    /// byte 6 = version 7 nibble + 4 rand bits, byte 8 = variant 10xx,
23    /// rest random. Monotonic within the same millisecond is not
24    /// guaranteed — callers that need strict monotonicity should add
25    /// a sequence counter.
26    pub fn new_v7() -> Self {
27        let ms = crate::utils::now_unix_millis();
28        let mut rand = [0u8; 10];
29        os_random::fill_bytes(&mut rand).expect("OS CSPRNG unavailable");
30
31        let mut bytes = [0u8; 16];
32        bytes[0] = ((ms >> 40) & 0xFF) as u8;
33        bytes[1] = ((ms >> 32) & 0xFF) as u8;
34        bytes[2] = ((ms >> 24) & 0xFF) as u8;
35        bytes[3] = ((ms >> 16) & 0xFF) as u8;
36        bytes[4] = ((ms >> 8) & 0xFF) as u8;
37        bytes[5] = (ms & 0xFF) as u8;
38        bytes[6] = (rand[0] & 0x0F) | 0x70;
39        bytes[7] = rand[1];
40        bytes[8] = (rand[2] & 0x3F) | 0x80;
41        bytes[9..16].copy_from_slice(&rand[3..10]);
42
43        Self(bytes)
44    }
45
46    /// Parse a hyphenated UUID string (xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).
47    pub fn parse_str(s: &str) -> Result<Self, UuidParseError> {
48        let normalized: String = s.chars().filter(|&c| c != '-').collect();
49        if normalized.len() != 32 {
50            return Err(UuidParseError);
51        }
52        let mut bytes = [0u8; 16];
53        for i in 0..16 {
54            bytes[i] = u8::from_str_radix(&normalized[i * 2..i * 2 + 2], 16)
55                .map_err(|_| UuidParseError)?;
56        }
57        Ok(Self(bytes))
58    }
59
60    pub fn as_bytes(&self) -> &[u8; 16] {
61        &self.0
62    }
63}
64
65impl fmt::Display for Uuid {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        let b = self.0;
68        write!(
69            f,
70            "{:02x}{:02x}{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}-{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}",
71            b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11],
72            b[12], b[13], b[14], b[15]
73        )
74    }
75}
76
77impl fmt::Debug for Uuid {
78    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79        fmt::Display::fmt(self, f)
80    }
81}