scru128 3.6.1

SCRU128: Sortable, Clock and Random number-based Unique identifier
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
//! SCRU128 generator and related items.
//!
//! This module is also exported as `scru128::gen` for backward compatibility.

#[cfg(not(feature = "std"))]
use core as std;
use std::{fmt, iter};

use crate::{Id, MAX_COUNTER_HI, MAX_COUNTER_LO, MAX_TIMESTAMP};

/// A trait that defines the minimum random number generator interface for [`Generator`].
pub trait RandSource {
    /// Returns the next random `u32`.
    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;

/// A trait that defines the minimum system clock interface for [`Generator`].
pub trait TimeSource {
    /// Returns the current Unix timestamp in milliseconds.
    fn unix_ts_ms(&mut self) -> u64;
}

/// Represents a SCRU128 ID generator that encapsulates the counters and other internal state and
/// guarantees the monotonic order of IDs generated within the same millisecond.
///
/// This type provides the interface to customize the random number generator, system clock, and
/// clock rollback handling of a SCRU128 ID generator.
///
/// # Examples
///
/// This structure is typically instantiated with a random number generator from `rand` crate via
/// the adapters enabled by the corresponding cargo features.
///
/// ```rust
/// # #[cfg(all(feature = "std", feature = "rand010"))]
/// # {
/// let mut g = scru128::Generator::with_rand010(rand::rng());
/// println!("{}", g.generate());
/// if let Some(value) = g.generate_or_abort() {
///     println!("{}", value);
/// }
/// # }
/// ```
///
/// # Generator functions
///
/// The generator comes with four different methods that generate a SCRU128 ID:
///
/// | Flavor                        | Timestamp | On big clock rewind |
/// | ----------------------------- | --------- | ------------------- |
/// | [`generate`]                  | Now       | Resets generator    |
/// | [`generate_or_abort`]         | Now       | Returns `None`      |
/// | [`generate_or_reset_with_ts`] | Argument  | Resets generator    |
/// | [`generate_or_abort_with_ts`] | Argument  | Returns `None`      |
///
/// All of the four return a monotonically increasing ID by reusing the previous `timestamp` even
/// if the one provided is smaller than the immediately preceding ID's. However, when such a clock
/// rollback is considered significant (by default, more than ten seconds):
///
/// 1.  `generate` (or_reset) methods reset the generator and return a new ID based on the given
///     `timestamp`, breaking the increasing order of IDs.
/// 2.  `or_abort` variants abort and return `None` immediately.
///
/// The `with_ts` functions accepts the `timestamp` as an argument.
///
/// [`generate`]: Generator::generate
/// [`generate_or_abort`]: Generator::generate_or_abort
/// [`generate_or_reset_with_ts`]: Generator::generate_or_reset_with_ts
/// [`generate_or_abort_with_ts`]: Generator::generate_or_abort_with_ts
#[derive(Clone, Eq, PartialEq)]
#[allow(deprecated)]
pub struct Generator<R = DefaultRng, T = StdSystemTime> {
    timestamp: u64,
    counter_hi: u32,
    counter_lo: u32,

    /// The timestamp at the last renewal of `counter_hi` field.
    ts_counter_hi: u64,

    /// The random number generator used by the generator.
    rand_source: R,

    /// The system clock used by the generator.
    time_source: T,

    /// The amount of `timestamp` rollback that is considered significant (in milliseconds).
    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 {
    /// Creates a generator object with the default random number generator.
    ///
    /// # Panics
    ///
    /// Panics in the highly unlikely event where [`DefaultRng`] could not be initialized.
    #[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> {
    /// Creates a generator object with a specified random number generator. The specified random
    /// number generator should be cryptographically strong and securely seeded.
    ///
    /// Use [`Generator::with_rand010()`] to create a generator with the random number generators
    /// from `rand` crate. Although this constructor accepts `rand::RngCore` (v0.8) types for
    /// historical reasons, such behavior is deprecated and will be removed in the future.
    #[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> {
    /// Creates a generator object with specified random number generator and system clock.
    ///
    /// Use [`with_rand010::Adapter`] to pass a random number generator from `rand` crate. Although
    /// this constructor accepts `rand::RngCore` (v0.8) types for historical reasons, such behavior
    /// is deprecated and will be removed in the future.
    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, // 10 seconds in milliseconds
        }
    }

    /// Sets the `rollback_allowance` parameter of the generator.
    ///
    /// The `rollback_allowance` parameter specifies the amount of `timestamp` rollback that is
    /// considered significant. The default value is `10_000` (milliseconds). See the [`Generator`]
    /// type documentation for the treatment of the significant rollback.
    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;
    }

    /// Resets the internal state of the generator.
    pub(crate) fn reset_state(&mut self) {
        self.timestamp = 0;
        self.counter_hi = 0;
        self.counter_lo = 0;
        self.ts_counter_hi = 0;
    }

    /// Returns a mutable reference to the inner random number source.
    #[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> {
    /// Generates a new SCRU128 ID object from the current `timestamp`, or resets the generator
    /// upon significant timestamp rollback.
    ///
    /// See the [`Generator`] type documentation for the description.
    pub fn generate(&mut self) -> Id {
        let timestamp = self.time_source.unix_ts_ms();
        self.generate_or_reset_with_ts(timestamp)
    }

    /// Generates a new SCRU128 ID object from the current `timestamp`, or returns `None` upon
    /// significant timestamp rollback.
    ///
    /// See the [`Generator`] type documentation for the description.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std", feature = "rand010"))]
    /// # {
    /// let mut g = scru128::Generator::with_rand010(rand::rng());
    /// let x = g.generate_or_abort().unwrap();
    /// let y = g
    ///     .generate_or_abort()
    ///     .expect("The clock went backwards by ten seconds!");
    /// assert!(x < y);
    /// # }
    /// ```
    pub fn generate_or_abort(&mut self) -> Option<Id> {
        let timestamp = self.time_source.unix_ts_ms();
        self.generate_or_abort_with_ts(timestamp)
    }

    /// Returns an infinite iterator that produces a new ID for each call of `next()`.
    ///
    /// # Examples
    ///
    /// ```rust
    /// # #[cfg(all(feature = "std", feature = "rand010"))]
    /// # {
    /// let mut g = scru128::Generator::with_rand010(rand::rng());
    /// for (i, e) in g.iter().take(8).enumerate() {
    ///     println!("[{}] {}", i, e);
    /// }
    /// # }
    /// ```
    pub fn iter(&mut self) -> impl Iterator<Item = Id> {
        iter::from_fn(|| Some(self.generate()))
    }
}

impl<R: RandSource, T> Generator<R, T> {
    /// Generates a new SCRU128 ID object from the `timestamp` passed, or resets the generator upon
    /// significant timestamp rollback.
    ///
    /// See the [`Generator`] type documentation for the description.
    ///
    /// # Panics
    ///
    /// Panics if `timestamp` is not a 48-bit positive integer.
    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 {
            // reset state and resume
            self.reset_state();
            self.generate_or_abort_with_ts(timestamp).unwrap()
        }
    }

    /// Generates a new SCRU128 ID object from the `timestamp` passed, or returns `None` upon
    /// significant timestamp rollback.
    ///
    /// See the [`Generator`] type documentation for the description.
    ///
    /// # Panics
    ///
    /// Panics if `timestamp` is not a 48-bit positive integer.
    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 {
            // go on with previous timestamp if new one is not much smaller
            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;
                    // increment timestamp at counter overflow
                    self.timestamp += 1;
                    self.counter_lo = self.rand_source.next_u32() & MAX_COUNTER_LO;
                }
            }
        } else {
            // abort if clock went backwards to unbearable extent
            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(),
        )
    }

    /// Generates a new SCRU128 ID object from the `timestamp` passed, or resets the generator upon
    /// significant timestamp rollback.
    ///
    /// This method is a deprecated version of `generate_or_reset_with_ts()` that accepts the
    /// `rollback_allowance` parameter as an argument, rather than using [the generator-level
    /// parameter](Self::set_rollback_allowance).
    ///
    /// # Panics
    ///
    /// Panics if `timestamp` is not a 48-bit positive integer.
    #[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 {
            // reset state and resume
            self.reset_state();
            self.generate_or_abort_core(timestamp, rollback_allowance)
                .unwrap()
        }
    }

    /// Generates a new SCRU128 ID object from the `timestamp` passed, or returns `None` upon
    /// significant timestamp rollback.
    ///
    /// This method is a deprecated version of `generate_or_abort_with_ts()` that accepts the
    /// `rollback_allowance` parameter as an argument, rather than using [the generator-level
    /// parameter](Self::set_rollback_allowance).
    ///
    /// # Panics
    ///
    /// Panics if `timestamp` is not a 48-bit positive integer.
    #[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()
    }
}

/// `Generator` behaves as an infinite iterator that produces a new ID for each call of `next()`.
/// This implementation is deprecated; use [`iter()`](Generator::iter) instead.
///
/// # Examples
///
/// ```rust
/// # #[cfg(all(feature = "std", feature = "rand010"))]
/// # {
/// let g = scru128::Generator::with_rand010(rand::rng());
/// for (i, e) in g.take(8).enumerate() {
///     println!("[{}] {}", i, e);
/// }
/// # }
/// ```
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> {}

/// The default random number generator used by [`Generator`].
///
/// Currently, `DefaultRng` uses [`ChaCha12Core`] that is initially seeded and subsequently
/// reseeded by [`OsRng`] every 64 kiB of random data using the [`ReseedingRng`] wrapper. It is the
/// same strategy as that employed by [`ThreadRng`]; see the docs of `rand` crate for a detailed
/// discussion on the strategy.
///
/// This structure does exist without the `default_rng` feature flag but is not able to be
/// instantiated or used as a random number generator.
///
/// [`ChaCha12Core`]: rand_chacha::ChaCha12Core
/// [`ReseedingRng`]: rand09::rngs::ReseedingRng
/// [`OsRng`]: rand09::rngs::OsRng
/// [`ThreadRng`]: rand09::rngs::ThreadRng
#[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;

/// The default [`TimeSource`] that uses [`std::time::SystemTime`].
#[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;