1use crate::rand::gen_random;
3
4pub struct IdGenerator {}
6
7impl IdGenerator {
8 pub fn generate() -> u64 {
10 let random_bytes: [u8; 8] = gen_random();
12 u64::from_le_bytes(random_bytes)
13 }
14}
15
16pub use cubecl_common::stream_id::StreamId;
17
18use core::hash::{BuildHasher, Hasher};
19
20use alloc::str::FromStr;
21use data_encoding::BASE32_DNSSEC;
22use serde::{Deserialize, Serialize};
23
24type DefaultHashBuilder = core::hash::BuildHasherDefault<ahash::AHasher>;
28
29#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy, PartialOrd, Ord, Serialize, Deserialize)]
31pub struct ParamId {
32 value: u64,
33}
34
35impl From<u64> for ParamId {
36 fn from(value: u64) -> Self {
37 Self { value }
38 }
39}
40
41impl Default for ParamId {
42 fn default() -> Self {
43 Self::new()
44 }
45}
46
47impl ParamId {
48 pub fn new() -> Self {
50 Self {
51 value: IdGenerator::generate(),
52 }
53 }
54
55 pub fn val(&self) -> u64 {
57 self.value
58 }
59}
60
61impl FromStr for ParamId {
62 type Err = &'static str; fn from_str(encoded: &str) -> Result<Self, Self::Err> {
71 let u64_id: Option<u64> = match BASE32_DNSSEC.decode(encoded.as_bytes()) {
72 Ok(bytes) => {
73 let mut buffer = [0u8; 8];
74 buffer[..bytes.len()].copy_from_slice(&bytes);
75 Some(u64::from_le_bytes(buffer))
76 }
77 Err(_) => match uuid::Uuid::try_parse(encoded) {
78 Ok(id) => {
80 let mut hasher = DefaultHashBuilder::default().build_hasher();
83 hasher.write(id.as_bytes());
85 Some(hasher.finish())
86 }
87 Err(_) => None,
88 },
89 };
90 u64_id.map(Self::from).ok_or("Invalid id.")
91 }
92}
93
94impl core::fmt::Display for ParamId {
95 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
96 let encoded = BASE32_DNSSEC.encode(&self.value.to_le_bytes());
97 f.write_str(&encoded)
98 }
99}
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 use alloc::collections::BTreeSet;
106 use alloc::string::ToString;
107
108 #[cfg(feature = "std")]
109 use dashmap::DashSet; #[cfg(feature = "std")]
111 use std::{sync::Arc, thread};
112
113 #[test]
114 fn uniqueness_test() {
115 const IDS_CNT: usize = 10_000;
116
117 let mut set: BTreeSet<u64> = BTreeSet::new();
118
119 for _i in 0..IDS_CNT {
120 assert!(set.insert(IdGenerator::generate()));
121 }
122
123 assert_eq!(set.len(), IDS_CNT);
124 }
125
126 #[cfg(feature = "std")]
127 #[test]
128 fn thread_safety_test() {
129 const NUM_THREADS: usize = 10;
130 const NUM_REPEATS: usize = 1_000;
131 const EXPECTED_TOTAL_IDS: usize = NUM_THREADS * NUM_REPEATS;
132
133 let set: Arc<DashSet<u64>> = Arc::new(DashSet::new());
134
135 let mut handles = vec![];
136
137 for _ in 0..NUM_THREADS {
138 let set = set.clone();
139
140 let handle = thread::spawn(move || {
141 for _i in 0..NUM_REPEATS {
142 assert!(set.insert(IdGenerator::generate()));
143 }
144 });
145 handles.push(handle);
146 }
147
148 for handle in handles {
149 handle.join().unwrap();
150 }
151 assert_eq!(set.len(), EXPECTED_TOTAL_IDS);
152 }
153
154 #[test]
155 fn param_serde_try_deserialize() {
156 let val = ParamId::from(123456u64);
157 let deserialized = ParamId::from_str(&val.to_string()).unwrap();
158 assert_eq!(val, deserialized);
159
160 assert_eq!(ParamId::from_str("invalid_id"), Err("Invalid id."));
161 }
162
163 #[test]
164 fn param_serde_deserialize() {
165 let val = ParamId::from(123456u64);
166 let deserialized = ParamId::from_str(&val.to_string()).unwrap();
167 assert_eq!(val, deserialized);
168 }
169
170 #[test]
171 fn param_serde_deserialize_legacy() {
172 let legacy_val = [45u8; 6];
173 let param_id = ParamId::from_str(&BASE32_DNSSEC.encode(&legacy_val)).unwrap();
174 assert_eq!(param_id.val().to_le_bytes()[0..6], legacy_val);
175 assert_eq!(param_id.val().to_le_bytes()[6..], [0, 0]);
176 }
177
178 #[test]
179 fn param_serde_deserialize_legacy_uuid() {
180 let legacy_id = "30b82c23-788d-4d63-a743-ada258d5f13c";
182 let param_id1 = ParamId::from_str(legacy_id).unwrap();
183 let param_id2 = ParamId::from_str(legacy_id).unwrap();
184 assert_eq!(param_id1, param_id2);
185 }
186
187 #[test]
188 #[should_panic = "Invalid id."]
189 fn param_serde_deserialize_invalid_id() {
190 let invalid_uuid = "30b82c23-788d-4d63-ada258d5f13c";
191 let _ = ParamId::from_str(invalid_uuid).unwrap();
192 }
193}