use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;
use chromiumoxide_types::CallId;
use dashmap::DashMap;
pub type Slot = u16;
#[derive(Clone, Default)]
pub(crate) struct CallIdAllocator {
next: Arc<AtomicU64>,
routing: Arc<DashMap<CallId, Slot>>,
}
impl CallIdAllocator {
pub fn new(start: u64) -> Self {
Self {
next: Arc::new(AtomicU64::new(start.max(1))),
routing: Arc::new(DashMap::with_capacity(1024)),
}
}
pub fn alloc(&self, slot: Slot) -> CallId {
let n = self.next.fetch_add(1, Ordering::Relaxed);
let id = CallId::new(n as usize);
if slot != 0 {
self.routing.insert(id, slot);
}
id
}
pub fn take_route(&self, id: CallId) -> Option<Slot> {
self.routing.remove(&id).map(|(_, slot)| slot)
}
pub fn drop_slot(&self, slot: Slot) {
self.routing.retain(|_, s| *s != slot);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn alloc_records_route_for_session_slots() {
let a = CallIdAllocator::new(1);
let id = a.alloc(7);
assert_eq!(a.take_route(id), Some(7));
assert_eq!(a.take_route(id), None);
}
#[test]
fn alloc_skips_routing_for_slot_zero() {
let a = CallIdAllocator::new(1);
let id = a.alloc(0);
assert_eq!(a.take_route(id), None);
}
#[test]
fn drop_slot_clears_only_its_entries() {
let a = CallIdAllocator::new(1);
let id1 = a.alloc(3);
let id2 = a.alloc(4);
a.drop_slot(3);
assert_eq!(a.take_route(id1), None);
assert_eq!(a.take_route(id2), Some(4));
}
}