partway towards commands
I got very confused and started mucking about with "spawn" when in fact all I needed was the "inline" extension type in orcx that allows the interpreter to expose custom constants.
This commit is contained in:
@@ -1,50 +1,120 @@
|
||||
use std::num::NonZeroU64;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Mutex, MutexGuard, OnceLock};
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use hashbrown::HashMap;
|
||||
use itertools::Itertools;
|
||||
|
||||
enum Rec<T> {
|
||||
Val(T),
|
||||
Next(usize),
|
||||
}
|
||||
|
||||
/// A simple and very fast store that assigns small stable integer IDs to
|
||||
/// objects. It uses a free-list for O(1) insertion, deletion and retrieval.
|
||||
pub struct IdStore<T> {
|
||||
table: OnceLock<Mutex<HashMap<NonZeroU64, T>>>,
|
||||
id: AtomicU64,
|
||||
first: usize,
|
||||
values: Vec<Rec<T>>,
|
||||
}
|
||||
impl<T> IdStore<T> {
|
||||
pub const fn new() -> Self { Self { table: OnceLock::new(), id: AtomicU64::new(1) } }
|
||||
pub fn add(&self, t: T) -> IdRecord<'_, T> {
|
||||
let tbl = self.table.get_or_init(Mutex::default);
|
||||
let mut tbl_g = tbl.lock().unwrap();
|
||||
let id: NonZeroU64 = self.id.fetch_add(1, Ordering::Relaxed).try_into().unwrap();
|
||||
assert!(tbl_g.insert(id, t).is_none(), "atom ID wraparound");
|
||||
IdRecord(id, tbl_g)
|
||||
pub fn new() -> Self { IdStore { first: 0, values: Vec::new() } }
|
||||
pub fn add(&mut self, value: T) -> usize {
|
||||
if self.first == 0 && self.values.is_empty() {
|
||||
self.first = 1;
|
||||
self.values.push(Rec::Val(value));
|
||||
return 0;
|
||||
}
|
||||
if self.first == self.values.len() {
|
||||
let len = self.values.len();
|
||||
self.values.extend((len..len * 2).map(|i| Rec::Next(i + 1)));
|
||||
}
|
||||
let Some(rec) = self.values.get_mut(self.first) else {
|
||||
panic!("Bounds check and growth above")
|
||||
};
|
||||
let Rec::Next(next) = rec else {
|
||||
panic!("first should always point to an empty space or one past the length")
|
||||
};
|
||||
let id = std::mem::replace(&mut self.first, *next);
|
||||
*rec = Rec::Val(value);
|
||||
id
|
||||
}
|
||||
pub fn get(&self, id: impl Into<NonZeroU64>) -> Option<IdRecord<'_, T>> {
|
||||
let tbl = self.table.get_or_init(Mutex::default);
|
||||
let tbl_g = tbl.lock().unwrap();
|
||||
let id64 = id.into();
|
||||
if tbl_g.contains_key(&id64) { Some(IdRecord(id64, tbl_g)) } else { None }
|
||||
pub fn add_with(&mut self, cb: impl FnOnce(usize) -> T) -> usize { self.add(cb(self.first)) }
|
||||
pub fn remove(&mut self, id: usize) -> T {
|
||||
let Some(rec) = self.values.get_mut(id) else { panic!("Index out of bounds") };
|
||||
let Rec::Val(val) = std::mem::replace(rec, Rec::Next(self.first)) else {
|
||||
panic!("Index vacated")
|
||||
};
|
||||
self.first = id;
|
||||
val
|
||||
}
|
||||
pub fn iter(&self) -> impl Iterator<Item = (usize, &T)> {
|
||||
(self.values.iter().enumerate())
|
||||
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None })
|
||||
}
|
||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = (usize, &mut T)> {
|
||||
(self.values.iter_mut().enumerate())
|
||||
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None })
|
||||
}
|
||||
}
|
||||
#[allow(clippy::type_complexity, reason = "This is verbose enough as it is")]
|
||||
pub struct IntoIter<T>(
|
||||
std::iter::FilterMap<
|
||||
std::iter::Enumerate<std::vec::IntoIter<Rec<T>>>,
|
||||
fn((usize, Rec<T>)) -> Option<(usize, T)>,
|
||||
>,
|
||||
);
|
||||
impl<T> Iterator for IntoIter<T> {
|
||||
type Item = (usize, T);
|
||||
fn next(&mut self) -> Option<Self::Item> { self.0.next() }
|
||||
fn size_hint(&self) -> (usize, Option<usize>) { self.0.size_hint() }
|
||||
}
|
||||
impl<T> IntoIterator for IdStore<T> {
|
||||
type Item = (usize, T);
|
||||
type IntoIter = IntoIter<T>;
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
IntoIter(
|
||||
(self.values.into_iter().enumerate())
|
||||
.filter_map(|(i, rec)| if let Rec::Val(val) = rec { Some((i, val)) } else { None }),
|
||||
)
|
||||
}
|
||||
}
|
||||
impl<T> Index<usize> for IdStore<T> {
|
||||
type Output = T;
|
||||
fn index(&self, index: usize) -> &Self::Output {
|
||||
match self.values.get(index) {
|
||||
Some(Rec::Val(val)) => val,
|
||||
_ => panic!("Invalid or stale index"),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<T> IndexMut<usize> for IdStore<T> {
|
||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
||||
match self.values.get_mut(index) {
|
||||
Some(Rec::Val(val)) => val,
|
||||
_ => panic!("Invalid or stale index"),
|
||||
}
|
||||
}
|
||||
pub fn is_empty(&self) -> bool { self.len() == 0 }
|
||||
pub fn len(&self) -> usize { self.table.get().map(|t| t.lock().unwrap().len()).unwrap_or(0) }
|
||||
}
|
||||
|
||||
impl<T> Default for IdStore<T> {
|
||||
fn default() -> Self { Self::new() }
|
||||
}
|
||||
impl<A> FromIterator<A> for IdStore<A> {
|
||||
fn from_iter<T: IntoIterator<Item = A>>(iter: T) -> Self {
|
||||
let values = iter.into_iter().map(|a| Rec::Val(a)).collect_vec();
|
||||
Self { first: values.len(), values }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct IdRecord<'a, T>(NonZeroU64, MutexGuard<'a, HashMap<NonZeroU64, T>>);
|
||||
impl<T> IdRecord<'_, T> {
|
||||
pub fn id(&self) -> NonZeroU64 { self.0 }
|
||||
pub fn remove(mut self) -> T { self.1.remove(&self.0).unwrap() }
|
||||
}
|
||||
impl<T> Deref for IdRecord<'_, T> {
|
||||
type Target = T;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.1.get(&self.0).expect("Existence checked on construction")
|
||||
}
|
||||
}
|
||||
impl<T> DerefMut for IdRecord<'_, T> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
self.1.get_mut(&self.0).expect("Existence checked on construction")
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn add_and_retrieve() {
|
||||
let mut store = IdStore::new();
|
||||
let key1 = store.add(14);
|
||||
let key2 = store.add(34);
|
||||
assert_eq!(store[key1], 14);
|
||||
assert_eq!(store[key2], 34);
|
||||
assert_eq!(store.remove(key1), 14);
|
||||
assert_eq!(store.iter().collect_vec(), vec![(key2, &34)]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user