use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use std::sync::atomic::{AtomicBool, AtomicU64, Ordering};
use std::sync::Arc;
pub struct DeterminismController {
seed: u64,
enforced: Arc<AtomicBool>,
counter: AtomicU64,
}
impl DeterminismController {
#[must_use]
pub fn new(seed: u64) -> Self {
Self {
seed,
enforced: Arc::new(AtomicBool::new(true)),
counter: AtomicU64::new(0),
}
}
#[must_use]
pub fn with_default_seed() -> Self {
Self::new(0)
}
#[must_use]
pub fn seed(&self) -> u64 {
self.seed
}
#[must_use]
pub fn derive_seed(&self, context: &str) -> u64 {
let counter = self.counter.fetch_add(1, Ordering::AcqRel);
let mut hasher = DefaultHasher::new();
self.seed.hash(&mut hasher);
context.hash(&mut hasher);
counter.hash(&mut hasher);
hasher.finish()
}
#[must_use]
pub fn is_enforced(&self) -> bool {
self.enforced.load(Ordering::Acquire)
}
pub fn set_enforced(&self, enforced: bool) {
self.enforced.store(enforced, Ordering::Release);
}
#[must_use]
pub fn content_hash(content: &str) -> u64 {
let mut hasher = DefaultHasher::new();
content.hash(&mut hasher);
hasher.finish()
}
#[must_use]
pub fn url_hash(url: &str) -> u64 {
let mut hasher = DefaultHasher::new();
url.hash(&mut hasher);
hasher.finish()
}
}
impl Default for DeterminismController {
fn default() -> Self {
Self::with_default_seed()
}
}
pub fn deterministic_sort<T>(items: &mut [T], key_fn: impl Fn(&T) -> u64) {
items.sort_by_key(|a| key_fn(a));
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_determinism_controller_seed() {
let ctrl = DeterminismController::new(42);
assert_eq!(ctrl.seed(), 42);
assert!(ctrl.is_enforced());
}
#[test]
fn test_determinism_controller_derive_seed() {
let ctrl = DeterminismController::new(42);
let seed1 = ctrl.derive_seed("context1");
let seed2 = ctrl.derive_seed("context2");
assert_ne!(seed1, seed2);
}
#[test]
fn test_determinism_controller_same_seed() {
let ctrl1 = DeterminismController::new(42);
let ctrl2 = DeterminismController::new(42);
assert_eq!(ctrl1.seed(), ctrl2.seed());
}
#[test]
fn test_content_hash_deterministic() {
let hash1 = DeterminismController::content_hash("hello");
let hash2 = DeterminismController::content_hash("hello");
assert_eq!(hash1, hash2);
}
#[test]
fn test_content_hash_different() {
let hash1 = DeterminismController::content_hash("hello");
let hash2 = DeterminismController::content_hash("world");
assert_ne!(hash1, hash2);
}
}