use super::Str;
use core::alloc::Layout;
use core::cell::Cell;
use core::ptr::null;
use std::alloc::handle_alloc_error;
pub(crate) struct StringTable<D> {
hash: Cell<*mut *const Str<D>>,
nuse: Cell<usize>,
size: Cell<usize>,
}
impl<D> StringTable<D> {
pub fn new() -> Self {
let size = 128;
let layout = Layout::array::<*const Str<D>>(size).unwrap();
let hash = unsafe { alloc::alloc::alloc_zeroed(layout) };
if hash.is_null() {
handle_alloc_error(layout);
}
Self {
hash: Cell::new(hash.cast()),
nuse: Cell::new(0),
size: Cell::new(size),
}
}
pub(super) unsafe fn insert(
&self,
h: u32,
str: &[u8],
) -> Result<*const Str<D>, *mut *const Str<D>> {
let mut e = self.entry(h);
let mut s = unsafe { *e };
while !s.is_null() {
if unsafe { (*s).as_bytes() == str } {
return Ok(s);
}
s = unsafe { (*s).hnext.get() };
}
if self.nuse >= self.size {
self.resize(self.size.get().checked_mul(2).unwrap());
e = self.entry(h);
}
self.nuse.set(self.nuse.get() + 1);
Err(e)
}
#[inline(never)]
pub(super) unsafe fn remove(&self, s: *mut Str<D>) {
let mut e = self.entry(unsafe { (*s).hash.get() });
while unsafe { *e != s } {
e = unsafe { (**e).hnext.as_ptr() };
}
unsafe { *e = (**e).hnext.get() };
self.nuse.set(self.nuse.get() - 1);
}
#[inline(always)]
fn entry(&self, h: u32) -> *mut *const Str<D> {
let t = self.hash.get();
unsafe { t.add(usize::try_from(h).unwrap() & (self.size.get() - 1)) }
}
fn resize(&self, nsize: usize) {
let table = self.hash.get();
let osize = self.size.get();
if nsize < osize {
unsafe { Self::rehash(table, osize, nsize) };
}
let layout = Layout::array::<*const Str<D>>(osize).unwrap();
let newvect = unsafe {
alloc::alloc::realloc(table.cast(), layout, nsize * size_of::<*const Str<D>>())
};
if newvect.is_null() {
handle_alloc_error(Layout::array::<*const Str<D>>(nsize).unwrap());
}
self.hash.set(newvect.cast());
self.size.set(nsize);
if nsize > osize {
unsafe { Self::rehash(self.hash.get(), osize, nsize) };
}
}
unsafe fn rehash(vect: *mut *const Str<D>, osize: usize, nsize: usize) {
for i in osize..nsize {
unsafe { vect.add(i).write(null()) };
}
for i in 0..osize {
let mut p = unsafe { vect.add(i).replace(null()) };
while !p.is_null() {
let hnext = unsafe { (*p).hnext.get() };
let h = usize::try_from(unsafe { (*p).hash.get() }).unwrap() & (nsize - 1);
unsafe { (*p).hnext.set(*vect.add(h)) };
unsafe { vect.add(h).write(p) };
p = hnext;
}
}
}
}
impl<D> Drop for StringTable<D> {
fn drop(&mut self) {
let layout = Layout::array::<*const Str<D>>(self.size.get()).unwrap();
unsafe { alloc::alloc::dealloc(self.hash.get().cast(), layout) };
}
}