use std::io;
use std::sync::Arc;
use crate::io::Source;
const INDEX_BITS: u32 = if usize::BITS >= 64 { 32 } else { 20 };
const INDEX_MASK: u64 = (1 << INDEX_BITS) - 1;
pub(crate) const CHILD: u64 = 1 << (usize::BITS - 1);
const GEN_MASK: u64 = ((usize::MAX as u64) >> 1) >> INDEX_BITS;
struct Slot<T> {
generation: u64,
entry: Option<T>,
}
pub(crate) struct Tokens<T, const TAG: u64> {
slots: Vec<Slot<T>>,
free: Vec<u32>,
}
pub(crate) type Sources = Tokens<Arc<Source>, 0>;
impl<T, const TAG: u64> Default for Tokens<T, TAG> {
fn default() -> Self {
Self {
slots: Vec::new(),
free: Vec::new(),
}
}
}
impl<T, const TAG: u64> Tokens<T, TAG> {
fn token(index: u32, generation: u64) -> u64 {
TAG | (generation << INDEX_BITS) | u64::from(index)
}
fn index_of(&self, token: u64) -> Option<usize> {
if token & CHILD != TAG {
return None;
}
let index = (token & INDEX_MASK) as usize;
let slot = self.slots.get(index)?;
(slot.generation == (token & !CHILD) >> INDEX_BITS).then_some(index)
}
pub(crate) fn insert(&mut self, entry: T) -> io::Result<u64> {
if let Some(index) = self.free.pop() {
let slot = &mut self.slots[index as usize];
slot.entry = Some(entry);
return Ok(Self::token(index, slot.generation));
}
let index = self.slots.len();
if index as u64 >= INDEX_MASK {
return Err(io::Error::new(
io::ErrorKind::OutOfMemory,
"the shard watches as many sources as a token can name",
));
}
self.slots.push(Slot {
generation: 0,
entry: Some(entry),
});
Ok(Self::token(index as u32, 0))
}
#[cfg(any(readiness, test))]
pub(crate) fn get(&self, token: u64) -> Option<&T> {
self.slots[self.index_of(token)?].entry.as_ref()
}
#[cfg(port)]
pub(crate) fn get_mut(&mut self, token: u64) -> Option<&mut T> {
let index = self.index_of(token)?;
self.slots[index].entry.as_mut()
}
pub(crate) fn remove(&mut self, token: u64) -> Option<T> {
let index = self.index_of(token)?;
let slot = &mut self.slots[index];
let entry = slot.entry.take()?;
slot.generation = (slot.generation + 1) & GEN_MASK;
self.free.push(index as u32);
Some(entry)
}
pub(crate) fn drain(&mut self) -> impl Iterator<Item = T> + '_ {
self.drain_tokens().map(|(_, entry)| entry)
}
pub(crate) fn drain_tokens(&mut self) -> impl Iterator<Item = (u64, T)> + '_ {
self.free.clear();
self.slots
.drain(..)
.enumerate()
.filter_map(|(index, slot)| {
let token = Self::token(index as u32, slot.generation);
slot.entry.map(|entry| (token, entry))
})
}
#[cfg(test)]
pub(crate) fn len(&self) -> usize {
self.slots.len() - self.free.len()
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::{CHILD, INDEX_BITS, Sources, Tokens};
use crate::io::Source;
use crate::io::source::Kind;
fn source() -> std::sync::Arc<Source> {
Source::new(crate::io::raw::NONE, Kind::Bytes)
}
#[test]
fn a_removed_registration_is_not_found_by_its_old_token() {
let mut s = Sources::default();
let a = s.insert(source()).unwrap();
assert!(s.get(a).is_some());
assert!(s.remove(a).is_some());
assert!(s.get(a).is_none());
let b = s.insert(source()).unwrap();
assert_eq!(a & ((1 << INDEX_BITS) - 1), b & ((1 << INDEX_BITS) - 1));
assert_ne!(a, b);
assert!(s.get(a).is_none(), "the old token names nothing");
assert!(s.remove(a).is_none(), "nor removes the new registration");
assert!(s.get(b).is_some());
assert_eq!(s.len(), 1);
}
#[test]
fn tokens_fit_a_pointer_and_are_never_all_ones() {
let mut s = Sources::default();
let mut c = Tokens::<u8, CHILD>::default();
for _ in 0..1000 {
let t = s.insert(source()).unwrap();
let u = c.insert(0).unwrap();
for token in [t, u] {
assert!(token <= usize::MAX as u64);
assert_ne!(token, u64::MAX);
}
s.remove(t);
c.remove(u);
}
}
#[test]
fn a_token_of_one_table_finds_nothing_in_the_other() {
let mut s = Sources::default();
let mut c = Tokens::<u8, CHILD>::default();
let t = s.insert(source()).unwrap();
let u = c.insert(7).unwrap();
assert_eq!(t & !CHILD, u & !CHILD, "the same index and generation");
assert!(s.get(u).is_none());
assert!(c.get(t).is_none());
assert!(s.remove(u).is_none() && c.remove(t).is_none());
assert_eq!((s.len(), c.len()), (1, 1));
}
}