Skip to main content

moq/
id.rs

1use std::collections::HashMap;
2use std::num::NonZero;
3use std::sync::atomic::{AtomicU32, Ordering};
4
5use crate::Error;
6
7/// Opaque resource identifier returned to C code.
8///
9/// Non-zero u32 value that uniquely identifies resources like sessions,
10/// origins, broadcasts, tracks, etc. Zero is reserved to indicate "none"
11/// or optional parameters.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub struct Id(NonZero<u32>);
14
15impl std::fmt::Display for Id {
16	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17		write!(f, "{}", self.0.get())
18	}
19}
20
21/// Global monotonic counter so IDs are never reused across the process lifetime.
22static NEXT_ID: AtomicU32 = AtomicU32::new(1);
23
24/// Map that assigns globally unique, never-reused IDs.
25///
26/// Unlike a slab, freed IDs are not recycled. This avoids races where
27/// parallel consumers of the same global state accidentally alias each
28/// other's resources.
29pub(crate) struct NonZeroSlab<T> {
30	map: HashMap<Id, T>,
31}
32
33impl<T> NonZeroSlab<T> {
34	pub fn insert(&mut self, value: T) -> Result<Id, Error> {
35		let raw = NEXT_ID.fetch_add(1, Ordering::Relaxed);
36		let id = Id::try_from(raw)?;
37		self.map.insert(id, value);
38		Ok(id)
39	}
40
41	pub fn get(&self, id: Id) -> Option<&T> {
42		self.map.get(&id)
43	}
44
45	pub fn get_mut(&mut self, id: Id) -> Option<&mut T> {
46		self.map.get_mut(&id)
47	}
48
49	pub fn remove(&mut self, id: Id) -> Option<T> {
50		self.map.remove(&id)
51	}
52}
53
54impl TryFrom<i32> for Id {
55	type Error = Error;
56
57	fn try_from(value: i32) -> Result<Self, Self::Error> {
58		Self::try_from(u32::try_from(value).map_err(|_| Error::InvalidId)?)
59	}
60}
61
62impl TryFrom<u32> for Id {
63	type Error = Error;
64
65	fn try_from(value: u32) -> Result<Self, Self::Error> {
66		// IDs must fit in i32 (positive) for the C FFI layer.
67		i32::try_from(value).map_err(|_| Error::InvalidId)?;
68		NonZero::try_from(value).map(Id).map_err(|_| Error::InvalidId)
69	}
70}
71
72impl From<Id> for u32 {
73	fn from(value: Id) -> Self {
74		value.0.get()
75	}
76}
77
78impl From<Id> for i32 {
79	fn from(value: Id) -> Self {
80		// Safety: TryFrom<u32> guarantees the value fits in i32.
81		value.0.get() as i32
82	}
83}
84
85impl<T> Default for NonZeroSlab<T> {
86	fn default() -> Self {
87		Self { map: HashMap::new() }
88	}
89}