1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//! [Snowflake ID] generation logic.
//!
//! [Snowflake ID]: https://en.wikipedia.org/wiki/Snowflake_ID

use once_cell::sync::Lazy;
use std::sync::atomic::{AtomicU8, Ordering};
use std::time::{Duration, SystemTime, UNIX_EPOCH};

static MACHINE_ID: Lazy<u8> = Lazy::new(|| (duration_since_epoch().as_micros() & 0xFF) as u8);
static SEQUENCE_NUMBER: AtomicU8 = AtomicU8::new(0);

/// Generates a "Snowflake ID". Since there are already variations, this doesn't
/// bother being exactly the same as Twitter's or Mastodon's, but the idea is
/// the same. 48 bits for time, 8 bits rerolled for each process, 8 bits from a
/// sequence number.
pub fn generate_snowflake() -> u64 {
    SEQUENCE_NUMBER.fetch_add(1, Ordering::Relaxed) as u64
        + ((*MACHINE_ID as u64) << 8)
        + ((duration_since_epoch().as_millis() as u64 & 0x0000_FFFF_FFFF_FFFF) << 16)
}

fn duration_since_epoch() -> Duration {
    SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
}