use std::{thread::sleep, time::Duration};
use rand::Rng;
use crate::{
Error, SmallUid,
generation::{assemble, timestamp_gen},
};
pub fn random_gen() -> u16 {
rand::rng().random_range(0..(1 << 10)) }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct MonotonicGenerator {
pub(crate) last_ms: u64,
pub(crate) lower_bits: u16, pub(crate) upper_counter: u16, }
impl MonotonicGenerator {
pub fn generate(&mut self) -> SmallUid {
generate(self).unwrap()
}
pub fn generate_batch(&mut self, count: usize) -> Vec<SmallUid> {
let mut smalluids = Vec::new();
for _ in 0..count {
smalluids.push(self.generate());
}
smalluids
}
pub fn generate_full(&mut self, timestamp: u64) -> [SmallUid; 1024] {
std::array::from_fn(|i| {
let random = random_gen() & 0x3FF;
let random = (i as u32) << 10 | random as u32;
assemble(timestamp, random as u64)
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::timestamp_gen;
#[test]
fn test_generate_single_uid() {
let mut generator = MonotonicGenerator::default();
let uid1 = generator.generate();
let uid2 = generator.generate();
assert_ne!(uid1, uid2, "Consecutive generated UIDs should be unique");
}
#[test]
fn test_generate_batch_count_and_uniqueness() {
let mut generator = MonotonicGenerator::default();
let batch_size = 100;
let uids = generator.generate_batch(batch_size);
assert_eq!(
uids.len(),
batch_size,
"Batch size should match requested count"
);
let mut seen = std::collections::HashSet::new();
for uid in uids.iter() {
assert!(seen.insert(uid), "Duplicate UID found in batch");
}
}
#[test]
fn test_generate_batch_monotonicity() {
let mut generator = MonotonicGenerator::default();
let batch_size = 50;
let uids = generator.generate_batch(batch_size);
for i in 1..uids.len() {
assert!(
uids[i] > uids[i - 1],
"UIDs are not monotonic at index {}",
i
);
}
}
#[test]
fn test_generate_full_monotonicity() {
let mut generator = MonotonicGenerator::default();
let timestamp = timestamp_gen().unwrap();
let start = std::time::Instant::now();
let uids = generator.generate_full(timestamp);
let duration = start.elapsed();
println!("Generate full take: {:?}", duration);
let mut seen = std::collections::HashSet::new();
for uid in uids.iter() {
assert!(seen.insert(uid), "Duplicate UID found in generate_full");
}
for i in 1..uids.len() {
assert!(
uids[i] > uids[i - 1],
"UIDs are not monotonic at index {}",
i
);
}
}
}
pub fn monotonic_random_gen(
generator: &mut MonotonicGenerator,
timestamp: u64,
) -> Result<u32, Error> {
if timestamp > generator.last_ms {
generator.last_ms = timestamp;
generator.lower_bits = random_gen() & 0x3FF; generator.upper_counter = 0; } else {
if generator.upper_counter >= 0x3FF {
return Err(Error::MonotonicCounterLimit);
}
generator.upper_counter += 1;
}
Ok(((generator.upper_counter as u32) << 10) | generator.lower_bits as u32)
}
pub fn generate(generator: &mut MonotonicGenerator) -> Result<SmallUid, Error> {
loop {
let timestamp = timestamp_gen()?;
match monotonic_random_gen(generator, timestamp) {
Ok(random) => return Ok(assemble(timestamp, random as u64)),
Err(_) => {
sleep(Duration::from_millis(1));
}
}
}
}