#[cfg(not(feature = "std"))]
use core as std;
use std::{fmt, iter};
use crate::{Id, MAX_COUNTER_HI, MAX_COUNTER_LO, MAX_TIMESTAMP};
pub trait RandSource {
fn next_u32(&mut self) -> u32;
}
#[deprecated(since = "3.3.0", note = "use `RandSource` instead")]
#[doc(hidden)]
pub use RandSource as Scru128Rng;
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)]
#[allow(deprecated)]
pub struct Generator<R = DefaultRng, T = StdSystemTime> {
timestamp: u64,
counter_hi: u32,
counter_lo: u32,
ts_counter_hi: u64,
rand_source: R,
time_source: T,
rollback_allowance: u64,
}
#[deprecated(since = "3.6.0", note = "use `Generator` instead")]
#[doc(hidden)]
pub use Generator as Scru128Generator;
#[cfg(feature = "default_rng")]
impl Generator {
#[deprecated(
since = "3.6.0",
note = "use `with_rand010()` instead. `DefaultRng` and the default type parameter `R` of `Generator` are deprecated and will be removed in the future."
)]
pub fn new() -> Self {
Default::default()
}
}
impl<R> Generator<R> {
#[deprecated(
since = "3.3.0",
note = "use `with_rand_and_time_sources()` with `StdSystemTime` instead"
)]
pub const fn with_rng(rng: R) -> Self {
Self::with_rand_and_time_sources(rng, StdSystemTime)
}
}
impl<R, T> Generator<R, T> {
pub const fn with_rand_and_time_sources(rand_source: R, time_source: T) -> Self {
Self {
timestamp: 0,
counter_hi: 0,
counter_lo: 0,
ts_counter_hi: 0,
rand_source,
time_source,
rollback_allowance: 10_000, }
}
pub fn set_rollback_allowance(&mut self, rollback_allowance: u64) {
if rollback_allowance > MAX_TIMESTAMP {
panic!("`rollback_allowance` out of reasonable range");
}
self.rollback_allowance = rollback_allowance;
}
pub(crate) fn reset_state(&mut self) {
self.timestamp = 0;
self.counter_hi = 0;
self.counter_lo = 0;
self.ts_counter_hi = 0;
}
#[cfg(feature = "global_gen")]
pub(crate) fn rand_source_mut(&mut self) -> &mut R {
&mut self.rand_source
}
}
impl<R: RandSource, T: TimeSource> Generator<R, T> {
pub fn generate(&mut self) -> Id {
let timestamp = self.time_source.unix_ts_ms();
self.generate_or_reset_with_ts(timestamp)
}
pub fn generate_or_abort(&mut self) -> Option<Id> {
let timestamp = self.time_source.unix_ts_ms();
self.generate_or_abort_with_ts(timestamp)
}
pub fn iter(&mut self) -> impl Iterator<Item = Id> {
iter::from_fn(|| Some(self.generate()))
}
}
impl<R: RandSource, T> Generator<R, T> {
pub fn generate_or_reset_with_ts(&mut self, timestamp: u64) -> Id {
if let Some(value) = self.generate_or_abort_with_ts(timestamp) {
value
} else {
self.reset_state();
self.generate_or_abort_with_ts(timestamp).unwrap()
}
}
pub fn generate_or_abort_with_ts(&mut self, timestamp: u64) -> Option<Id> {
if timestamp == 0 || timestamp > MAX_TIMESTAMP {
panic!("`timestamp` must be a 48-bit positive integer");
}
if timestamp > self.timestamp {
self.timestamp = timestamp;
self.counter_lo = self.rand_source.next_u32() & MAX_COUNTER_LO;
} else if timestamp + self.rollback_allowance >= self.timestamp {
self.counter_lo += 1;
if self.counter_lo > MAX_COUNTER_LO {
self.counter_lo = 0;
self.counter_hi += 1;
if self.counter_hi > MAX_COUNTER_HI {
self.counter_hi = 0;
self.timestamp += 1;
self.counter_lo = self.rand_source.next_u32() & MAX_COUNTER_LO;
}
}
} else {
return None;
}
if self.timestamp - self.ts_counter_hi >= 1_000 || self.ts_counter_hi == 0 {
self.ts_counter_hi = self.timestamp;
self.counter_hi = self.rand_source.next_u32() & MAX_COUNTER_HI;
}
Some(
Id::try_from_fields(
self.timestamp,
self.counter_hi,
self.counter_lo,
self.rand_source.next_u32(),
)
.unwrap(),
)
}
#[deprecated(since = "3.3.0", note = "use `generate_or_reset_with_ts()` instead")]
pub fn generate_or_reset_core(&mut self, timestamp: u64, rollback_allowance: u64) -> Id {
#[allow(deprecated)]
if let Some(value) = self.generate_or_abort_core(timestamp, rollback_allowance) {
value
} else {
self.reset_state();
self.generate_or_abort_core(timestamp, rollback_allowance)
.unwrap()
}
}
#[deprecated(since = "3.3.0", note = "use `generate_or_abort_with_ts()` instead")]
pub fn generate_or_abort_core(
&mut self,
timestamp: u64,
rollback_allowance: u64,
) -> Option<Id> {
struct PanicGuard<'a, R, T> {
orig_rollback_allowance: u64,
inner: &'a mut Generator<R, T>,
}
impl<R, T> Drop for PanicGuard<'_, R, T> {
fn drop(&mut self) {
self.inner.rollback_allowance = self.orig_rollback_allowance;
}
}
let guard = PanicGuard {
orig_rollback_allowance: self.rollback_allowance,
inner: self,
};
guard.inner.set_rollback_allowance(rollback_allowance);
guard.inner.generate_or_abort_with_ts(timestamp)
}
}
impl<R: Default, T: Default> Default for Generator<R, T> {
fn default() -> Self {
Self::with_rand_and_time_sources(R::default(), T::default())
}
}
impl<R: fmt::Debug, T: fmt::Debug> fmt::Debug for Generator<R, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
f.debug_struct("Generator")
.field("rand_source", &self.rand_source)
.field("time_source", &self.time_source)
.field("rollback_allowance", &self.rollback_allowance)
.finish_non_exhaustive()
}
}
impl<R: RandSource, T: TimeSource> Iterator for Generator<R, T> {
type Item = Id;
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 Generator<R, T> {}
#[derive(Clone, Debug)]
#[deprecated(
since = "3.6.0",
note = "this structure and the default type parameter `R` of `Generator` are deprecated and will be removed in the future. Use RNGs provided by `rand` crate."
)]
pub struct DefaultRng {
_private: (),
#[cfg(feature = "default_rng")]
inner: rand09::rngs::ReseedingRng<rand_chacha::ChaCha12Core, rand09::rngs::OsRng>,
}
#[cfg(feature = "default_rng")]
#[allow(deprecated)]
mod default_rng;
#[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;