use std::num::NonZeroU64;
use std::sync::atomic::{AtomicU64, Ordering};
use slotmap::{Key, KeyData, SlotMap, new_key_type};
use super::Val;
use super::object::{GcHeap, Markable, UpvaluePool};
new_key_type! {
pub(crate) struct AnchorKey;
}
static NEXT_STATE_ID: AtomicU64 = AtomicU64::new(1);
pub(crate) fn next_state_id() -> NonZeroU64 {
let id = NEXT_STATE_ID.fetch_add(1, Ordering::Relaxed);
NonZeroU64::new(id).expect("u64 state-id counter cannot wrap in a real process")
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Anchor {
state_id: NonZeroU64,
key: NonZeroU64,
}
impl Anchor {
fn new(state_id: NonZeroU64, key: AnchorKey) -> Self {
let ffi = key.data().as_ffi();
let key = NonZeroU64::new(ffi).expect("real slotmap keys are never zero");
Self { state_id, key }
}
fn slotmap_key(self) -> AnchorKey {
AnchorKey::from(KeyData::from_ffi(self.key.get()))
}
}
pub(crate) struct Registry {
state_id: NonZeroU64,
slots: SlotMap<AnchorKey, Val>,
}
impl Registry {
pub(crate) fn new(state_id: NonZeroU64) -> Self {
Self {
state_id,
slots: SlotMap::with_key(),
}
}
pub(crate) fn insert(&mut self, value: Val) -> Anchor {
let key = self.slots.insert(value);
Anchor::new(self.state_id, key)
}
pub(crate) fn get(&self, a: Anchor) -> Option<Val> {
if a.state_id != self.state_id {
return None;
}
self.slots.get(a.slotmap_key()).copied()
}
pub(crate) fn remove(&mut self, a: Anchor) -> bool {
if a.state_id != self.state_id {
return false;
}
self.slots.remove(a.slotmap_key()).is_some()
}
pub(crate) fn len(&self) -> usize {
self.slots.len()
}
}
impl Markable for Registry {
fn mark_reachable(&self, heap: &GcHeap, upvalue_pool: &UpvaluePool) {
for val in self.slots.values() {
val.mark_reachable(heap, upvalue_pool);
}
}
}