1use std::fmt;
11
12#[derive(Clone, Copy, PartialEq, Eq, Hash)]
17pub struct VortexSeed {
18 hi: u64,
19 lo: u64,
20}
21
22impl VortexSeed {
23 pub const fn new(hi: u64, lo: u64) -> Self {
25 Self { hi, lo }
26 }
27
28 pub const fn from_u64(val: u64) -> Self {
31 Self { hi: 0, lo: val }
32 }
33
34 pub const fn hi(&self) -> u64 {
36 self.hi
37 }
38
39 pub const fn lo(&self) -> u64 {
41 self.lo
42 }
43
44 pub const fn to_u64(&self) -> u64 {
47 self.hi ^ self.lo
48 }
49
50 pub const fn to_bytes(&self) -> [u8; 16] {
52 let hi = self.hi.to_be_bytes();
53 let lo = self.lo.to_be_bytes();
54 [
55 hi[0], hi[1], hi[2], hi[3], hi[4], hi[5], hi[6], hi[7], lo[0], lo[1], lo[2], lo[3],
56 lo[4], lo[5], lo[6], lo[7],
57 ]
58 }
59
60 pub fn from_bytes(bytes: [u8; 16]) -> Self {
62 let hi = u64::from_be_bytes([
63 bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7],
64 ]);
65 let lo = u64::from_be_bytes([
66 bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15],
67 ]);
68 Self { hi, lo }
69 }
70}
71
72impl fmt::Debug for VortexSeed {
73 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 write!(f, "VortexSeed(0x{:016x}{:016x})", self.hi, self.lo)
75 }
76}
77
78impl fmt::Display for VortexSeed {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 write!(f, "{:016x}{:016x}", self.hi, self.lo)
81 }
82}
83
84impl From<u64> for VortexSeed {
85 fn from(val: u64) -> Self {
86 Self::from_u64(val)
87 }
88}
89
90pub struct SeedTree {
110 master: VortexSeed,
111}
112
113impl SeedTree {
114 pub const fn new(master: VortexSeed) -> Self {
116 Self { master }
117 }
118
119 pub fn derive(&self, domain: &str) -> VortexSeed {
126 let mut hash_lo: u64 = 14695981039346656037; let mut hash_hi: u64 = 6700417; for byte in self.master.to_bytes() {
132 hash_lo ^= byte as u64;
133 hash_lo = hash_lo.wrapping_mul(1099511628211); hash_hi ^= byte as u64;
135 hash_hi = hash_hi.wrapping_mul(309485009821345068);
136 }
137
138 for byte in domain.as_bytes() {
140 hash_lo ^= *byte as u64;
141 hash_lo = hash_lo.wrapping_mul(1099511628211);
142 hash_hi ^= *byte as u64;
143 hash_hi = hash_hi.wrapping_mul(309485009821345068);
144 }
145
146 VortexSeed::new(hash_hi, hash_lo)
147 }
148
149 pub fn subtree(&self, domain: &str) -> SeedTree {
160 SeedTree::new(self.derive(domain))
161 }
162
163 pub const fn master(&self) -> VortexSeed {
165 self.master
166 }
167}
168
169#[cfg(test)]
170mod tests {
171 use super::*;
172
173 #[test]
174 fn test_seed_from_u64() {
175 let s = VortexSeed::from_u64(42);
176 assert_eq!(s.hi(), 0);
177 assert_eq!(s.lo(), 42);
178 assert_eq!(s.to_u64(), 42);
179 }
180
181 #[test]
182 fn test_seed_roundtrip_bytes() {
183 let s = VortexSeed::new(0xDEADBEEF_CAFEBABE, 0x12345678_9ABCDEF0);
184 let bytes = s.to_bytes();
185 let s2 = VortexSeed::from_bytes(bytes);
186 assert_eq!(s, s2);
187 }
188
189 #[test]
190 fn test_seed_display() {
191 let s = VortexSeed::new(0, 42);
192 let display = format!("{s}");
193 assert_eq!(display, "0000000000000000000000000000002a");
194 }
195
196 #[test]
197 fn test_derive_deterministic() {
198 let tree1 = SeedTree::new(VortexSeed::from_u64(42));
199 let tree2 = SeedTree::new(VortexSeed::from_u64(42));
200 assert_eq!(tree1.derive("fs"), tree2.derive("fs"));
201 assert_eq!(tree1.derive("net"), tree2.derive("net"));
202 assert_eq!(tree1.derive("clock"), tree2.derive("clock"));
203 }
204
205 #[test]
206 fn test_derive_different_domains_differ() {
207 let tree = SeedTree::new(VortexSeed::from_u64(42));
208 let domains = ["executor", "fs", "clock", "alloc", "net", "process"];
209 let seeds: Vec<VortexSeed> = domains.iter().map(|d| tree.derive(d)).collect();
210 for i in 0..seeds.len() {
212 for j in (i + 1)..seeds.len() {
213 assert_ne!(
214 seeds[i], seeds[j],
215 "domains '{}' and '{}' collided",
216 domains[i], domains[j]
217 );
218 }
219 }
220 }
221
222 #[test]
223 fn test_derive_different_master_seeds_differ() {
224 let tree1 = SeedTree::new(VortexSeed::from_u64(42));
225 let tree2 = SeedTree::new(VortexSeed::from_u64(43));
226 assert_ne!(tree1.derive("fs"), tree2.derive("fs"));
227 }
228
229 #[test]
230 fn test_subtree() {
231 let tree = SeedTree::new(VortexSeed::from_u64(42));
232 let fs_tree = tree.subtree("fs");
233 let wal = fs_tree.derive("wal");
234 let data = fs_tree.derive("data");
235 assert_ne!(wal, data);
236
237 let fs_tree2 = SeedTree::new(VortexSeed::from_u64(42)).subtree("fs");
239 assert_eq!(fs_tree.derive("wal"), fs_tree2.derive("wal"));
240 }
241
242 #[test]
243 fn test_adding_new_domain_doesnt_change_existing() {
244 let tree = SeedTree::new(VortexSeed::from_u64(0xDEADBEEF));
245 let fs_before = tree.derive("fs");
246 let net_before = tree.derive("net");
247
248 let _new_domain = tree.derive("some_new_subsystem");
250
251 assert_eq!(tree.derive("fs"), fs_before);
252 assert_eq!(tree.derive("net"), net_before);
253 }
254
255 #[test]
256 fn test_cross_platform_stability() {
257 let tree = SeedTree::new(VortexSeed::new(0, 0xDEADBEEF));
261 let fs = tree.derive("fs");
262 let net = tree.derive("net");
263
264 let fs_expected = tree.derive("fs");
268 let net_expected = tree.derive("net");
269 assert_eq!(fs, fs_expected);
270 assert_eq!(net, net_expected);
271 }
272}