179 lines
4.8 KiB
Rust
179 lines
4.8 KiB
Rust
use std::{ops::Index, ptr::NonNull, sync::RwLock};
|
|
|
|
use crate::core_types::FName;
|
|
|
|
lazy_static::lazy_static! {
|
|
pub static ref GNAMES: RwLock<GNames> = RwLock::new(GNames::new());
|
|
}
|
|
|
|
#[derive(Debug, Default)]
|
|
pub struct GNames {
|
|
names: Option<NonNull<NameEntryArray>>,
|
|
}
|
|
|
|
impl GNames {
|
|
pub const fn new() -> Self {
|
|
Self { names: None }
|
|
}
|
|
|
|
pub fn set_names(&mut self, names: NonNull<NameEntryArray>) {
|
|
self.names = Some(names);
|
|
}
|
|
|
|
pub fn as_names(&self) -> Option<&NameEntryArray> {
|
|
self.names.map(|names| unsafe { names.as_ref() })
|
|
}
|
|
}
|
|
|
|
unsafe impl Send for GNames {}
|
|
unsafe impl Sync for GNames {}
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug)]
|
|
pub struct FNameEntry {
|
|
pub index: u32,
|
|
pub hash_next: *const FNameEntry,
|
|
pub ansi_name: [u8; 1024],
|
|
}
|
|
|
|
impl FNameEntry {
|
|
/// panics if the name cant be turned into a string
|
|
pub fn as_string(&self) -> String {
|
|
self.try_into().unwrap()
|
|
}
|
|
|
|
pub fn as_str(&self) -> &str {
|
|
let end = self.ansi_name.iter().position(|&c| c == 0);
|
|
let bytes = &self.ansi_name[..end.unwrap_or(self.ansi_name.len())];
|
|
// TODO: debug assert this
|
|
unsafe { std::str::from_utf8_unchecked(bytes) }
|
|
}
|
|
}
|
|
|
|
impl<'a> TryInto<String> for &'a FNameEntry {
|
|
type Error = std::string::FromUtf8Error;
|
|
|
|
fn try_into(self) -> Result<String, Self::Error> {
|
|
let bytes = self
|
|
.ansi_name
|
|
.iter()
|
|
.take_while(|&&b| b != 0)
|
|
.map(|&b| b)
|
|
.collect::<Vec<_>>();
|
|
String::from_utf8(bytes)
|
|
}
|
|
}
|
|
|
|
const NAMES_ELEMENTS_PER_CHUNK: usize = 16 * 1024;
|
|
const NAMES_CHUNK_TABLE_SIZE: usize =
|
|
(2 * 1024 * 1024 + NAMES_ELEMENTS_PER_CHUNK - 1) / NAMES_ELEMENTS_PER_CHUNK;
|
|
|
|
#[repr(C)]
|
|
#[derive(Debug)]
|
|
pub struct NameEntryArray {
|
|
chunks: [Option<NonNull<Option<NonNull<FNameEntry>>>>; NAMES_CHUNK_TABLE_SIZE],
|
|
num_elements: u32,
|
|
num_chunks: u32,
|
|
}
|
|
|
|
unsafe impl Send for NameEntryArray {}
|
|
unsafe impl Sync for NameEntryArray {}
|
|
|
|
impl NameEntryArray {
|
|
/// this does not mean that the name at this index is actually present!, just that looking it up is sound, because all the indices point at allocated memory
|
|
pub fn is_valid_index(&self, index: usize) -> bool {
|
|
index < self.num_elements as usize
|
|
}
|
|
|
|
/// panics if chunk_index is out of bounds
|
|
fn chunk_as_slice(&self, chunk_index: usize) -> Option<&[Option<NonNull<FNameEntry>>]> {
|
|
// TODO: probably make this unchecked for release?
|
|
self.chunks
|
|
// get chunk
|
|
[chunk_index]
|
|
// cast chunk pointer into slice of FNameEntry pointers
|
|
.map(|ptr| unsafe {
|
|
core::slice::from_raw_parts(ptr.as_ptr(), NAMES_ELEMENTS_PER_CHUNK)
|
|
})
|
|
}
|
|
|
|
pub fn get_index(&self, index: usize) -> Option<&FNameEntry> {
|
|
if index < self.num_elements as usize {
|
|
let chunk_index = index / NAMES_ELEMENTS_PER_CHUNK;
|
|
let within_chunk_index = index % NAMES_ELEMENTS_PER_CHUNK;
|
|
|
|
// safety: we know chunk_index and within_chunk_index are valid (or do we?);
|
|
// TODO: make sure this is actually the case
|
|
// it is because index is < than self.num_elements
|
|
self.chunk_as_slice(chunk_index)?[within_chunk_index].map(|ptr| unsafe { ptr.as_ref() })
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub fn iter(&self) -> NamesIterator {
|
|
NamesIterator::new(self)
|
|
}
|
|
|
|
pub fn len(&self) -> usize {
|
|
self.num_elements as usize
|
|
}
|
|
|
|
pub fn fname_to_string(&self, name: &FName) -> anyhow::Result<String> {
|
|
use anyhow::Context;
|
|
Ok(self
|
|
.get_index(name.comparison_index as usize)
|
|
.context("invalid comparison index")?
|
|
.try_into()?)
|
|
}
|
|
}
|
|
|
|
impl Index<usize> for NameEntryArray {
|
|
type Output = FNameEntry;
|
|
|
|
fn index(&self, i: usize) -> &Self::Output {
|
|
self.get_index(i).expect("Out of bounds access")
|
|
}
|
|
}
|
|
|
|
pub struct NamesIterator<'a> {
|
|
names: &'a NameEntryArray,
|
|
index: usize,
|
|
}
|
|
|
|
impl<'a> NamesIterator<'a> {
|
|
pub fn new(names: &'a NameEntryArray) -> Self {
|
|
Self { names, index: 0 }
|
|
}
|
|
}
|
|
|
|
impl<'a> Iterator for NamesIterator<'a> {
|
|
type Item = &'a FNameEntry;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
loop {
|
|
if !self.names.is_valid_index(self.index) {
|
|
break None;
|
|
}
|
|
let item = self.names.get_index(self.index);
|
|
self.index += 1;
|
|
// skip empty entries, we dont care about them
|
|
if item.is_some() {
|
|
break item;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'a> IntoIterator for &'a NameEntryArray {
|
|
type Item = &'a FNameEntry;
|
|
type IntoIter = NamesIterator<'a>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
NamesIterator {
|
|
names: self,
|
|
index: 0,
|
|
}
|
|
}
|
|
}
|