use std::sync::atomic::{AtomicU64, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
pub const DEFAULT_EPOCH_MS: u64 = 1_767_225_600_000;
#[derive(Debug)]
pub struct Snowflake {
worker_id: u16,
epoch_ms: u64,
sequence: AtomicU64,
}
impl Snowflake {
pub fn new(worker_id: u16) -> Self {
Self::with_epoch(worker_id, DEFAULT_EPOCH_MS)
}
pub fn with_epoch(worker_id: u16, epoch_ms: u64) -> Self {
Self {
worker_id: worker_id & 0x3ff,
epoch_ms,
sequence: AtomicU64::new(0),
}
}
pub fn next_id(&self) -> u64 {
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_millis() as u64)
.unwrap_or(0);
let ts = now.saturating_sub(self.epoch_ms) & 0x1ffffffffff; let seq = self.sequence.fetch_add(1, Ordering::Relaxed) & 0xfff; let worker = self.worker_id as u64 & 0x3ff;
(ts << 22) | (worker << 12) | seq
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn next_id_produces_value() {
let gen = Snowflake::new(1);
let _ = gen.next_id();
}
#[test]
fn worker_id_clamped() {
let gen = Snowflake::new(0xffff);
let id = gen.next_id();
let worker = (id >> 12) & 0x3ff;
assert_eq!(worker, 0x3ff);
}
#[test]
fn unique_ids() {
let gen = Snowflake::new(1);
let a = gen.next_id();
let b = gen.next_id();
assert_ne!(a, b);
}
#[test]
fn monotonic_within_ms() {
let gen = Snowflake::new(1);
let a = gen.next_id();
let b = gen.next_id();
assert!(b >= a);
}
}