capnweb_core/
ids.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3use std::sync::atomic::{AtomicU64, Ordering};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6#[serde(transparent)]
7pub struct CallId(u64);
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
10#[serde(transparent)]
11pub struct PromiseId(u64);
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
14#[serde(transparent)]
15pub struct CapId(u64);
16
17impl CallId {
18    pub fn new(value: u64) -> Self {
19        CallId(value)
20    }
21
22    pub fn as_u64(&self) -> u64 {
23        self.0
24    }
25}
26
27impl PromiseId {
28    pub fn new(value: u64) -> Self {
29        PromiseId(value)
30    }
31
32    pub fn as_u64(&self) -> u64 {
33        self.0
34    }
35}
36
37impl CapId {
38    pub fn new(value: u64) -> Self {
39        CapId(value)
40    }
41
42    pub fn as_u64(&self) -> u64 {
43        self.0
44    }
45}
46
47impl fmt::Display for CallId {
48    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49        write!(f, "CallId({})", self.0)
50    }
51}
52
53impl fmt::Display for PromiseId {
54    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
55        write!(f, "PromiseId({})", self.0)
56    }
57}
58
59impl fmt::Display for CapId {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        write!(f, "CapId({})", self.0)
62    }
63}
64
65impl From<u64> for CallId {
66    fn from(value: u64) -> Self {
67        CallId::new(value)
68    }
69}
70
71impl From<u64> for PromiseId {
72    fn from(value: u64) -> Self {
73        PromiseId::new(value)
74    }
75}
76
77impl From<u64> for CapId {
78    fn from(value: u64) -> Self {
79        CapId::new(value)
80    }
81}
82
83pub struct CallIdAllocator {
84    next: AtomicU64,
85}
86
87pub struct PromiseIdAllocator {
88    next: AtomicU64,
89}
90
91pub struct CapIdAllocator {
92    next: AtomicU64,
93}
94
95impl CallIdAllocator {
96    pub fn new() -> Self {
97        CallIdAllocator {
98            next: AtomicU64::new(1),
99        }
100    }
101
102    pub fn allocate(&self) -> CallId {
103        let id = self.next.fetch_add(1, Ordering::Relaxed);
104        CallId::new(id)
105    }
106
107    pub fn peek_next(&self) -> u64 {
108        self.next.load(Ordering::Relaxed)
109    }
110}
111
112impl PromiseIdAllocator {
113    pub fn new() -> Self {
114        PromiseIdAllocator {
115            next: AtomicU64::new(1),
116        }
117    }
118
119    pub fn allocate(&self) -> PromiseId {
120        let id = self.next.fetch_add(1, Ordering::Relaxed);
121        PromiseId::new(id)
122    }
123
124    pub fn peek_next(&self) -> u64 {
125        self.next.load(Ordering::Relaxed)
126    }
127}
128
129impl CapIdAllocator {
130    pub fn new() -> Self {
131        CapIdAllocator {
132            next: AtomicU64::new(1),
133        }
134    }
135
136    pub fn allocate(&self) -> CapId {
137        let id = self.next.fetch_add(1, Ordering::Relaxed);
138        CapId::new(id)
139    }
140
141    pub fn peek_next(&self) -> u64 {
142        self.next.load(Ordering::Relaxed)
143    }
144}
145
146impl Default for CallIdAllocator {
147    fn default() -> Self {
148        Self::new()
149    }
150}
151
152impl Default for PromiseIdAllocator {
153    fn default() -> Self {
154        Self::new()
155    }
156}
157
158impl Default for CapIdAllocator {
159    fn default() -> Self {
160        Self::new()
161    }
162}
163
164#[cfg(test)]
165mod tests {
166    use super::*;
167    use std::collections::HashSet;
168    use std::sync::Arc;
169    use std::thread;
170
171    #[test]
172    fn test_id_creation_and_conversion() {
173        let call_id = CallId::new(42);
174        assert_eq!(call_id.as_u64(), 42);
175        assert_eq!(format!("{}", call_id), "CallId(42)");
176
177        let promise_id = PromiseId::new(100);
178        assert_eq!(promise_id.as_u64(), 100);
179        assert_eq!(format!("{}", promise_id), "PromiseId(100)");
180
181        let cap_id = CapId::new(999);
182        assert_eq!(cap_id.as_u64(), 999);
183        assert_eq!(format!("{}", cap_id), "CapId(999)");
184    }
185
186    #[test]
187    fn test_id_from_u64() {
188        let call_id: CallId = 42u64.into();
189        assert_eq!(call_id.as_u64(), 42);
190
191        let promise_id: PromiseId = 100u64.into();
192        assert_eq!(promise_id.as_u64(), 100);
193
194        let cap_id: CapId = 999u64.into();
195        assert_eq!(cap_id.as_u64(), 999);
196    }
197
198    #[test]
199    fn test_id_equality_and_hash() {
200        let id1 = CallId::new(42);
201        let id2 = CallId::new(42);
202        let id3 = CallId::new(43);
203
204        assert_eq!(id1, id2);
205        assert_ne!(id1, id3);
206
207        let mut set = HashSet::new();
208        set.insert(id1);
209        assert!(set.contains(&id2));
210        assert!(!set.contains(&id3));
211    }
212
213    #[test]
214    fn test_allocator_monotonic() {
215        let allocator = CallIdAllocator::new();
216
217        let id1 = allocator.allocate();
218        let id2 = allocator.allocate();
219        let id3 = allocator.allocate();
220
221        assert_eq!(id1.as_u64(), 1);
222        assert_eq!(id2.as_u64(), 2);
223        assert_eq!(id3.as_u64(), 3);
224        assert_eq!(allocator.peek_next(), 4);
225    }
226
227    #[test]
228    fn test_allocator_thread_safety() {
229        let allocator = Arc::new(CallIdAllocator::new());
230        let mut handles = vec![];
231        let num_threads = 10;
232        let ids_per_thread = 100;
233
234        for _ in 0..num_threads {
235            let alloc = Arc::clone(&allocator);
236            let handle = thread::spawn(move || {
237                let mut ids = vec![];
238                for _ in 0..ids_per_thread {
239                    ids.push(alloc.allocate().as_u64());
240                }
241                ids
242            });
243            handles.push(handle);
244        }
245
246        let mut all_ids = HashSet::new();
247        for handle in handles {
248            let ids = handle.join().unwrap();
249            for id in ids {
250                assert!(all_ids.insert(id), "Duplicate ID found: {}", id);
251            }
252        }
253
254        assert_eq!(all_ids.len(), num_threads * ids_per_thread);
255        assert_eq!(
256            allocator.peek_next(),
257            (num_threads * ids_per_thread + 1) as u64
258        );
259    }
260
261    #[test]
262    fn test_serialization_deserialization() {
263        let call_id = CallId::new(42);
264        let json = serde_json::to_string(&call_id).unwrap();
265        assert_eq!(json, "42");
266
267        let deserialized: CallId = serde_json::from_str(&json).unwrap();
268        assert_eq!(deserialized, call_id);
269
270        let promise_id = PromiseId::new(100);
271        let json = serde_json::to_string(&promise_id).unwrap();
272        assert_eq!(json, "100");
273
274        let deserialized: PromiseId = serde_json::from_str(&json).unwrap();
275        assert_eq!(deserialized, promise_id);
276
277        let cap_id = CapId::new(999);
278        let json = serde_json::to_string(&cap_id).unwrap();
279        assert_eq!(json, "999");
280
281        let deserialized: CapId = serde_json::from_str(&json).unwrap();
282        assert_eq!(deserialized, cap_id);
283    }
284
285    #[test]
286    fn test_multiple_allocators_independence() {
287        let call_allocator = CallIdAllocator::new();
288        let promise_allocator = PromiseIdAllocator::new();
289        let cap_allocator = CapIdAllocator::new();
290
291        let call1 = call_allocator.allocate();
292        let promise1 = promise_allocator.allocate();
293        let cap1 = cap_allocator.allocate();
294
295        assert_eq!(call1.as_u64(), 1);
296        assert_eq!(promise1.as_u64(), 1);
297        assert_eq!(cap1.as_u64(), 1);
298
299        let call2 = call_allocator.allocate();
300        let promise2 = promise_allocator.allocate();
301        let cap2 = cap_allocator.allocate();
302
303        assert_eq!(call2.as_u64(), 2);
304        assert_eq!(promise2.as_u64(), 2);
305        assert_eq!(cap2.as_u64(), 2);
306    }
307}