use crate::NODE_CTR_SIZE;
#[cfg(doc)]
use super::Scru64Generator;
pub trait CounterMode {
fn renew(&mut self, counter_size: u8, context: &RenewContext) -> u32;
}
#[derive(Debug)]
#[non_exhaustive]
pub struct RenewContext {
pub timestamp: u64,
pub node_id: u32,
}
#[derive(Clone, Debug, Eq, PartialEq, Default)]
pub struct DefaultCounterMode {
overflow_guard_size: u8,
rng: u64,
}
impl DefaultCounterMode {
pub const fn new(overflow_guard_size: u8) -> Self {
Self {
overflow_guard_size,
rng: 0, }
}
#[cold]
fn new_rng(&self, counter_size: u8, context: &RenewContext) -> u64 {
#[cfg(feature = "std")]
let addr = (&*Box::new(42) as *const i32) as u64;
#[cfg(not(feature = "std"))]
let addr = (self as *const Self) as u64;
addr ^ ((context.timestamp << NODE_CTR_SIZE) | (u64::from(context.node_id) << counter_size))
}
}
impl CounterMode for DefaultCounterMode {
fn renew(&mut self, counter_size: u8, context: &RenewContext) -> u32 {
debug_assert!(counter_size < NODE_CTR_SIZE);
if self.rng == 0 {
self.rng = self.new_rng(counter_size, context);
}
if self.overflow_guard_size < counter_size {
let shift = 64 + self.overflow_guard_size - counter_size;
self.rng ^= self.rng >> 12;
self.rng ^= self.rng << 25;
self.rng ^= self.rng >> 27;
(self.rng.wrapping_mul(2685821657736338717) >> shift) as u32
} else {
0
}
}
}
#[cfg(test)]
#[test]
fn test_default_counter_mode() {
const N_LOOPS: usize = 4096;
let margin = 5.730729 * (0.5 * 0.5 / N_LOOPS as f64).sqrt();
let context = RenewContext {
timestamp: 0x0123_4567_89ab,
node_id: 0,
};
for counter_size in 1..NODE_CTR_SIZE {
for overflow_guard_size in 0..NODE_CTR_SIZE {
let mut counts_by_pos = [0u32; NODE_CTR_SIZE as usize];
let mut c = DefaultCounterMode::new(overflow_guard_size);
for _ in 0..N_LOOPS {
let mut n = c.renew(counter_size, &context);
for e in counts_by_pos.iter_mut() {
*e += n & 1;
n >>= 1;
}
assert_eq!(n, 0);
}
let filled = counter_size.saturating_sub(overflow_guard_size) as usize;
for &e in counts_by_pos[..filled].iter() {
assert!((e as f64 / N_LOOPS as f64 - 0.5).abs() < margin);
}
for &e in counts_by_pos[filled..].iter() {
assert_eq!(e, 0);
}
}
}
}