1#[cfg(feature = "rand")]
2use std::time::SystemTime;
3use std::{fmt, ops::RangeInclusive, sync::Mutex};
4
5#[cfg(feature = "rand")]
6use rand::{
7 RngExt as _,
8 SeedableRng as _, rngs::{StdRng, SysRng}, };
11
12use crate::{RANDOM_BITS, RANDOM_GEN_MAX, TIMESTAMP_MASK, TIMESTAMP_MAX};
13
14pub trait EntropySource: Send {
45 fn timestamp(&mut self) -> Option<u64>;
47
48 fn random(&mut self, range: RangeInclusive<u128>) -> Option<u128>;
50}
51
52#[repr(transparent)]
63pub struct EntropySourceHandle {
64 inner: InnerHandle,
65}
66
67enum InnerHandle {
68 NoOp,
69 #[cfg(feature = "rand")]
70 Standard,
71 Custom(Box<dyn EntropySource>),
72}
73
74impl EntropySourceHandle {
75 #[must_use]
77 pub fn new(source: impl EntropySource + 'static) -> Self {
78 Self {
79 inner: InnerHandle::Custom(Box::new(source)),
80 }
81 }
82}
83
84impl fmt::Debug for EntropySourceHandle {
85 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86 f.write_str("EntropySourceHandle { ... }")
87 }
88}
89
90#[cfg(feature = "rand")]
108pub const STANDARD_ENTROPY_SOURCE: EntropySourceHandle = EntropySourceHandle {
109 inner: InnerHandle::Standard,
110};
111
112pub const NO_ENTROPY_SOURCE: EntropySourceHandle = EntropySourceHandle {
132 inner: InnerHandle::NoOp,
133};
134
135struct Generator {
136 source: EntropySourceHandle,
137 #[cfg(feature = "rand")]
138 rng: Option<StdRng>,
139 last_ulid: u128,
140}
141
142impl Generator {
143 #[must_use]
144 fn generate(&mut self) -> Option<u128> {
145 let now = self.timestamp()?;
146 assert!(now < TIMESTAMP_MAX); let timestamp = u128::from(now) << RANDOM_BITS;
149 let last_timestamp = self.last_ulid & TIMESTAMP_MASK;
150
151 let ulid = if timestamp > last_timestamp {
152 let random = self.random(1..=RANDOM_GEN_MAX)?;
154 timestamp | random
155 } else {
156 self.last_ulid.checked_add(1)?
157 };
158
159 assert!(ulid > self.last_ulid);
160
161 self.last_ulid = ulid;
162
163 Some(ulid)
164 }
165
166 #[must_use]
167 fn timestamp(&mut self) -> Option<u64> {
168 let candidate = match &mut self.source.inner {
169 InnerHandle::NoOp => None,
170 #[cfg(feature = "rand")]
171 InnerHandle::Standard => {
172 let now = SystemTime::now();
173 let since_epoch = now.duration_since(SystemTime::UNIX_EPOCH).ok()?;
174 let millis = since_epoch.as_millis();
175 u64::try_from(millis).ok()
176 }
177 InnerHandle::Custom(source) => source.timestamp(),
178 }?;
179
180 (candidate < TIMESTAMP_MAX).then_some(candidate)
182 }
183
184 #[must_use]
185 fn random(&mut self, range: RangeInclusive<u128>) -> Option<u128> {
186 let candidate = match &mut self.source.inner {
187 InnerHandle::NoOp => None,
188 #[cfg(feature = "rand")]
189 InnerHandle::Standard => {
190 let rng = self
191 .rng
192 .get_or_insert_with(|| StdRng::try_from_rng(&mut SysRng).unwrap()); Some(rng.random_range(range.clone()))
196 }
197 InnerHandle::Custom(source) => {
198 source.random(range.clone())
200 }
201 }?;
202
203 range.contains(&candidate).then_some(candidate)
205 }
206}
207
208static GENERATOR: Mutex<Generator> = {
209 #[cfg(feature = "rand")]
210 let generator = Generator {
211 source: STANDARD_ENTROPY_SOURCE,
212 rng: None,
213 last_ulid: 0,
214 };
215
216 #[cfg(not(feature = "rand"))]
217 let generator = Generator {
218 source: NO_ENTROPY_SOURCE,
219 last_ulid: 0,
220 };
221
222 Mutex::new(generator)
223};
224
225pub(crate) fn generate() -> Option<u128> {
226 let mut generator = GENERATOR.lock().ok()?;
227 generator.generate()
228}
229
230pub fn set_entropy_source(source: EntropySourceHandle) -> EntropySourceHandle {
237 let mut generator = GENERATOR.lock().unwrap_or_else(|poisoned| {
238 GENERATOR.clear_poison();
239 poisoned.into_inner()
240 });
241
242 std::mem::replace(&mut generator.source, source)
243}
244
245#[cfg(feature = "rand")]
246#[cfg(test)]
247mod tests {
248 use super::*;
249 use crate::Ulid;
250
251 fn manipulate_generator_last_ulid(last_id: u128) {
252 let mut generator = GENERATOR.lock().unwrap();
253 generator.last_ulid = last_id;
254 }
255
256 struct RestoreStandardSource;
257 impl Drop for RestoreStandardSource {
258 fn drop(&mut self) {
259 set_entropy_source(STANDARD_ENTROPY_SOURCE);
260 manipulate_generator_last_ulid(0);
261 }
262 }
263
264 struct FixedEntropySource {
265 timestamp: u64,
266 random: u128,
267 }
268 impl FixedEntropySource {
269 fn install(timestamp: u64, random: u128) -> RestoreStandardSource {
270 let source = Self { timestamp, random };
271 let handle = EntropySourceHandle::new(source);
272 set_entropy_source(handle);
273 RestoreStandardSource
274 }
275 }
276 impl EntropySource for FixedEntropySource {
277 fn timestamp(&mut self) -> Option<u64> {
278 Some(self.timestamp)
279 }
280 fn random(&mut self, _range: RangeInclusive<u128>) -> Option<u128> {
281 Some(self.random)
282 }
283 }
284
285 #[test]
286 fn test_generator_overflow() {
287 let _restore = FixedEntropySource::install(1, 1);
288
289 let u1 = Ulid::new();
290 assert_eq!(u1.timestamp(), 1);
291 assert_eq!(u1.randomness(), 1);
292
293 let u2 = Ulid::new();
294 assert_eq!(u2.timestamp(), 1);
295 assert_eq!(u2.randomness(), 2);
296
297 manipulate_generator_last_ulid((1 << RANDOM_BITS) | ((1 << RANDOM_BITS) - 2));
298
299 let u3 = Ulid::new();
300 assert_eq!(u3.timestamp(), 1);
301 assert_eq!(u3.randomness(), (1 << RANDOM_BITS) - 1);
302
303 let u4 = Ulid::new();
304 assert_eq!(u4.timestamp(), 2);
305 assert_eq!(u4.randomness(), 0);
306
307 let u5 = Ulid::new();
308 assert_eq!(u5.timestamp(), 2);
309 assert_eq!(u5.randomness(), 1);
310
311 manipulate_generator_last_ulid(u128::MAX - 1);
312
313 let u6 = Ulid::new();
314 assert_eq!(u6.to_u128(), u128::MAX);
315
316 assert!(Ulid::try_new().is_none());
317 }
318
319 #[test]
320 fn test_debug() {
321 struct TestSource;
322
323 impl EntropySource for TestSource {
324 fn timestamp(&mut self) -> Option<u64> {
325 None
326 }
327 fn random(&mut self, _range: RangeInclusive<u128>) -> Option<u128> {
328 None
329 }
330 }
331
332 let handle = EntropySourceHandle::new(TestSource);
333
334 assert_eq!(format!("{handle:?}"), "EntropySourceHandle { ... }");
335 assert_eq!(format!("{STANDARD_ENTROPY_SOURCE:?}"), "EntropySourceHandle { ... }");
336 assert_eq!(format!("{NO_ENTROPY_SOURCE:?}"), "EntropySourceHandle { ... }");
337 }
338}