use ahash::HashMap;
use slab::Slab;
use std::ops::{Index, IndexMut};
use zrx_graph::traversal::{Error, Result};
use crate::scheduler::signal::{Id, Scope};
use super::Frontier;
#[derive(Debug)]
pub struct Frontiers<I> {
store: Slab<Frontier<I>>,
index: HashMap<Scope<I>, Vec<usize>>,
}
impl<I> Frontiers<I>
where
I: Id,
{
pub fn insert(&mut self, frontier: Frontier<I>) -> Result<usize> {
let Frontier { scope, mut traversal, .. } = frontier;
if let Some(ids) = self.index.get(&scope) {
for &id in ids {
match self.store[id].traversal.converge(traversal) {
Ok(()) => return Ok(id),
Err(Error::Disjoint(existing)) => traversal = existing,
Err(err) => return Err(err),
}
}
}
let id = self.store.insert(Frontier::new(scope.clone(), traversal));
self.index.entry(scope).or_default().push(id);
Ok(id)
}
pub fn remove(&mut self, id: usize) -> Option<Frontier<I>> {
let frontier = self.store.try_remove(id)?;
let scope = frontier.scope();
if let Some(ids) = self.index.get_mut(scope) {
ids.retain(|&x| x != id);
if ids.is_empty() {
self.index.remove(scope);
}
}
Some(frontier)
}
}
#[allow(clippy::must_use_candidate)]
impl<I> Frontiers<I> {
#[inline]
pub fn len(&self) -> usize {
self.store.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.store.is_empty()
}
}
impl<I> Index<usize> for Frontiers<I> {
type Output = Frontier<I>;
#[inline]
fn index(&self, index: usize) -> &Self::Output {
&self.store[index]
}
}
impl<I> IndexMut<usize> for Frontiers<I> {
#[inline]
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
&mut self.store[index]
}
}
impl<I> Default for Frontiers<I> {
#[inline]
fn default() -> Self {
Self {
store: Slab::default(),
index: HashMap::default(),
}
}
}