use crate::{Epoch, Layout, Snowflake};
use std::marker::PhantomData;
#[derive(Debug)]
pub struct ClassicLayout<I>
where
I: MachineId,
{
_marker: PhantomData<I>,
}
impl<I> ClassicLayout<I>
where
I: MachineId,
{
const TIMESTAMP_BITS: usize = 41;
const TIMESTAMP_MASK: u64 =
((1 << Self::TIMESTAMP_BITS) - 1) << (Self::MACHINE_ID_BITS + Self::SEQUENCE_NUMBER_BITS);
const MACHINE_ID_BITS: usize = 10;
const MACHINE_ID_MASK: u64 = ((1 << Self::MACHINE_ID_BITS) - 1) << Self::SEQUENCE_NUMBER_BITS;
const SEQUENCE_NUMBER_BITS: usize = 12;
const SEQUENCE_NUMBER_MASK: u64 = (1 << Self::SEQUENCE_NUMBER_BITS) - 1;
#[inline]
pub fn machine_id(input: u64) -> u64 {
(input & Self::MACHINE_ID_MASK) >> Self::SEQUENCE_NUMBER_BITS
}
#[inline]
fn exceeds_machine_id(input: u64) -> bool {
input >= 1 << Self::MACHINE_ID_BITS
}
}
impl<I> Layout for ClassicLayout<I>
where
I: MachineId,
{
#[inline]
fn construct_snowflake(timestamp: u64, sequence_number: u64) -> u64 {
let machine_id = I::machine_id();
assert!(
!Self::exceeds_timestamp(timestamp)
&& !Self::exceeds_sequence_number(sequence_number)
&& !Self::exceeds_machine_id(machine_id)
);
(timestamp << (Self::MACHINE_ID_BITS + Self::SEQUENCE_NUMBER_BITS))
| (machine_id << Self::SEQUENCE_NUMBER_BITS)
| sequence_number
}
#[inline]
fn timestamp(input: u64) -> u64 {
(input & Self::TIMESTAMP_MASK) >> (Self::MACHINE_ID_BITS + Self::SEQUENCE_NUMBER_BITS)
}
#[inline]
fn exceeds_timestamp(input: u64) -> bool {
input >= 1 << Self::TIMESTAMP_BITS
}
#[inline]
fn sequence_number(input: u64) -> u64 {
input & Self::SEQUENCE_NUMBER_MASK
}
#[inline]
fn exceeds_sequence_number(input: u64) -> bool {
input >= 1 << Self::SEQUENCE_NUMBER_BITS
}
#[inline]
fn is_valid_snowflake(input: u64) -> bool {
input < 1 << 63
}
}
pub trait ClassicLayoutSnowflakeExtension {
fn machine_id(&self) -> u64;
fn into_i64(self) -> i64;
}
impl<I, E> ClassicLayoutSnowflakeExtension for Snowflake<ClassicLayout<I>, E>
where
I: MachineId,
E: Epoch,
{
#[inline]
fn machine_id(&self) -> u64 {
ClassicLayout::<I>::machine_id(self.into_inner())
}
#[inline]
fn into_i64(self) -> i64 {
self.inner as i64
}
}
pub trait MachineId {
fn machine_id() -> u64;
}
#[cfg(test)]
mod tests {
use crate::sync::atomic::{AtomicU64, Ordering};
use crate::sync::{Mutex, MutexGuard};
use crate::{ClassicLayout, ClassicLayoutSnowflakeExtension, Epoch, Layout, MachineId, Snowflake};
static MACHINE_ID: AtomicU64 = AtomicU64::new(0);
static MACHINE_LOCK: Mutex<()> = Mutex::new(());
struct SimpleMachineId;
impl SimpleMachineId {
fn set_id(id: u64) {
MACHINE_ID.store(id, Ordering::Relaxed);
}
fn acquire_lock() -> MutexGuard<'static, ()> {
MACHINE_LOCK.lock().unwrap_or_else(|e| e.into_inner())
}
}
impl MachineId for SimpleMachineId {
fn machine_id() -> u64 {
MACHINE_ID.load(Ordering::Relaxed)
}
}
struct ZeroMachineId;
impl MachineId for ZeroMachineId {
fn machine_id() -> u64 {
0
}
}
#[test]
fn construct_snowflake() {
let _g = SimpleMachineId::acquire_lock();
SimpleMachineId::set_id(0);
assert_eq!(
(1 << 12) - 1,
ClassicLayout::<SimpleMachineId>::construct_snowflake(0, (1 << 12) - 1),
"`construct_snowflake` used an unrelated sequence number"
);
SimpleMachineId::set_id((1 << 10) - 1);
assert_eq!(
(1 << 10) - 1,
ClassicLayout::<SimpleMachineId>::construct_snowflake(0, 0) >> 12
);
SimpleMachineId::set_id(0);
assert_eq!(
(1 << 41) - 1,
ClassicLayout::<SimpleMachineId>::construct_snowflake((1 << 41) - 1, 0) >> 22
);
SimpleMachineId::set_id((1 << 10) - 1);
assert_eq!(
0,
ClassicLayout::<SimpleMachineId>::construct_snowflake((1 << 41) - 1, (1 << 12) - 1) >> 63
);
SimpleMachineId::set_id(0);
assert_eq!(0, ClassicLayout::<SimpleMachineId>::construct_snowflake(0, 0));
}
#[test]
#[should_panic]
fn extreme_timestamp() {
let _ = ClassicLayout::<ZeroMachineId>::construct_snowflake(1 << 41, 0);
}
#[test]
#[should_panic]
fn extreme_sequence_number() {
let _ = ClassicLayout::<ZeroMachineId>::construct_snowflake(0, 1 << 12);
}
#[test]
#[should_panic]
fn extreme_machine_id() {
let _g = SimpleMachineId::acquire_lock();
SimpleMachineId::set_id(1 << 10);
let _ = ClassicLayout::<SimpleMachineId>::construct_snowflake(0, 0);
}
#[test]
fn getters() {
assert_eq!(0, ClassicLayout::<ZeroMachineId>::timestamp((1 << 22) - 1));
assert_eq!(
123,
ClassicLayout::<ZeroMachineId>::timestamp(123 << 22 | ((1 << 22) - 1))
);
assert_eq!((1 << 41) - 1, ClassicLayout::<ZeroMachineId>::timestamp(u64::MAX >> 1));
assert_eq!(
0,
ClassicLayout::<ZeroMachineId>::sequence_number((u64::MAX << 13) >> 1)
);
assert_eq!(
123,
ClassicLayout::<ZeroMachineId>::sequence_number((u64::MAX << 13) >> 1 | 123)
);
assert_eq!(
(1 << 12) - 1,
ClassicLayout::<ZeroMachineId>::sequence_number(u64::MAX >> 1)
);
assert_eq!(
0,
ClassicLayout::<ZeroMachineId>::machine_id((u64::MAX << 23) >> 1 | ((1 << 12) - 1))
);
assert_eq!(
123,
ClassicLayout::<ZeroMachineId>::machine_id((u64::MAX << 23) >> 1 | 123 << 12 | ((1 << 12) - 1))
);
assert_eq!((1 << 10) - 1, ClassicLayout::<ZeroMachineId>::machine_id(u64::MAX >> 1));
}
#[test]
fn snowflake_extension() {
struct SimpleEpoch;
impl Epoch for SimpleEpoch {
fn millis_since_unix() -> u64 {
0
}
}
type SimpleSnowflake = Snowflake<ClassicLayout<ZeroMachineId>, SimpleEpoch>;
assert_eq!(0, SimpleSnowflake::from_raw(0).unwrap().machine_id());
assert_eq!(234, SimpleSnowflake::from_raw(234 << 12).unwrap().machine_id());
assert_eq!(
(1 << 10) - 1,
SimpleSnowflake::from_raw(u64::MAX >> 1).unwrap().machine_id()
);
}
}