use super::Resource;
use crate::prelude::*;
use alloc::collections::BTreeSet;
use core::any::Any;
use core::fmt;
#[derive(Debug)]
pub enum ResourceTableError {
Full,
NotPresent,
WrongType,
HasChildren,
}
impl fmt::Display for ResourceTableError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Full => write!(f, "resource table has no free keys"),
Self::NotPresent => write!(f, "resource not present"),
Self::WrongType => write!(f, "resource is of another type"),
Self::HasChildren => write!(f, "resource has children"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ResourceTableError {}
#[derive(Debug)]
pub struct ResourceTable {
entries: Vec<Entry>,
free_head: Option<usize>,
}
#[derive(Debug)]
enum Entry {
Free { next: Option<usize> },
Occupied { entry: TableEntry },
}
impl Entry {
pub fn occupied(&self) -> Option<&TableEntry> {
match self {
Self::Occupied { entry } => Some(entry),
Self::Free { .. } => None,
}
}
pub fn occupied_mut(&mut self) -> Option<&mut TableEntry> {
match self {
Self::Occupied { entry } => Some(entry),
Self::Free { .. } => None,
}
}
}
#[derive(Debug)]
struct TableEntry {
entry: Box<dyn Any + Send>,
parent: Option<u32>,
children: BTreeSet<u32>,
}
impl TableEntry {
fn new(entry: Box<dyn Any + Send>, parent: Option<u32>) -> Self {
Self {
entry,
parent,
children: BTreeSet::new(),
}
}
fn add_child(&mut self, child: u32) {
debug_assert!(!self.children.contains(&child));
self.children.insert(child);
}
fn remove_child(&mut self, child: u32) {
let was_removed = self.children.remove(&child);
debug_assert!(was_removed);
}
}
impl ResourceTable {
pub fn new() -> Self {
ResourceTable {
entries: Vec::new(),
free_head: None,
}
}
pub fn with_capacity(capacity: usize) -> Self {
ResourceTable {
entries: Vec::with_capacity(capacity),
free_head: None,
}
}
pub fn push<T>(&mut self, entry: T) -> Result<Resource<T>, ResourceTableError>
where
T: Send + 'static,
{
let idx = self.push_(TableEntry::new(Box::new(entry), None))?;
Ok(Resource::new_own(idx))
}
fn pop_free_list(&mut self) -> Option<usize> {
if let Some(ix) = self.free_head {
match &self.entries[ix] {
Entry::Free { next } => self.free_head = *next,
Entry::Occupied { .. } => unreachable!(),
}
Some(ix)
} else {
None
}
}
fn free_entry(&mut self, ix: usize) -> TableEntry {
let entry = match core::mem::replace(
&mut self.entries[ix],
Entry::Free {
next: self.free_head,
},
) {
Entry::Occupied { entry } => entry,
Entry::Free { .. } => unreachable!(),
};
self.free_head = Some(ix);
entry
}
fn push_(&mut self, e: TableEntry) -> Result<u32, ResourceTableError> {
if let Some(free) = self.pop_free_list() {
self.entries[free] = Entry::Occupied { entry: e };
Ok(free as u32)
} else {
let ix = self
.entries
.len()
.try_into()
.map_err(|_| ResourceTableError::Full)?;
self.entries.push(Entry::Occupied { entry: e });
Ok(ix)
}
}
fn occupied(&self, key: u32) -> Result<&TableEntry, ResourceTableError> {
self.entries
.get(key as usize)
.and_then(Entry::occupied)
.ok_or(ResourceTableError::NotPresent)
}
fn occupied_mut(&mut self, key: u32) -> Result<&mut TableEntry, ResourceTableError> {
self.entries
.get_mut(key as usize)
.and_then(Entry::occupied_mut)
.ok_or(ResourceTableError::NotPresent)
}
pub fn push_child<T, U>(
&mut self,
entry: T,
parent: &Resource<U>,
) -> Result<Resource<T>, ResourceTableError>
where
T: Send + 'static,
U: 'static,
{
let parent = parent.rep();
self.occupied(parent)?;
let child = self.push_(TableEntry::new(Box::new(entry), Some(parent)))?;
self.occupied_mut(parent)?.add_child(child);
Ok(Resource::new_own(child))
}
pub fn get<T: Any + Sized>(&self, key: &Resource<T>) -> Result<&T, ResourceTableError> {
self.get_(key.rep())?
.downcast_ref()
.ok_or(ResourceTableError::WrongType)
}
fn get_(&self, key: u32) -> Result<&dyn Any, ResourceTableError> {
let r = self.occupied(key)?;
Ok(&*r.entry)
}
pub fn get_mut<T: Any + Sized>(
&mut self,
key: &Resource<T>,
) -> Result<&mut T, ResourceTableError> {
self.get_any_mut(key.rep())?
.downcast_mut()
.ok_or(ResourceTableError::WrongType)
}
pub fn get_any_mut(&mut self, key: u32) -> Result<&mut dyn Any, ResourceTableError> {
let r = self.occupied_mut(key)?;
Ok(&mut *r.entry)
}
pub fn delete<T>(&mut self, resource: Resource<T>) -> Result<T, ResourceTableError>
where
T: Any,
{
debug_assert!(resource.owned());
let entry = self.delete_entry(resource.rep())?;
match entry.entry.downcast() {
Ok(t) => Ok(*t),
Err(_e) => Err(ResourceTableError::WrongType),
}
}
fn delete_entry(&mut self, key: u32) -> Result<TableEntry, ResourceTableError> {
if !self.occupied(key)?.children.is_empty() {
return Err(ResourceTableError::HasChildren);
}
let e = self.free_entry(key as usize);
if let Some(parent) = e.parent {
self.occupied_mut(parent)
.expect("missing parent")
.remove_child(key);
}
Ok(e)
}
#[cfg(feature = "std")]
pub fn iter_entries<'a, T>(
&'a mut self,
map: std::collections::HashMap<u32, T>,
) -> impl Iterator<Item = (Result<&'a mut dyn Any, ResourceTableError>, T)> {
map.into_iter().map(move |(k, v)| {
let item = self
.occupied_mut(k)
.map(|e| Box::as_mut(&mut e.entry))
.map(|item| unsafe { &mut *(item as *mut dyn Any) });
(item, v)
})
}
pub fn iter_children<T>(
&self,
parent: &Resource<T>,
) -> Result<impl Iterator<Item = &(dyn Any + Send)>, ResourceTableError>
where
T: 'static,
{
let parent_entry = self.occupied(parent.rep())?;
Ok(parent_entry.children.iter().map(|child_index| {
let child = self.occupied(*child_index).expect("missing child");
child.entry.as_ref()
}))
}
}
impl Default for ResourceTable {
fn default() -> Self {
ResourceTable::new()
}
}
#[test]
pub fn test_free_list() {
let mut table = ResourceTable::new();
let x = table.push(()).unwrap();
assert_eq!(x.rep(), 0);
let y = table.push(()).unwrap();
assert_eq!(y.rep(), 1);
table.delete(x).unwrap();
let x = table.push(()).unwrap();
assert_eq!(x.rep(), 0);
table.delete(x).unwrap();
table.delete(y).unwrap();
let y = table.push(()).unwrap();
assert_eq!(y.rep(), 1);
let x = table.push(()).unwrap();
assert_eq!(x.rep(), 0);
let x = table.push(()).unwrap();
assert_eq!(x.rep(), 2);
}