use crate::Nonce;
use std::sync::atomic::{AtomicU64, AtomicBool, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
const EPOCH: u64 = 1700000000000;
const TIMESTAMP_BITS: u8 = 41;
const MACHINE_ID_BITS: u8 = 10;
const SEQUENCE_BITS: u8 = 12;
const MAX_MACHINE_ID: u16 = (1 << MACHINE_ID_BITS) - 1;
const MAX_SEQUENCE: u64 = (1 << SEQUENCE_BITS) - 1;
#[derive(Debug)]
pub struct SnowflakeGenerator {
machine_id: u64,
last_timestamp: AtomicU64,
sequence: AtomicU64,
spinlock: AtomicBool, }
impl SnowflakeGenerator {
pub fn new(machine_id: u16) -> Self {
assert!(machine_id <= MAX_MACHINE_ID, "Machine ID out of range");
Self {
machine_id: machine_id as u64,
last_timestamp: AtomicU64::new(0),
sequence: AtomicU64::new(0),
spinlock: AtomicBool::new(false), }
}
pub fn generate_id(&self) -> u64 {
#[allow(deprecated)] while self.spinlock.compare_and_swap(false, true, Ordering::Acquire) {
}
let mut timestamp = current_millis();
let last_timestamp = self.last_timestamp.load(Ordering::Relaxed);
let mut sequence = self.sequence.load(Ordering::Relaxed);
if timestamp == last_timestamp {
sequence = (sequence + 1) & MAX_SEQUENCE;
if sequence == 0 {
while timestamp <= last_timestamp {
timestamp = current_millis();
}
}
} else {
sequence = 0;
}
self.sequence.store(sequence, Ordering::Release);
self.last_timestamp.store(timestamp, Ordering::Relaxed);
self.spinlock.store(false, Ordering::Release);
if timestamp > (1 << TIMESTAMP_BITS) - 1 {
eprintln!("[WARN] {}: timestamp overflow", std::module_path!());
}
((timestamp - EPOCH) << (MACHINE_ID_BITS + SEQUENCE_BITS))
| (self.machine_id << SEQUENCE_BITS)
| sequence
}
}
fn current_millis() -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.expect("Time went backwards")
.as_millis() as u64
}
pub fn snowflake_generator(machine_id: u16) -> impl Fn() -> Nonce + Send + Sync + 'static {
let generator = SnowflakeGenerator::new(machine_id);
move || Nonce(generator.generate_id())
}