Now let's take a silly one
0

Configure Feed

Select the types of activity you want to include in your feed.

at main 4.6 kB View raw
1use std::sync::atomic::{AtomicU64, Ordering}; 2 3const GOLDEN_GAMMA: u64 = 0x9E37_79B9_7F4A_7C15; 4 5fn splitmix64(z: u64) -> u64 { 6 let z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); 7 let z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); 8 z ^ (z >> 31) 9} 10 11pub trait Entropy: Send + Sync + 'static { 12 fn next_u64(&self) -> u64; 13 fn fill(&self, buffer: &mut [u8]); 14 fn derive(&self, label: u64) -> Box<dyn Entropy>; 15} 16 17pub struct OsEntropy; 18 19impl Entropy for OsEntropy { 20 fn next_u64(&self) -> u64 { 21 let mut bytes = [0u8; 8]; 22 getrandom::fill(&mut bytes).expect("OS entropy unavailable"); 23 u64::from_le_bytes(bytes) 24 } 25 26 fn fill(&self, buffer: &mut [u8]) { 27 getrandom::fill(buffer).expect("OS entropy unavailable"); 28 } 29 30 fn derive(&self, _label: u64) -> Box<dyn Entropy> { 31 Box::new(OsEntropy) 32 } 33} 34 35/// A reproducible splitmix64 stream for tests and deterministic simulation. 36/// 37/// Draws from one instance are reproducible only when taken sequentially: a 38/// single instance shared across concurrent tasks interleaves its atomic 39/// counter nondeterministically. To fan entropy out to concurrent consumers, 40/// give each its own substream with [`SeededEntropy::derive`], which is a pure 41/// function of the parent seed and a label and so is independent of draw 42/// timing. That keeps the whole fan-out reproducible regardless of scheduling. 43pub struct SeededEntropy { 44 seed: u64, 45 state: AtomicU64, 46} 47 48impl SeededEntropy { 49 pub fn new(seed: u64) -> Self { 50 Self { 51 seed, 52 state: AtomicU64::new(seed), 53 } 54 } 55 56 pub fn derive(&self, label: u64) -> SeededEntropy { 57 SeededEntropy::new(splitmix64(self.seed ^ splitmix64(label))) 58 } 59} 60 61impl Entropy for SeededEntropy { 62 fn next_u64(&self) -> u64 { 63 let z = self 64 .state 65 .fetch_add(GOLDEN_GAMMA, Ordering::SeqCst) 66 .wrapping_add(GOLDEN_GAMMA); 67 splitmix64(z) 68 } 69 70 fn fill(&self, buffer: &mut [u8]) { 71 buffer.chunks_mut(8).for_each(|chunk| { 72 let value = self.next_u64().to_le_bytes(); 73 chunk.copy_from_slice(&value[..chunk.len()]); 74 }); 75 } 76 77 fn derive(&self, label: u64) -> Box<dyn Entropy> { 78 Box::new(SeededEntropy::derive(self, label)) 79 } 80} 81 82#[cfg(test)] 83mod tests { 84 use super::*; 85 86 fn stream(entropy: &SeededEntropy, count: usize) -> Vec<u64> { 87 std::iter::repeat_with(|| entropy.next_u64()) 88 .take(count) 89 .collect() 90 } 91 92 #[test] 93 fn seeded_entropy_is_deterministic() { 94 let one = SeededEntropy::new(42); 95 let two = SeededEntropy::new(42); 96 assert_eq!(stream(&one, 256), stream(&two, 256)); 97 } 98 99 #[test] 100 fn distinct_seeds_diverge() { 101 assert_ne!( 102 stream(&SeededEntropy::new(1), 64), 103 stream(&SeededEntropy::new(2), 64) 104 ); 105 } 106 107 #[test] 108 fn derive_is_independent_of_parent_draw_timing() { 109 let early = SeededEntropy::new(42); 110 let child_before = early.derive(7); 111 112 let late = SeededEntropy::new(42); 113 let _ = stream(&late, 100); 114 let child_after = late.derive(7); 115 116 assert_eq!(stream(&child_before, 128), stream(&child_after, 128)); 117 } 118 119 #[test] 120 fn derived_streams_differ_by_label() { 121 let parent = SeededEntropy::new(42); 122 assert_ne!(stream(&parent.derive(1), 64), stream(&parent.derive(2), 64)); 123 } 124 125 fn first_fill(entropy: &dyn Entropy, label: u64) -> [u8; 16] { 126 let mut buffer = [0u8; 16]; 127 entropy.derive(label).fill(&mut buffer); 128 buffer 129 } 130 131 #[test] 132 fn trait_object_derive_is_independent_of_draw_order() { 133 let parent: &dyn Entropy = &SeededEntropy::new(77); 134 let in_order = [first_fill(parent, 10), first_fill(parent, 20)]; 135 136 let parent: &dyn Entropy = &SeededEntropy::new(77); 137 let reversed = [first_fill(parent, 20), first_fill(parent, 10)]; 138 139 assert_eq!(in_order[0], reversed[1]); 140 assert_eq!(in_order[1], reversed[0]); 141 assert_ne!(in_order[0], in_order[1]); 142 } 143 144 #[test] 145 fn seeded_fill_matches_stream() { 146 let stream = SeededEntropy::new(7); 147 let expected = stream.next_u64().to_le_bytes(); 148 let bytes = SeededEntropy::new(7); 149 let mut buffer = [0u8; 8]; 150 bytes.fill(&mut buffer); 151 assert_eq!(buffer, expected); 152 } 153 154 #[test] 155 fn os_entropy_fills() { 156 let mut buffer = [0u8; 16]; 157 OsEntropy.fill(&mut buffer); 158 assert!(buffer.iter().any(|byte| *byte != 0)); 159 } 160}