use crate::source::SourceVtable;
use crate::utils::small_c_string::run_with_cstr;
use crate::{error, File, Source};
use sqsh_sys as ffi;
use sqsh_sys::SqshMemoryMapperImpl;
use std::ffi::c_void;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::path::Path;
use std::ptr::NonNull;
#[derive(Debug)]
pub struct Archive<'a> {
pub(crate) inner: NonNull<ffi::SqshArchive>,
_marker: PhantomData<&'a ()>,
}
unsafe impl<'a> Send for Archive<'a> {}
unsafe impl<'a> Sync for Archive<'a> {}
impl<'a> Archive<'a> {
pub fn new<P>(path: P) -> error::Result<Self>
where
P: AsRef<Path>,
{
Self::_new(path.as_ref())
}
fn _new(path: &Path) -> error::Result<Self> {
run_with_cstr(path.as_os_str().as_encoded_bytes(), |path| unsafe {
Self::new_raw_simple(&*ffi::sqsh_mapper_impl_mmap, 0, path.as_ptr().cast())
})
}
pub fn from_slice(data: &'a [u8]) -> error::Result<Self> {
unsafe {
Self::new_raw_simple(
&*ffi::sqsh_mapper_impl_static,
data.len(),
data.as_ptr().cast(),
)
}
}
unsafe fn new_raw(config: &ffi::SqshConfig, source_ptr: *const c_void) -> error::Result<Self> {
let mut err = 0;
let archive = ffi::sqsh_archive_open(source_ptr, config, &mut err);
match NonNull::new(archive) {
Some(archive) => Ok(Self {
inner: archive,
_marker: PhantomData,
}),
None => Err(error::new(err)),
}
}
unsafe fn new_raw_simple(
source_mapper: &'a SqshMemoryMapperImpl,
size: usize,
source_ptr: *const c_void,
) -> error::Result<Self> {
let mut config: MaybeUninit<ffi::SqshConfig> = MaybeUninit::zeroed();
(*config.as_mut_ptr()).source_mapper = source_mapper;
(*config.as_mut_ptr()).source_size = size.try_into().unwrap();
Self::new_raw(config.assume_init_ref(), source_ptr)
}
pub fn with_source<S: Source + 'a>(source: S) -> error::Result<Self> {
let vtable: &'a SourceVtable<S> = &const { SourceVtable::new() };
let source_ptr = crate::source::to_ptr(source);
unsafe { Self::new_raw_simple(vtable.mapper_impl(), 0, source_ptr) }
}
}
impl<'a> Archive<'a> {
pub fn root(&self) -> error::Result<File<'_>> {
let superblock = self.superblock();
let inode_ref = superblock.root_inode_ref();
self.open_ref(inode_ref)
}
}
impl Drop for Archive<'_> {
fn drop(&mut self) {
unsafe {
ffi::sqsh_archive_close(self.inner.as_ptr());
}
}
}