use std::mem::{MaybeUninit, size_of};
use doublets::mem::RawMem;
use doublets::mem::unit::LinkPart;
use link_cli::storage::PersistentFileMapped;
use super::StorageError;
const DOUBLETS_BOOTSTRAP_ITEMS: usize = 8 * 1024;
pub(super) struct LoadedFileMapped {
inner: PersistentFileMapped<LinkPart<usize>>,
preserve_bootstrap: bool,
}
impl LoadedFileMapped {
pub(super) fn new(file: std::fs::File) -> Result<Self, StorageError> {
let bytes = usize::try_from(file.metadata()?.len())
.map_err(|_| std::io::Error::other("mapped file is too large for this platform"))?;
if bytes % size_of::<LinkPart<usize>>() != 0 && bytes >= 4096 {
return Err(StorageError::Codec(
"mapped file length is not aligned to a doublets link part".into(),
));
}
let mut inner = PersistentFileMapped::new(file)?;
let items = bytes.max(4096) / size_of::<LinkPart<usize>>();
#[allow(unsafe_code)]
unsafe {
inner
.grow_assumed(items)
.map_err(|error| StorageError::Codec(format!("restore capacity: {error}")))?;
}
Ok(Self {
inner,
preserve_bootstrap: items > DOUBLETS_BOOTSTRAP_ITEMS,
})
}
}
impl RawMem for LoadedFileMapped {
type Item = LinkPart<usize>;
fn allocated(&self) -> &[Self::Item] {
self.inner.allocated()
}
fn allocated_mut(&mut self) -> &mut [Self::Item] {
self.inner.allocated_mut()
}
#[allow(unsafe_code)]
unsafe fn grow(
&mut self,
addition: usize,
fill: impl FnOnce(usize, (&mut [Self::Item], &mut [MaybeUninit<Self::Item>])),
) -> doublets::mem::Result<&mut [Self::Item]> {
unsafe {
self.inner.grow(addition, fill)?;
}
Ok(self.inner.allocated_mut())
}
fn shrink(&mut self, count: usize) -> doublets::mem::Result<()> {
if self.preserve_bootstrap
&& self.inner.allocated().len().saturating_sub(count) == DOUBLETS_BOOTSTRAP_ITEMS
{
self.preserve_bootstrap = false;
return Ok(());
}
self.inner.shrink(count)
}
}