use hashbrown::HashMap;
use super::Policy;
use crate::LightCache;
use std::hash::{BuildHasher, Hash};
pub(crate) struct LinkedArena<I, N> {
pub(crate) idx_of: HashMap<I, usize>,
pub(crate) nodes: Vec<N>,
pub(crate) head: Option<usize>,
pub(crate) tail: Option<usize>,
}
impl<I, N> LinkedArena<I, N> {
pub fn new() -> Self {
LinkedArena {
idx_of: HashMap::new(),
nodes: Vec::new(),
head: None,
tail: None,
}
}
}
pub trait LinkedNode<I>
where
Self: Sized,
I: Copy + Hash + Eq,
{
fn new(item: I, parent: Option<usize>, child: Option<usize>) -> Self;
fn item(&self) -> &I;
fn prev(&self) -> Option<usize>;
fn next(&self) -> Option<usize>;
fn set_prev(&mut self, parent: Option<usize>);
fn set_next(&mut self, child: Option<usize>);
}
impl<I, N> LinkedArena<I, N>
where
N: LinkedNode<I>,
I: Copy + Hash + Eq,
{
pub(crate) fn insert_head(&mut self, key: I) {
let new_head = self.nodes.len();
if let Some(_) = self.idx_of.insert(key, new_head) {
panic!("Key already exists in LinkedArena");
}
if let Some(old_head) = self.head {
self.nodes.push(N::new(key, None, Some(old_head)));
unsafe {
self.nodes
.get_unchecked_mut(old_head)
.set_prev(Some(new_head));
}
} else {
self.nodes.push(N::new(key, None, None));
self.tail = Some(new_head);
}
self.head = Some(new_head);
}
fn relink(&mut self, idx: usize) {
let node = self.nodes.get(idx).unwrap();
let prev = node.prev();
let next = node.next();
if let Some(parent) = prev {
unsafe {
self.nodes.get_unchecked_mut(parent).set_next(Some(idx));
}
} else {
self.head = Some(idx);
}
if let Some(child) = next {
unsafe {
self.nodes.get_unchecked_mut(child).set_prev(Some(idx));
}
} else {
self.tail = Some(idx);
}
}
fn unlink(&mut self, idx: usize) {
let node = self.nodes.get(idx).expect("Invalid index to unlink");
let parent = node.prev();
let child = node.next();
if let Some(parent) = parent {
unsafe {
self.nodes.get_unchecked_mut(parent).set_next(child);
}
} else {
self.head = child;
}
if let Some(child) = child {
unsafe {
self.nodes.get_unchecked_mut(child).set_prev(parent);
}
} else {
self.tail = parent;
}
}
}
#[allow(unused)]
impl<I, N> LinkedArena<I, N>
where
N: LinkedNode<I>,
I: Copy + Hash + Eq,
{
pub(crate) fn move_to_head_item(&mut self, key: &I) {
if let Some(new_head_idx) = self.idx_of.get(key).copied() {
self.move_to_head(new_head_idx)
} else {
panic!("Invalid key to move to head on LinkedArena");
}
}
pub(crate) fn move_to_head(&mut self, new_head: usize) {
self.unlink(new_head);
let node = unsafe { self.nodes.get_unchecked_mut(new_head) };
node.set_next(self.head);
node.set_prev(None);
if let Some(old) = self.head.replace(new_head) {
unsafe {
self.nodes.get_unchecked_mut(old).set_prev(Some(new_head));
}
}
}
pub(crate) fn get_node_mut(&mut self, key: &I) -> Option<(usize, &mut N)> {
if let Some(idx) = self.idx_of.get(key).copied() {
Some((idx, unsafe { self.nodes.get_unchecked_mut(idx) }))
} else {
None
}
}
pub(crate) fn remove_item(&mut self, item: &I) -> Option<(usize, N)> {
if let Some(idx) = self.idx_of.get(item).copied() {
Some(self.remove(idx))
} else {
None
}
}
pub(crate) fn remove(&mut self, idx: usize) -> (usize, N) {
self.unlink(idx);
let removed = self.nodes.swap_remove(idx);
self.idx_of.remove(removed.item());
let len = self.nodes.len();
if idx != len {
*self
.idx_of
.get_mut(self.nodes.get(idx).unwrap().item())
.unwrap() = idx;
self.relink(idx);
}
(len, removed)
}
pub(crate) fn remove_end<V, S, P>(&mut self, cache: &LightCache<I, V, S, P>)
where
V: Clone + Sync,
S: BuildHasher,
P: Policy<I, V>,
{
if let Some(tail) = self.tail {
let (_, n) = self.remove(tail);
cache.remove_no_policy(n.item());
}
}
pub(crate) fn head(&self) -> Option<(usize, &N)> {
self.head.map(|idx| (idx, unsafe { self.nodes.get_unchecked(idx) }))
}
pub(crate) fn tail(&self) -> Option<(usize, &N)> {
self.tail.map(|idx| (idx, unsafe { self.nodes.get_unchecked(idx) }))
}
pub(crate) fn len(&self) -> usize {
self.nodes.len()
}
}