#![cfg_attr(
any(
all(feature = "blocking", not(feature = "lock-free")),
all(feature = "lock-free", not(feature = "blocking"))
),
doc = "```"
)]
#![cfg_attr(all(feature = "blocking", feature = "lock-free"), doc = "```ignore")]
#![doc = "```"]
#![warn(missing_docs, missing_debug_implementations)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#[cfg(not(any(feature = "blocking", feature = "lock-free")))]
compile_error!("you must enable at least one generator implementation (blocking or lock-free)");
#[cfg(feature = "lock-free")]
use crate::sync::atomic;
#[cfg(feature = "lock-free")]
use crate::sync::atomic::AtomicU64;
use crate::sync::Arc;
#[cfg(feature = "blocking")]
use crate::sync::Mutex;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::cmp;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use std::time::Duration;
#[cfg(not(any(test, loom)))]
use std::time::SystemTime;
#[cfg(any(test, loom))]
use system_time_mock::SystemTime;
mod classic;
mod comparator;
mod sync {
#[cfg(test)]
pub(crate) use lazy_static::lazy_static;
#[cfg(all(loom))]
pub(crate) use loom::lazy_static;
#[cfg(all(feature = "lock-free", loom))]
pub(crate) use loom::sync::atomic;
#[cfg(loom)]
pub(crate) use loom::sync::Arc;
#[cfg(all(feature = "blocking", loom))]
pub(crate) use loom::sync::Mutex;
#[cfg(all(loom))]
pub(crate) use loom::sync::MutexGuard;
#[cfg(all(any(feature = "lock-free", test), not(loom)))]
pub(crate) use std::sync::atomic;
#[cfg(not(loom))]
pub(crate) use std::sync::Arc;
#[cfg(any(all(any(feature = "blocking", test), not(loom))))]
pub(crate) use std::sync::Mutex;
#[cfg(all(test, not(loom)))]
pub(crate) use std::sync::MutexGuard;
}
#[doc(hidden)]
#[cfg(any(test, loom))]
pub mod system_time_mock {
use crate::sync::{lazy_static, Mutex, MutexGuard};
use std::time::Duration;
lazy_static! {
static ref TIME: Mutex<u64> = Mutex::new(0);
static ref TIME_LOCK: Mutex<()> = Mutex::new(());
}
#[derive(Debug, Clone, Copy, PartialEq, PartialOrd)]
#[repr(transparent)]
pub struct SystemTime(u64);
impl SystemTime {
pub const UNIX_EPOCH: Self = Self(0);
pub fn set_time(time: u64) {
*TIME.lock().unwrap() = time;
}
pub fn acquire_lock() -> MutexGuard<'static, ()> {
TIME_LOCK.lock().unwrap_or_else(|poisoned| poisoned.into_inner())
}
pub(crate) fn checked_add(&self, duration: Duration) -> Option<SystemTime> {
let duration = duration.as_millis();
if self.0 as u128 + duration >= u64::MAX as u128 {
return None;
}
Some(Self(self.0 + duration as u64))
}
pub(crate) fn duration_since(&self, earlier: SystemTime) -> Result<Duration, SystemTimeError> {
if earlier.0 > self.0 {
return Err(SystemTimeError);
}
Ok(Duration::from_millis(self.0 - earlier.0))
}
pub(crate) fn now() -> Self {
Self(*TIME.lock().unwrap())
}
}
pub(crate) struct SystemTimeError;
}
#[cfg_attr(
any(
all(feature = "blocking", not(feature = "lock-free")),
all(feature = "lock-free", not(feature = "blocking"))
),
doc = "```"
)]
#[cfg_attr(all(feature = "blocking", feature = "lock-free"), doc = "```ignore")]
#[derive(Debug, Clone)]
pub struct Generator<L, E>
where
L: Layout,
E: Epoch,
{
#[cfg(feature = "blocking")]
last_snowflake_blocking: Arc<Mutex<u64>>,
#[cfg(feature = "lock-free")]
last_snowflake_atomic: Arc<AtomicU64>,
_marker: PhantomData<(L, E)>,
}
impl<L, E> Generator<L, E>
where
L: Layout,
E: Epoch,
{
#[cfg(any(all(feature = "blocking", not(feature = "lock-free")), doc))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "blocking", not(feature = "lock-free")))))]
#[inline]
pub fn generate(&self) -> Result<Snowflake<L, E>> {
self.generate_blocking()
}
#[cfg(any(all(feature = "lock-free", not(feature = "blocking")), doc))]
#[cfg_attr(docsrs, doc(cfg(all(feature = "lock-free", not(feature = "blocking")))))]
#[inline]
pub fn generate(&self) -> Result<Snowflake<L, E>> {
self.generate_lock_free()
}
#[cfg(any(feature = "blocking", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "blocking")))]
pub fn generate_blocking(&self) -> Result<Snowflake<L, E>> {
let mut last_snowflake = self.last_snowflake_blocking.lock().unwrap();
let time = Self::timestamp()?;
let last_timestamp = L::timestamp(*last_snowflake);
match last_timestamp.cmp(&time) {
cmp::Ordering::Equal => {
let sequence = L::sequence_number(*last_snowflake)
.checked_add(1)
.ok_or(Error::SnowflakeExhaustion)?;
if L::exceeds_sequence_number(sequence) {
return Err(Error::SnowflakeExhaustion);
}
let snowflake = L::construct_snowflake(time, sequence);
*last_snowflake = snowflake;
Ok(Snowflake {
inner: snowflake,
_marker: PhantomData,
})
}
cmp::Ordering::Less => {
let snowflake = L::construct_snowflake(time, 0);
*last_snowflake = snowflake;
Ok(Snowflake {
inner: snowflake,
_marker: PhantomData,
})
}
cmp::Ordering::Greater => {
Err(Error::NonMonotonicClock)
}
}
}
#[cfg(any(feature = "lock-free", doc))]
#[cfg_attr(docsrs, doc(cfg(feature = "lock-free")))]
pub fn generate_lock_free(&self) -> Result<Snowflake<L, E>> {
let mut last_snowflake = self.last_snowflake_atomic.load(atomic::Ordering::Relaxed);
loop {
let time = Self::timestamp()?;
let last_timestamp = L::timestamp(last_snowflake);
let sequence = match last_timestamp.cmp(&time) {
cmp::Ordering::Equal => {
L::sequence_number(last_snowflake)
.checked_add(1)
.and_then(|sequence| {
if L::exceeds_sequence_number(sequence) {
None
} else {
Some(sequence)
}
})
.ok_or(Error::SnowflakeExhaustion)?
}
cmp::Ordering::Less => {
0
}
cmp::Ordering::Greater => {
return Err(Error::NonMonotonicClock);
}
};
let new_snowflake = L::construct_snowflake(time, sequence);
match self.last_snowflake_atomic.compare_exchange_weak(
last_snowflake,
new_snowflake,
atomic::Ordering::Relaxed,
atomic::Ordering::Relaxed,
) {
Ok(_) => {
return Ok(Snowflake {
inner: new_snowflake,
_marker: PhantomData,
});
}
Err(current_value) => {
last_snowflake = current_value
} }
}
}
fn timestamp() -> Result<u64> {
let time = SystemTime::now()
.duration_since(
SystemTime::UNIX_EPOCH
.checked_add(Duration::from_millis(E::millis_since_unix()))
.ok_or(Error::FatalSnowflakeExhaustion)?,
)
.map_err(|_| Error::InvalidEpoch)?
.as_millis();
if time > u64::MAX as u128 || L::exceeds_timestamp(time as u64) {
return Err(Error::FatalSnowflakeExhaustion);
}
Ok(time as u64)
}
#[doc(hidden)]
#[cfg(test)]
fn set_last_snowflake(&self, last_snowflake: u64) {
assert!(L::is_valid_snowflake(last_snowflake));
#[cfg(feature = "blocking")]
{
let mut guard = self.last_snowflake_blocking.lock().unwrap();
*guard = last_snowflake;
}
#[cfg(feature = "lock-free")]
{
self.last_snowflake_atomic
.store(last_snowflake, atomic::Ordering::Release);
}
}
}
impl<L, E> Default for Generator<L, E>
where
L: Layout,
E: Epoch,
{
fn default() -> Self {
Self {
#[cfg(feature = "blocking")]
last_snowflake_blocking: Arc::new(Mutex::new(0)),
#[cfg(feature = "lock-free")]
last_snowflake_atomic: Arc::new(AtomicU64::new(0)),
_marker: PhantomData,
}
}
}
#[cfg(test)]
mod generator_tests {
use crate::system_time_mock::SystemTime;
use crate::{Epoch, Error, Generator, Layout, Result, Snowflake};
use std::collections::{HashSet, VecDeque};
#[derive(Debug)]
struct SimpleSnowflakeParams;
impl Layout for SimpleSnowflakeParams {
fn construct_snowflake(timestamp: u64, sequence_number: u64) -> u64 {
assert!(!Self::exceeds_timestamp(timestamp) && !Self::exceeds_sequence_number(sequence_number));
timestamp << 12 | sequence_number
}
fn timestamp(input: u64) -> u64 {
input >> 12
}
fn exceeds_timestamp(input: u64) -> bool {
input >= (1 << 51)
}
fn sequence_number(input: u64) -> u64 {
input & ((1 << 12) - 1)
}
fn exceeds_sequence_number(input: u64) -> bool {
input >= (1 << 12)
}
fn is_valid_snowflake(input: u64) -> bool {
input >> 63 == 0
}
}
impl Epoch for SimpleSnowflakeParams {
fn millis_since_unix() -> u64 {
1672531200000
}
}
type SimpleSnowflake = Snowflake<SimpleSnowflakeParams, SimpleSnowflakeParams>;
type SimpleSnowflakeGenerator = Generator<SimpleSnowflakeParams, SimpleSnowflakeParams>;
macro_rules! impl_test {
($test_impl:ident, $test:ident, $test_blocking:ident, $test_lock_free:ident) => {
#[test]
#[cfg(any(
all(feature = "blocking", not(feature = "lock-free")),
all(feature = "lock-free", not(feature = "blocking"))
))]
fn $test() {
let _g = SystemTime::acquire_lock();
$test_impl(Generator::generate);
}
#[test]
#[cfg(feature = "blocking")]
fn $test_blocking() {
let _g = SystemTime::acquire_lock();
$test_impl(Generator::generate_blocking);
}
#[test]
#[cfg(feature = "lock-free")]
fn $test_lock_free() {
let _g = SystemTime::acquire_lock();
$test_impl(Generator::generate_lock_free);
}
};
}
impl_test!(test_unique, unique, unique_blocking, unique_lock_fre);
fn test_unique(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531200001);
let gen = SimpleSnowflakeGenerator::default();
let mut snowflakes: HashSet<_> = (0..10).map(|_| generator(&gen).unwrap()).collect();
SystemTime::set_time(1672531200002);
for _ in 0..10 {
snowflakes.insert(generator(&gen).unwrap());
}
assert_eq!(20, snowflakes.len());
}
impl_test!(test_monotonic, monotonic, monotonic_blocking, monotonic_lock_free);
fn test_monotonic(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531200001);
let gen = SimpleSnowflakeGenerator::default();
let mut snowflakes: VecDeque<_> = (0..10).map(|_| generator(&gen).unwrap()).collect();
SystemTime::set_time(1672531200002);
for _ in 0..10 {
snowflakes.push_back(generator(&gen).unwrap());
}
let mut previous = snowflakes.pop_front().unwrap();
for snowflake in snowflakes {
assert!(previous < snowflake);
previous = snowflake;
}
}
impl_test!(
test_snowflake_exhaustion,
snowflake_exhaustion,
snowflake_exhaustion_blocking,
snowflake_exhaustion_lock_free
);
fn test_snowflake_exhaustion(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531200001);
let gen = SimpleSnowflakeGenerator::default();
for _ in 0..(1 << 12) {
match generator(&gen) {
Err(Error::FatalSnowflakeExhaustion) => {
panic!("fatal snowflake exhaustion without being limited by the epoch");
}
Err(Error::SnowflakeExhaustion) => {
panic!("snowflakes exhausted without being limited by the sequence number");
}
Err(_) => {
panic!("unexpected error while generating a snowflake");
}
Ok(_) => {}
}
}
let result = generator(&gen);
assert_eq!(Error::SnowflakeExhaustion, result.unwrap_err());
SystemTime::set_time(1672531200002);
let _ = generator(&gen).unwrap();
}
#[test]
fn malicious_snowflake_exhaustion() {
let _g = SystemTime::acquire_lock();
#[derive(Debug)]
struct Bad;
impl Layout for Bad {
fn construct_snowflake(_timestamp: u64, sequence_number: u64) -> u64 {
sequence_number
}
fn timestamp(_input: u64) -> u64 {
0
}
fn exceeds_timestamp(_input: u64) -> bool {
false
}
fn sequence_number(input: u64) -> u64 {
input
}
fn exceeds_sequence_number(_input: u64) -> bool {
false
}
fn is_valid_snowflake(_input: u64) -> bool {
true
}
}
SystemTime::set_time(1672531200000);
type BadGen = Generator<Bad, SimpleSnowflakeParams>;
let gen = BadGen::default();
gen.set_last_snowflake(u64::MAX);
#[cfg(any(
all(feature = "blocking", not(feature = "lock-free")),
all(feature = "lock-free", not(feature = "blocking"))
))]
assert_eq!(Error::SnowflakeExhaustion, BadGen::generate(&gen).unwrap_err());
#[cfg(feature = "blocking")]
assert_eq!(Error::SnowflakeExhaustion, BadGen::generate_blocking(&gen).unwrap_err());
#[cfg(feature = "lock-free")]
assert_eq!(
Error::SnowflakeExhaustion,
BadGen::generate_lock_free(&gen).unwrap_err()
);
}
#[test]
fn test_extreme_epoch() {
let _g = SystemTime::acquire_lock();
#[derive(Debug)]
struct BadEpoch;
impl Epoch for BadEpoch {
fn millis_since_unix() -> u64 {
u64::MAX
}
}
type BadGen = Generator<SimpleSnowflakeParams, BadEpoch>;
SystemTime::set_time(u64::MAX);
let gen = BadGen::default();
#[cfg(any(
all(feature = "blocking", not(feature = "lock-free")),
all(feature = "lock-free", not(feature = "blocking"))
))]
assert_eq!(Error::FatalSnowflakeExhaustion, gen.generate().unwrap_err());
#[cfg(feature = "blocking")]
assert_eq!(Error::FatalSnowflakeExhaustion, gen.generate_blocking().unwrap_err());
#[cfg(feature = "lock-free")]
assert_eq!(Error::FatalSnowflakeExhaustion, gen.generate_lock_free().unwrap_err());
}
impl_test!(
test_non_monotonic_clock,
non_monotonic_clock,
non_monotonic_clock_blocking,
non_monotonic_clock_lock_free
);
fn test_non_monotonic_clock(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531200002);
let gen = SimpleSnowflakeGenerator::default();
let before = generator(&gen).unwrap();
SystemTime::set_time(1672531200001);
let result = generator(&gen);
assert_eq!(
Error::NonMonotonicClock,
result.expect_err("non-monotonic clock not detected")
);
SystemTime::set_time(1672531200002);
let after = generator(&gen).expect("snowflake generation failed after clock was adjusted back");
assert!(before < after);
SystemTime::set_time(1672531200003);
let _ = generator(&gen).unwrap();
}
impl_test!(
test_invalid_epoch,
invalid_epoch,
invalid_epoch_blocking,
invalid_epoch_lock_free
);
fn test_invalid_epoch(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531199999);
let gen = SimpleSnowflakeGenerator::default();
assert_eq!(Error::InvalidEpoch, generator(&gen).unwrap_err());
SystemTime::set_time(1672531200000);
assert_eq!(
Snowflake::from_raw(1).unwrap(),
generator(&gen).expect("generator failed to generate a snowflake after the")
);
assert!(generator(&gen).is_ok());
}
impl_test!(
test_type_limits,
type_limits,
type_limits_blocking,
type_limits_lock_free
);
fn test_type_limits(generator: fn(&SimpleSnowflakeGenerator) -> Result<SimpleSnowflake>) {
SystemTime::set_time(1672531200000 + (1 << 51));
let gen = SimpleSnowflakeGenerator::default();
assert_eq!(Error::FatalSnowflakeExhaustion, generator(&gen).unwrap_err());
SystemTime::set_time(u64::MAX);
assert_eq!(Error::FatalSnowflakeExhaustion, generator(&gen).unwrap_err());
}
}
pub trait Layout {
fn construct_snowflake(timestamp: u64, sequence_number: u64) -> u64;
fn timestamp(input: u64) -> u64;
fn exceeds_timestamp(input: u64) -> bool;
fn sequence_number(input: u64) -> u64;
fn exceeds_sequence_number(input: u64) -> bool;
fn is_valid_snowflake(input: u64) -> bool;
}
pub trait Epoch {
fn millis_since_unix() -> u64;
}
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[repr(transparent)]
pub struct Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
inner: u64,
#[cfg_attr(feature = "serde", serde(skip))]
_marker: PhantomData<(L, E)>,
}
impl<L, E> Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
pub fn from_raw(input: u64) -> Result<Self> {
if !L::is_valid_snowflake(input) {
return Err(Error::InvalidSnowflake);
}
Ok(Self {
inner: input,
_marker: PhantomData,
})
}
#[inline]
pub fn into_inner(self) -> u64 {
self.inner
}
pub fn timestamp(&self) -> Result<SystemTime> {
SystemTime::UNIX_EPOCH
.checked_add(Duration::from_millis(
E::millis_since_unix()
.checked_add(L::timestamp(self.inner))
.ok_or(Error::FatalSnowflakeExhaustion)?,
))
.ok_or(Error::FatalSnowflakeExhaustion)
}
#[inline]
pub fn timestamp_raw(&self) -> u64 {
L::timestamp(self.inner)
}
#[inline]
pub fn sequence_number(&self) -> u64 {
L::sequence_number(self.inner)
}
}
impl<L, E> Clone for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn clone(&self) -> Self {
*self
}
}
impl<L, E> Copy for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
}
impl<L, E> Display for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.inner)
}
}
impl<L, E> PartialEq for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<L, E> Eq for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
}
impl<L, E> Hash for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<L, E> PartialOrd for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<L, E> Ord for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
fn cmp(&self, other: &Self) -> cmp::Ordering {
self.inner.cmp(&other.inner)
}
}
impl<L, E> TryFrom<u64> for Snowflake<L, E>
where
L: Layout,
E: Epoch,
{
type Error = Error;
fn try_from(value: u64) -> std::result::Result<Self, Self::Error> {
Self::from_raw(value)
}
}
#[cfg(test)]
pub mod snowflake_tests {
use crate::system_time_mock::SystemTime;
use crate::{Epoch, Error, Layout, Snowflake, SnowflakeComparator};
use std::cmp::{max, max_by, min, min_by, Ordering};
use std::fmt::Debug;
use std::time::Duration;
#[derive(Debug)]
struct SimpleParams;
impl Layout for SimpleParams {
fn construct_snowflake(timestamp: u64, sequence_number: u64) -> u64 {
assert!(!Self::exceeds_timestamp(timestamp) && !Self::exceeds_sequence_number(sequence_number));
timestamp << 22 | sequence_number
}
fn timestamp(input: u64) -> u64 {
input >> 22
}
fn exceeds_timestamp(input: u64) -> bool {
input >= 1 << 41
}
fn sequence_number(input: u64) -> u64 {
input & ((1 << 12) - 1)
}
fn exceeds_sequence_number(input: u64) -> bool {
input >= 1 << 12
}
fn is_valid_snowflake(input: u64) -> bool {
input >> 63 == 0
}
}
impl Epoch for SimpleParams {
fn millis_since_unix() -> u64 {
0
}
}
type SimpleSnowflake = Snowflake<SimpleParams, SimpleParams>;
#[test]
fn invalid_from_raw() {
assert_eq!(
Error::InvalidSnowflake,
SimpleSnowflake::from_raw(1 << 63).unwrap_err(),
"`Snowflake::from_raw` allowed creating an invalid snowflake"
);
assert_eq!(
Error::InvalidSnowflake,
SimpleSnowflake::try_from(1 << 63).unwrap_err(),
"`Snowflake::try_from` allowed creating an invalid snowflake"
);
assert_eq!(
(1 << 63) - 1,
SimpleSnowflake::from_raw((1 << 63) - 1)
.expect("`Snowflake::from_raw` rejected a valid snowflake")
.into_inner(),
"`Snowflake::from_raw` produced an unrelated snowflake"
);
assert_eq!(
(1 << 63) - 1,
SimpleSnowflake::try_from((1 << 63) - 1)
.expect("`Snowflake::try_from` rejected a valid snowflake")
.into_inner(),
"`Snowflake::try_from` produced an unrelated snowflake"
);
}
#[test]
fn timestamp() {
let snowflake = SimpleSnowflake::from_raw(123 << 22).unwrap();
assert_eq!(
SystemTime::UNIX_EPOCH.checked_add(Duration::from_millis(123)).unwrap(),
snowflake.timestamp().expect("snowflake failed to return its timestamp"),
"snowflake returned an unrelated timestamp"
);
assert_eq!(
123,
snowflake.timestamp_raw(),
"snowflake returned an unrelated raw timestamp"
);
}
#[test]
fn timestamp_custom_epoch() {
struct CustomEpoch;
const EPOCH: u64 = 1024;
impl Epoch for CustomEpoch {
fn millis_since_unix() -> u64 {
EPOCH
}
}
let snowflake = Snowflake::<SimpleParams, CustomEpoch>::from_raw(234 << 22)
.expect("failed to create snowflake with custom epoch");
assert_eq!(
SystemTime::UNIX_EPOCH
.checked_add(Duration::from_millis(234 + EPOCH))
.unwrap(),
snowflake
.timestamp()
.expect("snowflake failed to return its timestamp with a custom epoch"),
"snowflake didn't use the custom epoch to return a timestamp or returned an otherwise unrelated timestamp"
);
assert_eq!(
234,
snowflake.timestamp_raw(),
"snowflake returned an invalid raw timestamp (possibly converting it to another epoch)"
);
}
#[test]
fn sequence_number() {
assert_eq!(
0,
SimpleSnowflake::from_raw(0).unwrap().sequence_number(),
"snowflake with sequence number 0 returned invalid sequence number"
);
assert_eq!(
(1 << 12) - 1,
SimpleSnowflake::from_raw((1 << 12) - 1).unwrap().sequence_number(),
"snowflake with max sequence number returned invalid sequence number"
);
assert_eq!(
0,
SimpleSnowflake::from_raw(1 << 22).unwrap().sequence_number(),
"snowflake failed to return sequence number 0 with a non-zero timestamp"
);
assert_eq!(
(1 << 12) - 1,
SimpleSnowflake::from_raw(1 << 22 | ((1 << 12) - 1))
.unwrap()
.sequence_number(),
"snowflake failed to return max sequence number with a non-zero timestamp"
);
}
#[test]
fn comparators() {
let (foo, bar, baz) = (
SimpleSnowflake::from_raw(0).unwrap(),
SimpleSnowflake::from_raw(1 << 22).unwrap(),
SimpleSnowflake::from_raw(2 << 22).unwrap(),
);
assert!(foo < bar && bar < baz);
let (foo, bar, baz) = (
SnowflakeComparator::try_from(foo).unwrap(),
SnowflakeComparator::try_from(bar).unwrap(),
SnowflakeComparator::try_from(baz).unwrap(),
);
assert!(foo < bar && bar < baz, "snowflakes returned unrelated comparators");
}
#[test]
fn ord() {
for timestamp in 0..1 {
let foo = SimpleSnowflake::from_raw(timestamp << 22 | 2).unwrap();
let (less, equal, greater) = (
SimpleSnowflake::from_raw(timestamp << 22 | 1).unwrap(),
SimpleSnowflake::from_raw(timestamp << 22 | 2).unwrap(),
SimpleSnowflake::from_raw(timestamp << 22 | 3).unwrap(),
);
validate_partial_ord(foo, less, equal, greater);
validate_ord(foo, less, equal, greater);
}
}
#[allow(clippy::nonminimal_bool)]
pub(crate) fn validate_partial_ord<T, O>(original: T, less: O, equal: O, greater: O)
where
T: Ord + Eq + PartialOrd<O> + PartialEq<O> + Debug,
O: PartialEq<T> + PartialOrd<T> + Debug,
{
assert_ne!(original, less);
assert!(!(original == less));
assert_eq!(original, equal);
assert!(!(original != equal));
assert_ne!(original, greater);
assert!(!(original == greater));
assert_ne!(Ordering::Equal, PartialOrd::partial_cmp(&original, &less).unwrap());
assert_eq!(Ordering::Equal, PartialOrd::partial_cmp(&original, &equal).unwrap());
assert_ne!(Ordering::Equal, PartialOrd::partial_cmp(&original, &greater).unwrap());
assert_eq!(Ordering::Less, PartialOrd::partial_cmp(&less, &original).unwrap());
assert_ne!(Ordering::Less, PartialOrd::partial_cmp(&original, &equal).unwrap());
assert_eq!(Ordering::Less, PartialOrd::partial_cmp(&original, &greater).unwrap());
assert_eq!(Ordering::Greater, PartialOrd::partial_cmp(&original, &less).unwrap());
assert_ne!(Ordering::Greater, PartialOrd::partial_cmp(&original, &equal).unwrap());
assert_eq!(Ordering::Greater, PartialOrd::partial_cmp(&greater, &original).unwrap());
assert!(original <= equal && original <= greater);
assert!(!(original <= less));
assert!(original >= less && original >= equal);
assert!(!(original >= greater));
}
#[allow(clippy::eq_op)]
pub(crate) fn validate_ord<T>(original: T, less: T, equal: T, greater: T)
where
T: Eq + Ord + Debug + Copy,
{
assert_eq!(original, original);
assert_eq!(original, equal);
assert_eq!(equal, original);
assert_eq!(
PartialOrd::partial_cmp(&original, &less).unwrap(),
Ord::cmp(&original, &less)
);
assert_eq!(
PartialOrd::partial_cmp(&less, &original).unwrap(),
Ord::cmp(&less, &original)
);
assert_eq!(
PartialOrd::partial_cmp(&original, &equal).unwrap(),
Ord::cmp(&original, &equal)
);
assert_eq!(
PartialOrd::partial_cmp(&equal, &original).unwrap(),
Ord::cmp(&equal, &original)
);
assert_eq!(
PartialOrd::partial_cmp(&original, &greater).unwrap(),
Ord::cmp(&original, &greater)
);
assert_eq!(
PartialOrd::partial_cmp(&greater, &original).unwrap(),
Ord::cmp(&greater, &original)
);
assert_eq!(max(original, less), max_by(original, less, Ord::cmp));
assert_eq!(max(less, original), max_by(less, original, Ord::cmp));
assert_eq!(max(original, equal), max_by(original, equal, Ord::cmp));
assert_eq!(max(equal, original), max_by(equal, original, Ord::cmp));
assert_eq!(max(original, greater), max_by(original, greater, Ord::cmp));
assert_eq!(max(greater, original), max_by(greater, original, Ord::cmp));
assert_eq!(min(original, less), min_by(original, less, Ord::cmp));
assert_eq!(min(less, original), min_by(less, original, Ord::cmp));
assert_eq!(min(original, equal), min_by(original, equal, Ord::cmp));
assert_eq!(min(equal, original), min_by(equal, original, Ord::cmp));
assert_eq!(min(original, greater), min_by(original, greater, Ord::cmp));
assert_eq!(min(greater, original), min_by(greater, original, Ord::cmp));
}
#[test]
fn timestamp_overflow() {
#[derive(Debug)]
struct BadEpoch;
impl Epoch for BadEpoch {
fn millis_since_unix() -> u64 {
u64::MAX
}
}
let snowflake = Snowflake::<SimpleParams, BadEpoch>::from_raw((u32::MAX as u64) << 22).unwrap();
assert_eq!(Error::FatalSnowflakeExhaustion, snowflake.timestamp().unwrap_err());
let snowflake = Snowflake::<SimpleParams, BadEpoch>::from_raw((1 << 63) - 1).unwrap();
assert_eq!(Error::FatalSnowflakeExhaustion, snowflake.timestamp().unwrap_err());
}
}
pub use classic::{ClassicLayout, ClassicLayoutSnowflakeExtension, MachineId};
pub use comparator::SnowflakeComparator;
#[derive(Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
NonMonotonicClock,
InvalidEpoch,
SnowflakeExhaustion,
FatalSnowflakeExhaustion,
InvalidSnowflake,
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match self {
Error::NonMonotonicClock => {
write!(f, "can't generate a new snowflake - the clock went backwards")
}
Error::InvalidEpoch => {
write!(f, "invalid epoch provided")
}
Error::SnowflakeExhaustion => {
write!(f, "too many snowflakes were generated this millisecond")
}
Error::FatalSnowflakeExhaustion => {
write!(f, "the timestamp can't be represented by the underlying data structure")
}
Error::InvalidSnowflake => {
write!(f, "the integer representation is not a valid snowflake")
}
}
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;