|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
use std::sync::atomic::{AtomicU64, Ordering}; |
|
|
use std::time::{SystemTime, UNIX_EPOCH}; |
|
|
|
|
|
|
|
|
static COUNTER: AtomicU64 = AtomicU64::new(0); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)] |
|
|
pub struct Id([u8; 16]); |
|
|
|
|
|
impl Id { |
|
|
|
|
|
|
|
|
|
|
|
pub fn now() -> Self { |
|
|
let timestamp = SystemTime::now() |
|
|
.duration_since(UNIX_EPOCH) |
|
|
.unwrap() |
|
|
.as_millis() as u64; |
|
|
|
|
|
|
|
|
let counter = COUNTER.fetch_add(1, Ordering::Relaxed); |
|
|
|
|
|
let mut bytes = [0u8; 16]; |
|
|
|
|
|
|
|
|
bytes[0] = (timestamp >> 40) as u8; |
|
|
bytes[1] = (timestamp >> 32) as u8; |
|
|
bytes[2] = (timestamp >> 24) as u8; |
|
|
bytes[3] = (timestamp >> 16) as u8; |
|
|
bytes[4] = (timestamp >> 8) as u8; |
|
|
bytes[5] = timestamp as u8; |
|
|
|
|
|
|
|
|
bytes[6] = (counter >> 8) as u8; |
|
|
bytes[7] = counter as u8; |
|
|
|
|
|
|
|
|
let random_seed = timestamp |
|
|
.wrapping_mul(6364136223846793005) |
|
|
.wrapping_add(counter); |
|
|
bytes[8] = (random_seed >> 56) as u8; |
|
|
bytes[9] = (random_seed >> 48) as u8; |
|
|
bytes[10] = (random_seed >> 40) as u8; |
|
|
bytes[11] = (random_seed >> 32) as u8; |
|
|
bytes[12] = (random_seed >> 24) as u8; |
|
|
bytes[13] = (random_seed >> 16) as u8; |
|
|
bytes[14] = (random_seed >> 8) as u8; |
|
|
bytes[15] = random_seed as u8; |
|
|
|
|
|
Self(bytes) |
|
|
} |
|
|
|
|
|
|
|
|
pub fn from_bytes(bytes: [u8; 16]) -> Self { |
|
|
Self(bytes) |
|
|
} |
|
|
|
|
|
|
|
|
pub fn as_bytes(&self) -> &[u8; 16] { |
|
|
&self.0 |
|
|
} |
|
|
|
|
|
|
|
|
pub fn timestamp_ms(&self) -> u64 { |
|
|
((self.0[0] as u64) << 40) |
|
|
| ((self.0[1] as u64) << 32) |
|
|
| ((self.0[2] as u64) << 24) |
|
|
| ((self.0[3] as u64) << 16) |
|
|
| ((self.0[4] as u64) << 8) |
|
|
| (self.0[5] as u64) |
|
|
} |
|
|
|
|
|
|
|
|
pub fn nil() -> Self { |
|
|
Self([0u8; 16]) |
|
|
} |
|
|
|
|
|
|
|
|
pub fn is_nil(&self) -> bool { |
|
|
self.0 == [0u8; 16] |
|
|
} |
|
|
} |
|
|
|
|
|
impl std::fmt::Display for Id { |
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
|
|
|
|
|
for byte in &self.0 { |
|
|
write!(f, "{:02x}", byte)?; |
|
|
} |
|
|
Ok(()) |
|
|
} |
|
|
} |
|
|
|
|
|
#[cfg(test)] |
|
|
mod tests { |
|
|
use super::*; |
|
|
use std::thread; |
|
|
use std::time::Duration; |
|
|
|
|
|
#[test] |
|
|
fn test_id_creation() { |
|
|
let id = Id::now(); |
|
|
assert!(!id.is_nil()); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_id_timestamp() { |
|
|
let before = SystemTime::now() |
|
|
.duration_since(UNIX_EPOCH) |
|
|
.unwrap() |
|
|
.as_millis() as u64; |
|
|
|
|
|
let id = Id::now(); |
|
|
|
|
|
let after = SystemTime::now() |
|
|
.duration_since(UNIX_EPOCH) |
|
|
.unwrap() |
|
|
.as_millis() as u64; |
|
|
|
|
|
let ts = id.timestamp_ms(); |
|
|
assert!(ts >= before); |
|
|
assert!(ts <= after); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_id_ordering() { |
|
|
let id1 = Id::now(); |
|
|
thread::sleep(Duration::from_millis(2)); |
|
|
let id2 = Id::now(); |
|
|
|
|
|
|
|
|
assert!(id2 > id1); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_id_from_bytes() { |
|
|
let bytes = [1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]; |
|
|
let id = Id::from_bytes(bytes); |
|
|
assert_eq!(id.as_bytes(), &bytes); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_id_nil() { |
|
|
let nil = Id::nil(); |
|
|
assert!(nil.is_nil()); |
|
|
assert_eq!(nil.timestamp_ms(), 0); |
|
|
} |
|
|
|
|
|
#[test] |
|
|
fn test_id_display() { |
|
|
let id = Id::from_bytes([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]); |
|
|
let display = format!("{}", id); |
|
|
assert_eq!(display, "000102030405060708090a0b0c0d0e0f"); |
|
|
} |
|
|
} |
|
|
|