#[cfg(feature = "rand")]
use std::time::SystemTime;
use std::{fmt, ops::RangeInclusive, sync::Mutex};
#[cfg(feature = "rand")]
use rand::{
RngExt as _,
SeedableRng as _, rngs::{StdRng, SysRng}, };
use crate::{RANDOM_BITS, RANDOM_GEN_MAX, TIMESTAMP_MASK, TIMESTAMP_MAX};
pub trait EntropySource: Send {
fn timestamp(&mut self) -> Option<u64>;
fn random(&mut self, range: RangeInclusive<u128>) -> Option<u128>;
}
#[repr(transparent)]
pub struct EntropySourceHandle {
inner: InnerHandle,
}
enum InnerHandle {
NoOp,
#[cfg(feature = "rand")]
Standard,
Custom(Box<dyn EntropySource>),
}
impl EntropySourceHandle {
#[must_use]
pub fn new(source: impl EntropySource + 'static) -> Self {
Self {
inner: InnerHandle::Custom(Box::new(source)),
}
}
}
impl fmt::Debug for EntropySourceHandle {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("EntropySourceHandle { ... }")
}
}
#[cfg(feature = "rand")]
pub const STANDARD_ENTROPY_SOURCE: EntropySourceHandle = EntropySourceHandle {
inner: InnerHandle::Standard,
};
pub const NO_ENTROPY_SOURCE: EntropySourceHandle = EntropySourceHandle {
inner: InnerHandle::NoOp,
};
struct Generator {
source: EntropySourceHandle,
#[cfg(feature = "rand")]
rng: Option<StdRng>,
last_ulid: u128,
}
impl Generator {
#[must_use]
fn generate(&mut self) -> Option<u128> {
let now = self.timestamp()?;
assert!(now < TIMESTAMP_MAX);
let timestamp = u128::from(now) << RANDOM_BITS;
let last_timestamp = self.last_ulid & TIMESTAMP_MASK;
let ulid = if timestamp > last_timestamp {
let random = self.random(1..=RANDOM_GEN_MAX)?;
timestamp | random
} else {
self.last_ulid.checked_add(1)?
};
assert!(ulid > self.last_ulid);
self.last_ulid = ulid;
Some(ulid)
}
#[must_use]
fn timestamp(&mut self) -> Option<u64> {
let candidate = match &mut self.source.inner {
InnerHandle::NoOp => None,
#[cfg(feature = "rand")]
InnerHandle::Standard => {
let now = SystemTime::now();
let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).ok()?;
let millis = since_epoch.as_millis();
u64::try_from(millis).ok()
}
InnerHandle::Custom(source) => source.timestamp(),
}?;
(candidate < TIMESTAMP_MAX).then_some(candidate)
}
#[must_use]
fn random(&mut self, range: RangeInclusive<u128>) -> Option<u128> {
let candidate = match &mut self.source.inner {
InnerHandle::NoOp => None,
#[cfg(feature = "rand")]
InnerHandle::Standard => {
let rng = self
.rng
.get_or_insert_with(|| StdRng::try_from_rng(&mut SysRng).unwrap());
Some(rng.random_range(range.clone()))
}
InnerHandle::Custom(source) => {
source.random(range.clone())
}
}?;
range.contains(&candidate).then_some(candidate)
}
}
static GENERATOR: Mutex<Generator> = {
#[cfg(feature = "rand")]
let generator = Generator {
source: STANDARD_ENTROPY_SOURCE,
rng: None,
last_ulid: 0,
};
#[cfg(not(feature = "rand"))]
let generator = Generator {
source: NO_ENTROPY_SOURCE,
last_ulid: 0,
};
Mutex::new(generator)
};
pub(crate) fn generate() -> Option<u128> {
let mut generator = GENERATOR.lock().ok()?;
generator.generate()
}
pub fn set_entropy_source(source: EntropySourceHandle) -> EntropySourceHandle {
let mut generator = GENERATOR.lock().unwrap_or_else(|poisoned| {
GENERATOR.clear_poison();
poisoned.into_inner()
});
std::mem::replace(&mut generator.source, source)
}
#[cfg(feature = "rand")]
#[cfg(test)]
mod tests {
use super::*;
use crate::Ulid;
fn manipulate_generator_last_ulid(last_id: u128) {
let mut generator = GENERATOR.lock().unwrap();
generator.last_ulid = last_id;
}
struct RestoreStandardSource;
impl Drop for RestoreStandardSource {
fn drop(&mut self) {
set_entropy_source(STANDARD_ENTROPY_SOURCE);
manipulate_generator_last_ulid(0);
}
}
struct FixedEntropySource {
timestamp: u64,
random: u128,
}
impl FixedEntropySource {
fn install(timestamp: u64, random: u128) -> RestoreStandardSource {
let source = Self { timestamp, random };
let handle = EntropySourceHandle::new(source);
set_entropy_source(handle);
RestoreStandardSource
}
}
impl EntropySource for FixedEntropySource {
fn timestamp(&mut self) -> Option<u64> {
Some(self.timestamp)
}
fn random(&mut self, _range: RangeInclusive<u128>) -> Option<u128> {
Some(self.random)
}
}
#[test]
fn test_generator_overflow() {
let _restore = FixedEntropySource::install(1, 1);
let u1 = Ulid::new();
assert_eq!(u1.timestamp(), 1);
assert_eq!(u1.randomness(), 1);
let u2 = Ulid::new();
assert_eq!(u2.timestamp(), 1);
assert_eq!(u2.randomness(), 2);
manipulate_generator_last_ulid((1 << RANDOM_BITS) | ((1 << RANDOM_BITS) - 2));
let u3 = Ulid::new();
assert_eq!(u3.timestamp(), 1);
assert_eq!(u3.randomness(), (1 << RANDOM_BITS) - 1);
let u4 = Ulid::new();
assert_eq!(u4.timestamp(), 2);
assert_eq!(u4.randomness(), 0);
let u5 = Ulid::new();
assert_eq!(u5.timestamp(), 2);
assert_eq!(u5.randomness(), 1);
manipulate_generator_last_ulid(u128::MAX - 1);
let u6 = Ulid::new();
assert_eq!(u6.to_u128(), u128::MAX);
assert!(Ulid::try_new().is_none());
}
#[test]
fn test_debug() {
struct TestSource;
impl EntropySource for TestSource {
fn timestamp(&mut self) -> Option<u64> {
None
}
fn random(&mut self, _range: RangeInclusive<u128>) -> Option<u128> {
None
}
}
let handle = EntropySourceHandle::new(TestSource);
assert_eq!(format!("{handle:?}"), "EntropySourceHandle { ... }");
assert_eq!(format!("{STANDARD_ENTROPY_SOURCE:?}"), "EntropySourceHandle { ... }");
assert_eq!(format!("{NO_ENTROPY_SOURCE:?}"), "EntropySourceHandle { ... }");
}
}