#[cfg(not(feature = "std"))]
use core as std;
use std::{fmt, iter};
use crate::Uuid;
pub trait RandSource {
fn next_u32(&mut self) -> u32;
fn next_u64(&mut self) -> u64;
}
#[deprecated(since = "1.5.0", note = "use `RandSource` instead")]
#[doc(hidden)]
pub use RandSource as Rng;
pub mod with_rand010;
pub mod with_rand08;
pub mod with_rand09;
pub trait TimeSource {
fn unix_ts_ms(&mut self) -> u64;
}
#[derive(Clone, Eq, PartialEq)]
pub struct V7Generator<R, T = StdSystemTime> {
timestamp_biased: u64,
counter: u64,
rand_source: R,
time_source: T,
rollback_allowance: u64,
}
impl<R> V7Generator<R> {
pub const fn new(rand_source: R) -> Self {
Self::with_rand_and_time_sources(rand_source, StdSystemTime)
}
}
impl<R, T> V7Generator<R, T> {
pub const fn with_rand_and_time_sources(rand_source: R, time_source: T) -> Self {
Self {
timestamp_biased: 0,
counter: 0,
rand_source,
time_source,
rollback_allowance: 10_000, }
}
pub fn set_rollback_allowance(&mut self, rollback_allowance: u64) {
if rollback_allowance >= 1 << 48 {
panic!("`rollback_allowance` out of reasonable range");
}
self.rollback_allowance = rollback_allowance;
}
pub(crate) fn reset_state(&mut self) {
self.timestamp_biased = 0;
self.counter = 0;
}
#[cfg(feature = "global_gen")]
pub(crate) fn rand_source_mut(&mut self) -> &mut R {
&mut self.rand_source
}
}
impl<R: RandSource, T: TimeSource> V7Generator<R, T> {
pub fn generate(&mut self) -> Uuid {
let unix_ts_ms = self.time_source.unix_ts_ms();
self.generate_or_reset_with_ts(unix_ts_ms)
}
pub fn generate_or_abort(&mut self) -> Option<Uuid> {
let unix_ts_ms = self.time_source.unix_ts_ms();
self.generate_or_abort_with_ts(unix_ts_ms)
}
pub fn iter(&mut self) -> impl Iterator<Item = Uuid> {
iter::from_fn(|| Some(self.generate()))
}
}
impl<R: RandSource, T> V7Generator<R, T> {
pub fn generate_or_reset_with_ts(&mut self, unix_ts_ms: u64) -> Uuid {
if let Some(value) = self.generate_or_abort_with_ts(unix_ts_ms) {
value
} else {
self.reset_state();
self.generate_or_abort_with_ts(unix_ts_ms).unwrap()
}
}
pub fn generate_or_abort_with_ts(&mut self, unix_ts_ms: u64) -> Option<Uuid> {
if unix_ts_ms >= 1 << 48 {
panic!("`unix_ts_ms` must be a 48-bit unsigned integer");
}
const MAX_COUNTER: u64 = (1 << 42) - 1;
let unix_ts_ms = unix_ts_ms + 1;
if unix_ts_ms > self.timestamp_biased {
self.timestamp_biased = unix_ts_ms;
self.counter = self.rand_source.next_u64() & MAX_COUNTER;
} else if unix_ts_ms + self.rollback_allowance >= self.timestamp_biased {
self.counter += 1;
if self.counter > MAX_COUNTER {
self.timestamp_biased += 1;
self.counter = self.rand_source.next_u64() & MAX_COUNTER;
}
} else {
return None;
}
Some(
Uuid::try_from_fields_v7(
self.timestamp_biased - 1,
(self.counter >> 30) as u16,
((self.counter & 0x3fff_ffff) << 32) | self.rand_source.next_u32() as u64,
)
.unwrap(),
)
}
#[deprecated(since = "1.5.0", note = "use `generate_or_reset_with_ts()` instead")]
pub fn generate_or_reset_core(&mut self, unix_ts_ms: u64, rollback_allowance: u64) -> Uuid {
let guard = GenerateCorePanicGuard {
orig_rollback_allowance: self.rollback_allowance,
inner: self,
};
guard.inner.set_rollback_allowance(rollback_allowance);
guard.inner.generate_or_reset_with_ts(unix_ts_ms)
}
#[deprecated(since = "1.5.0", note = "use `generate_or_abort_with_ts()` instead")]
pub fn generate_or_abort_core(
&mut self,
unix_ts_ms: u64,
rollback_allowance: u64,
) -> Option<Uuid> {
let guard = GenerateCorePanicGuard {
orig_rollback_allowance: self.rollback_allowance,
inner: self,
};
guard.inner.set_rollback_allowance(rollback_allowance);
guard.inner.generate_or_abort_with_ts(unix_ts_ms)
}
}
struct GenerateCorePanicGuard<'a, R, T> {
orig_rollback_allowance: u64,
inner: &'a mut V7Generator<R, T>,
}
impl<R, T> Drop for GenerateCorePanicGuard<'_, R, T> {
fn drop(&mut self) {
self.inner.rollback_allowance = self.orig_rollback_allowance;
}
}
impl<R: Default, T: Default> Default for V7Generator<R, T> {
fn default() -> Self {
Self::with_rand_and_time_sources(R::default(), T::default())
}
}
impl<R, T: fmt::Debug> fmt::Debug for V7Generator<R, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("V7Generator")
.field("time_source", &self.time_source)
.field("rollback_allowance", &self.rollback_allowance)
.finish_non_exhaustive()
}
}
impl<R: RandSource, T: TimeSource> Iterator for V7Generator<R, T> {
type Item = Uuid;
fn next(&mut self) -> Option<Self::Item> {
Some(self.generate())
}
fn size_hint(&self) -> (usize, Option<usize>) {
(usize::MAX, None)
}
}
impl<R: RandSource, T: TimeSource> iter::FusedIterator for V7Generator<R, T> {}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct StdSystemTime;
#[cfg(feature = "std")]
impl TimeSource for StdSystemTime {
fn unix_ts_ms(&mut self) -> u64 {
use std::time;
time::SystemTime::now()
.duration_since(time::UNIX_EPOCH)
.expect("clock may have gone backwards")
.as_millis() as u64
}
}
#[cfg(test)]
mod tests;