use crate::ko::errors::StringTableParseError;
use crate::ko::SectionIdx;
use crate::{BufferIterator, WritableBuffer};
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::Hasher;
use std::slice::Iter;
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct StringIdx(u32);
impl From<usize> for StringIdx {
fn from(i: usize) -> Self {
Self(i as u32)
}
}
impl From<u8> for StringIdx {
fn from(i: u8) -> Self {
Self(i as u32)
}
}
impl From<u16> for StringIdx {
fn from(i: u16) -> Self {
Self(i as u32)
}
}
impl From<u32> for StringIdx {
fn from(i: u32) -> Self {
Self(i)
}
}
impl From<StringIdx> for usize {
fn from(str_idx: StringIdx) -> Self {
str_idx.0 as usize
}
}
impl From<StringIdx> for u32 {
fn from(str_idx: StringIdx) -> Self {
str_idx.0
}
}
impl StringIdx {
pub const EMPTY: StringIdx = StringIdx(0u32);
}
#[derive(Debug)]
pub struct StringTable {
map: HashMap<u64, usize>,
contents: Vec<String>,
section_index: SectionIdx,
size: u32,
}
impl StringTable {
pub fn new(section_index: SectionIdx) -> Self {
Self::with_capacity(0, section_index)
}
pub fn with_capacity(amount: usize, section_index: SectionIdx) -> Self {
let empty = String::new();
let mut hasher = DefaultHasher::new();
hasher.write(empty.as_bytes());
let hash = hasher.finish();
let mut contents = Vec::with_capacity(1 + amount);
contents.push(empty);
let mut map = HashMap::with_capacity(1 + amount);
map.insert(hash, 0);
Self {
map,
contents,
section_index,
size: 1,
}
}
pub fn get(&self, index: StringIdx) -> Option<&String> {
self.contents.get(usize::from(index))
}
pub fn position(&self, s: impl AsRef<str>) -> Option<StringIdx> {
let mut hasher = DefaultHasher::new();
hasher.write(s.as_ref().as_bytes());
let hash = hasher.finish();
self.map.get(&hash).cloned().map(StringIdx::from)
}
pub fn strings(&self) -> Iter<String> {
self.contents.iter()
}
pub fn add_checked(&mut self, new_str: impl Into<String>) -> StringIdx {
let s = new_str.into();
let mut hasher = DefaultHasher::new();
hasher.write(s.as_bytes());
let hash = hasher.finish();
if let Some(i) = self.map.get(&hash) {
StringIdx::from(*i)
} else {
self.add(s)
}
}
pub fn add(&mut self, new_str: impl Into<String>) -> StringIdx {
let s = new_str.into();
let mut hasher = DefaultHasher::new();
hasher.write(s.as_bytes());
let hash = hasher.finish();
let idx = self.contents.len();
self.map.insert(hash, idx);
self.size += (s.len() + 1) as u32;
self.contents.push(s);
StringIdx(idx as u32)
}
pub fn size(&self) -> u32 {
self.size
}
pub fn section_index(&self) -> SectionIdx {
self.section_index
}
pub fn parse(
source: &mut BufferIterator,
size: u32,
section_index: SectionIdx,
) -> Result<Self, StringTableParseError> {
let mut contents = Vec::new();
let mut map = HashMap::new();
let mut read = 0;
while read < size {
let mut b = Vec::new();
while read < size {
let c = source
.next()
.ok_or_else(|| StringTableParseError::EOFError(source.current_index(), size))?;
read += 1;
if c == b'\0' {
break;
}
b.push(c);
}
let s = String::from_utf8(b)
.map_err(|e| StringTableParseError::InvalidUtf8Error(contents.len(), e))?;
let mut hasher = DefaultHasher::new();
hasher.write(s.as_bytes());
let hash = hasher.finish();
let idx = contents.len();
map.insert(hash, idx);
contents.push(s);
}
Ok(Self {
map,
contents,
section_index,
size,
})
}
pub fn write(&self, buf: &mut impl WritableBuffer) {
for string in self.contents.iter() {
buf.write_bytes(string.as_bytes());
buf.write(0);
}
}
}