#![allow(box_pointers)]
use std::{
mem,
sync::{
atomic::{self, AtomicPtr},
Mutex,
},
u16,
};
use astral_thirdparty::slog::{warn, Logger};
use super::{Allocator, Entry, StaticRefVector, StringId, MAX_STRING_LENGTH};
const NUM_BUCKETS: usize = u16::max_value() as usize + 1;
pub(super) struct EntryHashTable {
head: Box<[AtomicPtr<Entry>; NUM_BUCKETS]>,
}
impl EntryHashTable {
pub(super) fn new() -> (Self, usize, usize) {
let table = Self {
head: Box::new(unsafe { mem::zeroed() }),
};
let used_memory = mem::size_of::<AtomicPtr<Entry>>() * NUM_BUCKETS;
let used_chunks = 1;
(table, used_memory, used_chunks)
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn find(&self, name: &str, hash: u16) -> Option<&Entry> {
debug_assert!((hash as usize) < self.head.len());
let head = self.head[hash as usize].load(atomic::Ordering::Acquire);
if head.is_null() {
None
} else {
for entry in unsafe { (*head).iter() } {
if entry.as_str() == name {
return Some(entry);
}
}
None
}
}
#[allow(clippy::cast_possible_truncation)]
pub(super) fn find_or_insert(
&self,
string: &str,
hash: u64,
reference_map: &StaticRefVector<Entry>,
allocator: &Mutex<Allocator>,
log: &Logger,
) -> (StringId, usize, usize, bool) {
let hash = hash as u16;
if hash == 60224 {}
if let Some(entry) = self.find(string, hash) {
return (entry.id(), 0, 0, false);
}
let mut allocator = allocator.lock().unwrap();
if let Some(entry) = self.find(string, hash) {
return (entry.id(), 0, 0, false);
}
let (mut entry, alloc_memory, alloc_chunks) = if string.len() > MAX_STRING_LENGTH {
warn!(log,
"string is too long and will be shorten";
"max length" => MAX_STRING_LENGTH,
"length" => string.len(),
);
allocator.allocate(&string[0..MAX_STRING_LENGTH])
} else {
allocator.allocate(string)
};
debug_assert!(entry.id.is_none());
unsafe {
let (id, map_memory, map_chunks) = reference_map.push(entry);
(*entry).id = Some(id);
let head = self.head[hash as usize].load(atomic::Ordering::Relaxed);
if head.is_null() {
self.head[hash as usize].store(entry, atomic::Ordering::Release);
} else {
let next = (*head)
.iter()
.last()
.expect("unexpeted end of hash bucket")
.next();
debug_assert!(next.load(atomic::Ordering::SeqCst).is_null());
next.store(entry, atomic::Ordering::Release)
}
(
(*entry).id(),
alloc_memory + map_memory,
alloc_chunks + map_chunks,
true,
)
}
}
}
unsafe impl Send for EntryHashTable {}
unsafe impl Sync for EntryHashTable {}