use std::{collections::HashMap, marker::PhantomData};
type MemoTable<T> = Vec<T>;
pub(crate) type ParserId = usize;
type EntryId = usize;
struct ErasedMemoTable<'src> {
ptr: *mut (),
drop_fn: unsafe fn(*mut ()),
_marker: PhantomData<&'src mut ()>,
}
impl<'src> Drop for ErasedMemoTable<'src> {
fn drop(&mut self) {
unsafe { (self.drop_fn)(self.ptr) };
}
}
unsafe fn drop_erased_memo_table<T>(ptr: *mut ()) {
let ptr: *mut MemoTable<T> = ptr.cast();
let table = unsafe { Box::from_raw(ptr) };
drop(table);
}
impl<'src> ErasedMemoTable<'src> {
fn new<T: 'src>() -> Self {
let boxed = Box::new(MemoTable::<T>::new());
let ptr: *mut () = Box::into_raw(boxed).cast();
Self {
ptr,
drop_fn: drop_erased_memo_table::<T>,
_marker: PhantomData,
}
}
unsafe fn get_unerased_table<'a, T: 'src>(&'a self) -> &'a MemoTable<T> {
let ptr: *mut MemoTable<T> = self.ptr.cast();
unsafe { &*ptr }
}
unsafe fn get_unerased_table_mut<'a, T: 'src>(&'a mut self) -> &'a mut MemoTable<T> {
let ptr: *mut MemoTable<T> = self.ptr.cast();
unsafe { &mut *ptr }
}
unsafe fn get_entry<'a, T: 'src>(&'a self, entry_id: EntryId) -> &'a T {
let table = unsafe { self.get_unerased_table::<T>() };
&table[entry_id]
}
unsafe fn add_entry<T: 'src>(&mut self, entry: T) -> EntryId {
let table = unsafe { self.get_unerased_table_mut::<T>() };
let entry_id = table.len();
table.push(entry);
entry_id
}
}
pub(crate) struct Cache<'src> {
index: HashMap<(ParserId, usize), (usize, EntryId)>,
table_index: HashMap<ParserId, usize>,
tables: Vec<ErasedMemoTable<'src>>,
}
impl<'src> Cache<'src> {
pub fn new() -> Self {
Self {
index: HashMap::new(),
table_index: HashMap::new(),
tables: Vec::new(),
}
}
fn get_or_create_table<T: 'src>(&mut self, parser_id: ParserId) -> usize {
self.table_index
.get(&parser_id)
.cloned()
.unwrap_or_else(|| {
let table_id = self.tables.len();
self.tables.push(ErasedMemoTable::new::<T>());
self.table_index.insert(parser_id, table_id);
table_id
})
}
pub(crate) unsafe fn get_entry<'a, T: 'src>(
&'a self,
parser_id: ParserId,
pos: usize,
) -> Option<&'a T> {
let (table_id, entry_id) = self.index.get(&(parser_id, pos))?;
let erased_table = &self.tables[*table_id];
Some(unsafe { erased_table.get_entry::<T>(*entry_id) })
}
pub(crate) unsafe fn set_entry<'a, T: 'src>(
&'a mut self,
parser_id: ParserId,
pos: usize,
entry: T,
) -> &'a T {
let table_id = self.get_or_create_table::<T>(parser_id);
let table = &mut self.tables[table_id];
let entry_id = unsafe { table.add_entry(entry) };
self.index.insert((parser_id, pos), (table_id, entry_id));
unsafe { table.get_entry(entry_id) }
}
}