use alloc::borrow::Cow;
use core::ffi::CStr;
use core::iter::FusedIterator;
#[cfg(unix)]
use std::ffi::OsStr;
#[cfg(not(unix))]
use std::ffi::OsString;
use std::fs::File;
use std::path::Path;
#[cfg(not(unix))]
use std::path::PathBuf;
use memmap2::{Mmap, MmapOptions};
use crate::errors::Error;
use crate::Result;
#[cfg(unix)]
pub(crate) fn os_str_from_cstr(cstr: &CStr) -> Result<&OsStr> {
use std::os::unix::ffi::OsStrExt;
Ok(OsStr::from_bytes(cstr.to_bytes()))
}
#[cfg(windows)]
pub(crate) fn os_string_from_cstr(cstr: &CStr) -> Result<OsString> {
use std::os::windows::ffi::OsStringExt;
let wstr: Vec<_> = cstr.to_str()?.encode_utf16().collect();
Ok(OsString::from_wide(&wstr))
}
#[cfg(unix)]
pub(crate) fn path_from_cstr(cstr: &CStr) -> Result<&Path> {
os_str_from_cstr(cstr).map(Path::new)
}
#[cfg(not(unix))]
pub(crate) fn path_buf_from_cstr(cstr: &CStr) -> Result<PathBuf> {
os_string_from_cstr(cstr).map(PathBuf::from)
}
#[cfg(unix)]
pub(crate) fn path_from_bytes(bytes: &[u8]) -> Result<Cow<Path>> {
use std::os::unix::ffi::OsStrExt;
Ok(Cow::Borrowed(Path::new(OsStr::from_bytes(bytes))))
}
#[cfg(windows)]
pub(crate) fn path_from_bytes(bytes: &[u8]) -> Result<Cow<Path>> {
use std::os::windows::ffi::OsStringExt;
let wstr: Vec<_> = std::str::from_utf8(bytes)?.encode_utf16().collect();
Ok(Cow::Owned(PathBuf::from(OsString::from_wide(&wstr))))
}
pub(crate) fn map_file(path: &Path) -> Result<Mmap> {
let file = File::open(path).map_err(|source| Error::Open {
source,
path: path.into(),
})?;
let md = file.metadata().map_err(|source| Error::ReadMetaData {
source,
path: path.into(),
})?;
let size = usize::try_from(md.len()).unwrap_or(usize::MAX);
if size == 0 {
return Err(Error::FileIsEmpty { path: path.into() });
}
unsafe { MmapOptions::default().len(size).map(&file) }.map_err(|source| Error::MapFile {
source,
path: path.into(),
})
}
#[derive(Debug)]
pub(crate) struct CStringTable<'table> {
string_table: &'table [u8],
pub(crate) path: &'table Path,
}
impl<'table> CStringTable<'table> {
pub(crate) fn new(string_table: &'table [u8], path: &'table Path) -> Self {
Self { string_table, path }
}
pub(crate) fn get(&self, start_index: u32) -> Result<&'table CStr> {
let start_index = usize::try_from(start_index)?;
let bytes = self
.string_table
.get(start_index..)
.ok_or_else(|| Error::OffsetIsInvalid {
path: self.path.into(),
})?;
CStr::from_bytes_until_nul(bytes).map_err(Error::from)
}
}
#[derive(Debug)]
pub(crate) struct HashTableIter<'cache, const BUCKET_SIZE: usize, P> {
hash_table: &'cache [u8],
string_table: CStringTable<'cache>,
next_hash_table_entry_parser: P,
}
impl<'cache, const BUCKET_SIZE: usize, P> HashTableIter<'cache, BUCKET_SIZE, P> {
pub(crate) fn new(
hash_table: &'cache [u8],
string_table: CStringTable<'cache>,
next_hash_table_entry_parser: P,
) -> Self {
Self {
hash_table,
string_table,
next_hash_table_entry_parser,
}
}
}
impl<'cache, const BUCKET_SIZE: usize, P> Iterator for HashTableIter<'cache, BUCKET_SIZE, P>
where
P: nom::Parser<&'cache [u8], Output = (u32, u32), Error = nom::error::Error<&'cache [u8]>>,
{
type Item = Result<crate::Entry<'cache>>;
fn next(&mut self) -> Option<Self::Item> {
(self.hash_table.len() >= BUCKET_SIZE).then(|| {
let (input, (key, value)) = self
.next_hash_table_entry_parser
.parse(self.hash_table)
.map_err(|r| Error::from_nom_parse(r, self.hash_table, self.string_table.path))?;
self.hash_table = input;
crate::Entry::new_by_string_table_indices(&self.string_table, key, value)
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = self.hash_table.len() / BUCKET_SIZE;
(remaining, Some(remaining))
}
}
impl<'cache, const BUCKET_SIZE: usize, P> FusedIterator for HashTableIter<'cache, BUCKET_SIZE, P> where
P: nom::Parser<&'cache [u8], Output = (u32, u32), Error = nom::error::Error<&'cache [u8]>>
{
}
impl<'cache, const BUCKET_SIZE: usize, P> ExactSizeIterator
for HashTableIter<'cache, BUCKET_SIZE, P>
where
P: nom::Parser<&'cache [u8], Output = (u32, u32), Error = nom::error::Error<&'cache [u8]>>,
{
}