codetics_ocpp_core/
msgid.rs1use std::collections::hash_map::RandomState;
13use std::hash::{BuildHasher, Hasher};
14use std::sync::atomic::{AtomicU64, Ordering};
15use std::time::{SystemTime, UNIX_EPOCH};
16
17pub struct MessageIdGenerator {
19 state: AtomicU64,
20 key: u64,
21}
22
23impl Default for MessageIdGenerator {
24 fn default() -> Self {
25 Self::new()
26 }
27}
28
29impl MessageIdGenerator {
30 pub fn new() -> Self {
31 let mut h = RandomState::new().build_hasher();
33 h.write_u128(
34 SystemTime::now()
35 .duration_since(UNIX_EPOCH)
36 .unwrap_or_default()
37 .as_nanos(),
38 );
39 h.write_u32(std::process::id());
40 let seed = h.finish();
41 let mut h2 = RandomState::new().build_hasher();
42 h2.write_u64(seed);
43 MessageIdGenerator {
44 state: AtomicU64::new(seed),
45 key: h2.finish() | 1,
46 }
47 }
48
49 fn next_u64(&self) -> u64 {
50 let z = self
52 .state
53 .fetch_add(0x9E37_79B9_7F4A_7C15, Ordering::Relaxed);
54 let mut z = z ^ self.key;
55 z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
56 z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
57 z ^ (z >> 31)
58 }
59
60 pub fn next_id(&self) -> String {
62 let hi = (self.next_u64() & 0xFFFF_FFFF_FFFF_0FFF) | 0x0000_0000_0000_4000;
63 let lo = (self.next_u64() & 0x3FFF_FFFF_FFFF_FFFF) | 0x8000_0000_0000_0000;
64 format!(
65 "{:08x}-{:04x}-{:04x}-{:04x}-{:012x}",
66 hi >> 32,
67 (hi >> 16) & 0xFFFF,
68 hi & 0xFFFF,
69 lo >> 48,
70 lo & 0xFFFF_FFFF_FFFF
71 )
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 use super::*;
78 use std::collections::HashSet;
79
80 #[test]
81 fn unique_and_well_formed() {
82 let g = MessageIdGenerator::new();
83 let mut seen = HashSet::new();
84 for _ in 0..100_000 {
85 let id = g.next_id();
86 assert_eq!(id.len(), 36);
87 assert_eq!(&id[14..15], "4");
88 assert!(matches!(&id[19..20], "8" | "9" | "a" | "b"));
89 assert!(seen.insert(id));
90 }
91 }
92}