use std::sync::OnceLock;
use std::sync::atomic::{AtomicI32, Ordering};
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Debug)]
pub struct Sequence {
base: OnceLock<i32>,
next: AtomicI32,
}
impl Sequence {
pub const fn new() -> Self {
Sequence {
base: OnceLock::new(),
next: AtomicI32::new(0),
}
}
pub fn next_i32(&self) -> i32 {
let base = *self.base.get_or_init(|| {
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_nanos();
((nanos as i64) & 0x3fff_0000) as i32
});
base + self.next.fetch_add(1, Ordering::Relaxed)
}
pub fn next_i64(&self) -> i64 {
i64::from(self.next_i32())
}
}
impl Default for Sequence {
fn default() -> Self {
Sequence::new()
}
}
#[cfg(test)]
mod tests {
use std::collections::HashSet;
use super::*;
#[test]
fn a_static_sequence_yields_distinct_positive_i32_values() {
static SEQ: Sequence = Sequence::new();
let mut seen = HashSet::new();
for _ in 0..200 {
let v = SEQ.next_i32();
assert!(v >= 0);
assert!(seen.insert(v), "sequence repeated {v}");
}
}
#[test]
fn the_i64_getter_shares_the_counter_with_the_i32_one() {
let seq = Sequence::new();
let a = seq.next_i32();
let b = seq.next_i64();
assert_eq!(b, i64::from(a) + 1, "one counter behind both getters");
}
}