1use std::sync::atomic::{AtomicU64, Ordering};
7
8use bbx_core::random::XorShiftRng;
9
10use crate::error::{NetError, Result};
11
12static NODE_ID_COUNTER: AtomicU64 = AtomicU64::new(0);
13
14#[repr(C)]
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct NodeId {
22 pub high: u64,
23 pub low: u64,
24}
25
26impl NodeId {
27 pub fn generate(rng: &mut XorShiftRng) -> Self {
39 let sample1 = (rng.next_noise_sample() + 1.0) / 2.0;
40 let sample2 = (rng.next_noise_sample() + 1.0) / 2.0;
41 Self {
42 high: (sample1 * u64::MAX as f64) as u64,
43 low: (sample2 * u64::MAX as f64) as u64,
44 }
45 }
46
47 pub fn generate_with_entropy(clock_micros: u64) -> Self {
52 let counter = NODE_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
53 let pid = std::process::id() as u64;
54 let addr_entropy = &counter as *const _ as u64;
55
56 let seed = clock_micros.wrapping_mul(0x517cc1b727220a95)
57 ^ pid.rotate_left(17)
58 ^ addr_entropy.rotate_left(31)
59 ^ counter.wrapping_mul(0x2545f4914f6cdd1d);
60
61 let mut rng = XorShiftRng::new(seed.max(1));
62 Self::generate(&mut rng)
63 }
64
65 pub fn generate_unique<F>(clock_micros: u64, mut exists: F) -> Self
70 where
71 F: FnMut(&NodeId) -> bool,
72 {
73 loop {
74 let candidate = Self::generate_with_entropy(clock_micros);
75 if !exists(&candidate) {
76 return candidate;
77 }
78 }
79 }
80
81 pub const fn from_parts(high: u64, low: u64) -> Self {
83 Self { high, low }
84 }
85
86 pub fn to_uuid_string(&self) -> String {
90 format!(
91 "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
92 (self.high >> 32) as u32,
93 ((self.high >> 16) & 0xFFFF) as u16,
94 (self.high & 0xFFFF) as u16,
95 ((self.low >> 48) & 0xFFFF) as u16,
96 (self.low & 0xFFFF_FFFF_FFFF)
97 )
98 }
99
100 pub fn from_uuid_string(s: &str) -> Result<Self> {
104 let s = s.trim();
105 let parts: Vec<&str> = s.split('-').collect();
106
107 if parts.len() != 5 {
108 return Err(NetError::InvalidNodeId);
109 }
110
111 let p0 = u32::from_str_radix(parts[0], 16).map_err(|_| NetError::InvalidNodeId)?;
112 let p1 = u16::from_str_radix(parts[1], 16).map_err(|_| NetError::InvalidNodeId)?;
113 let p2 = u16::from_str_radix(parts[2], 16).map_err(|_| NetError::InvalidNodeId)?;
114 let p3 = u16::from_str_radix(parts[3], 16).map_err(|_| NetError::InvalidNodeId)?;
115 let p4 = u64::from_str_radix(parts[4], 16).map_err(|_| NetError::InvalidNodeId)?;
116
117 if p4 > 0xFFFF_FFFF_FFFF {
118 return Err(NetError::InvalidNodeId);
119 }
120
121 let high = ((p0 as u64) << 32) | ((p1 as u64) << 16) | (p2 as u64);
122 let low = ((p3 as u64) << 48) | p4;
123
124 Ok(Self { high, low })
125 }
126}
127
128#[derive(Debug, Clone)]
134pub struct AddressPath {
135 pub block_id: Option<NodeId>,
137 pub param_name: String,
139}
140
141impl AddressPath {
142 pub fn parse(address: &str) -> Result<Self> {
159 let parts: Vec<&str> = address.split('/').filter(|s| !s.is_empty()).collect();
160
161 match parts.as_slice() {
162 ["block", uuid, "param", name] => {
163 let block_id = NodeId::from_uuid_string(uuid)?;
164 Ok(Self {
165 block_id: Some(block_id),
166 param_name: (*name).to_string(),
167 })
168 }
169 ["blocks", "param", name] => Ok(Self {
170 block_id: None,
171 param_name: (*name).to_string(),
172 }),
173 _ => Err(NetError::InvalidAddress),
174 }
175 }
176
177 pub fn to_address_string(&self) -> String {
179 match &self.block_id {
180 Some(id) => format!("/block/{}/param/{}", id.to_uuid_string(), self.param_name),
181 None => format!("/blocks/param/{}", self.param_name),
182 }
183 }
184}
185
186#[cfg(test)]
187mod tests {
188 use super::*;
189
190 #[test]
191 fn test_node_id_generate() {
192 let mut rng = XorShiftRng::new(12345);
193 let id1 = NodeId::generate(&mut rng);
194 let id2 = NodeId::generate(&mut rng);
195
196 assert_ne!(id1, id2);
197 assert_ne!(id1.high, 0);
198 assert_ne!(id1.low, 0);
199 }
200
201 #[test]
202 fn test_node_id_deterministic() {
203 let mut rng1 = XorShiftRng::new(42);
204 let mut rng2 = XorShiftRng::new(42);
205
206 let id1 = NodeId::generate(&mut rng1);
207 let id2 = NodeId::generate(&mut rng2);
208
209 assert_eq!(id1, id2);
210 }
211
212 #[test]
213 fn test_node_id_uuid_roundtrip() {
214 let mut rng = XorShiftRng::new(54321);
215 let original = NodeId::generate(&mut rng);
216
217 let uuid_str = original.to_uuid_string();
218 let parsed = NodeId::from_uuid_string(&uuid_str).unwrap();
219
220 assert_eq!(original, parsed);
221 }
222
223 #[test]
224 fn test_node_id_uuid_format() {
225 let id = NodeId::from_parts(0x12345678_abcd_ef01, 0x2345_6789abcdef01);
226 let uuid = id.to_uuid_string();
227
228 assert_eq!(uuid, "12345678-abcd-ef01-2345-6789abcdef01");
229 }
230
231 #[test]
232 fn test_node_id_invalid_uuid() {
233 assert!(NodeId::from_uuid_string("invalid").is_err());
234 assert!(NodeId::from_uuid_string("12345678-abcd").is_err());
235 assert!(NodeId::from_uuid_string("zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz").is_err());
236 }
237
238 #[test]
239 fn test_address_path_broadcast() {
240 let path = AddressPath::parse("/blocks/param/gain").unwrap();
241 assert!(path.block_id.is_none());
242 assert_eq!(path.param_name, "gain");
243 }
244
245 #[test]
246 fn test_address_path_targeted() {
247 let id = NodeId::from_parts(0x12345678_abcd_ef01, 0x2345_6789abcdef01);
248 let address = format!("/block/{}/param/volume", id.to_uuid_string());
249
250 let path = AddressPath::parse(&address).unwrap();
251 assert_eq!(path.block_id, Some(id));
252 assert_eq!(path.param_name, "volume");
253 }
254
255 #[test]
256 fn test_address_path_roundtrip() {
257 let original = AddressPath {
258 block_id: Some(NodeId::from_parts(0xaabb_ccdd_eeff_0011, 0x2233_445566778899)),
259 param_name: "frequency".to_string(),
260 };
261
262 let address_str = original.to_address_string();
263 let parsed = AddressPath::parse(&address_str).unwrap();
264
265 assert_eq!(original.block_id, parsed.block_id);
266 assert_eq!(original.param_name, parsed.param_name);
267 }
268
269 #[test]
270 fn test_address_path_invalid() {
271 assert!(AddressPath::parse("/invalid/path").is_err());
272 assert!(AddressPath::parse("/block/notauuid/param/test").is_err());
273 assert!(AddressPath::parse("").is_err());
274 }
275
276 #[test]
277 fn test_generate_with_entropy_uniqueness() {
278 let mut ids = std::collections::HashSet::new();
279 for _ in 0..10_000 {
280 let id = NodeId::generate_with_entropy(0);
281 assert!(ids.insert(id), "Collision detected");
282 }
283 }
284
285 #[test]
286 fn test_generate_unique_avoids_collision() {
287 let existing = NodeId::from_parts(123, 456);
288 let mut attempts = 0;
289 let new_id = NodeId::generate_unique(0, |id| {
290 attempts += 1;
291 *id == existing && attempts == 1
292 });
293 assert_ne!(new_id, existing);
294 }
295}