blotter.rs/src/misc/object_store.rs
Adam Gausmann 934055d060 Sandbox: Refactor to use ObjectStore for components/wires
And don't directly map raw IDs to sandbox, reallocate and remap.
2022-11-08 20:01:41 -06:00

161 lines
4.3 KiB
Rust

use std::{
fmt::Debug,
hash::{Hash, Hasher},
marker::PhantomData,
mem::replace,
};
/// An object store that stores a free-list inline with its elements.
///
/// The original idea for this allocator is to have a stack of indexes that have
/// been vacated when items are removed from those locations. When adding an
/// item, the stack is checked first for a free index; if the stack is empty,
/// then the list has no vacant entries and the item is simply appended to the
/// end.
///
/// Instead of storing this stack separately; the cost of the free-list stack is
/// amortized by storing it inline with the items themselves. This may require
/// up to 8 bytes in addition to the size of each item.
pub struct ObjectStore<T> {
first_vacant: usize,
entries: Vec<Entry<T>>,
}
impl<T> ObjectStore<T> {
pub fn new() -> Self {
Self {
first_vacant: usize::MAX,
entries: Vec::new(),
}
}
pub fn contains(&self, address: Address<T>) -> bool {
self.get(address).is_some()
}
pub fn insert(&mut self, item: T) -> Address<T> {
if let Some(entry) = self.entries.get_mut(self.first_vacant) {
let address = Address::from_raw(self.first_vacant);
let replaced = replace(entry, Entry::Occupied(item));
match replaced {
Entry::Vacant { next_vacant } => {
self.first_vacant = next_vacant;
}
_ => {
unreachable!("occupied entry in free list");
}
}
address
} else {
let address = Address::from_raw(self.entries.len());
self.entries.push(Entry::Occupied(item));
address
}
}
pub fn get(&self, address: Address<T>) -> Option<&T> {
self.entries.get(address.into_raw()).and_then(Entry::get)
}
pub fn get_mut(&mut self, address: Address<T>) -> Option<&mut T> {
self.entries
.get_mut(address.into_raw())
.and_then(Entry::get_mut)
}
pub fn remove(&mut self, address: Address<T>) -> Option<T> {
let index = address.into_raw();
match self.entries.get(index)? {
Entry::Occupied(_) => {
let replaced = replace(
&mut self.entries[index],
Entry::Vacant {
next_vacant: self.first_vacant,
},
);
self.first_vacant = index;
match replaced {
Entry::Occupied(x) => Some(x),
_ => unreachable!("occupied is not occupied?"),
}
}
Entry::Vacant { .. } => None,
}
}
pub fn iter(&self) -> impl Iterator<Item = (Address<T>, &T)> {
self.entries
.iter()
.enumerate()
.flat_map(|(index, entry)| entry.get().map(|item| (Address::from_raw(index), item)))
}
pub fn iter_mut(&mut self) -> impl Iterator<Item = (Address<T>, &mut T)> {
self.entries
.iter_mut()
.enumerate()
.flat_map(|(index, entry)| entry.get_mut().map(|item| (Address::from_raw(index), item)))
}
}
enum Entry<T> {
Vacant { next_vacant: usize },
Occupied(T),
}
impl<T> Entry<T> {
fn get(&self) -> Option<&T> {
match self {
Self::Occupied(x) => Some(x),
_ => None,
}
}
fn get_mut(&mut self) -> Option<&mut T> {
match self {
Self::Occupied(x) => Some(x),
_ => None,
}
}
}
pub struct Address<T>(usize, PhantomData<T>);
impl<T> Address<T> {
pub fn from_raw(raw: usize) -> Self {
Self(raw, PhantomData)
}
pub fn into_raw(self) -> usize {
self.0
}
}
impl<T> Debug for Address<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Address").field(&self.0).finish()
}
}
impl<T> Clone for Address<T> {
fn clone(&self) -> Self {
Self(self.0, PhantomData)
}
}
impl<T> Copy for Address<T> {}
impl<T> PartialEq for Address<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> Eq for Address<T> {}
impl<T> Hash for Address<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash(state);
}
}