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,
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |