use crate::memory::Memory;
use core::ops::{Deref, DerefMut};
use memmap2::{MmapMut, MmapOptions};
use std::{
fs::{File, OpenOptions},
path::Path,
};
pub struct MmapFile<'a> {
options: MmapOptions,
mmap: MmapMut,
len: &'a mut usize,
file: File,
}
impl<'a> MmapFile<'a> {
pub fn new(file: File, len: &'a mut usize, data_options: MmapOptions) -> std::io::Result<Self> {
let mmap = unsafe { data_options.map_mut(&file) }?;
Ok(Self {
options: data_options,
mmap,
len,
file,
})
}
pub fn into_file(self) -> File {
self.file
}
pub fn file(&self) -> &File {
&self.file
}
}
impl<'a> core::fmt::Debug for MmapFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MmapFile")
.field("options", &self.options)
.field("len", &self.len)
.field("file", &self.file)
.finish()
}
}
impl<'a> core::ops::Deref for MmapFile<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.mmap.deref()
}
}
impl<'a> core::ops::DerefMut for MmapFile<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.mmap.deref_mut()
}
}
impl<'a> Memory for MmapFile<'a>
where
Self: Deref<Target = [u8]> + DerefMut<Target = [u8]>,
{
type Error = std::io::Error;
fn as_ptr(&self) -> *const u8 {
self.mmap.as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut u8 {
self.mmap.as_mut_ptr()
}
fn len(&self) -> usize {
*self.len
}
fn len_mut(&mut self) -> &mut usize {
self.len
}
fn reserve(&mut self, capacity: usize) -> std::io::Result<()> {
let additional_cap = capacity.wrapping_sub(self.mmap.len());
if (additional_cap as isize) < 0 {
return Ok(());
}
let bytes_len = self.file.metadata()?.len() + additional_cap as u64;
self.file.set_len(bytes_len)?;
assert_eq!(bytes_len, self.file.metadata()?.len());
self.mmap = unsafe { self.options.map_mut(&self.file)? };
Ok(())
}
fn shrink(&mut self, capacity: usize) -> Result<(), Self::Error> {
let redundant_cap = self.mmap.len().wrapping_sub(capacity);
if (redundant_cap as isize) < 0 {
return Ok(());
}
let bytes_len = self.file.metadata()?.len() - redundant_cap as u64;
#[cfg(windows)]
{
self.mmap = MmapOptions::new().len(0).map_anon()?;
let set_len_result = self.file.set_len(bytes_len);
self.mmap = unsafe { self.options.map_mut(&self.file).expect("mmap is broken") };
set_len_result?;
}
#[cfg(not(windows))]
{
self.file.set_len(bytes_len)?;
self.mmap = unsafe { self.options.map_mut(&self.file)? };
}
Ok(())
}
}
pub struct VecFile<'a> {
mmap_file: MmapFile<'a>,
#[allow(dead_code)]
len_mmap: MmapMut,
}
impl<'a> core::fmt::Debug for VecFile<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("VecFile")
.field("mmap_file", &self.mmap_file)
.finish()
}
}
impl<'a> VecFile<'a> {
const HEADER_LEN: usize = core::mem::size_of::<u64>();
pub fn open_or_create(
path: impl AsRef<Path>,
init: impl FnOnce(&mut VecFile) -> Result<(), std::io::Error>,
) -> std::io::Result<Self> {
let path = path.as_ref();
let existing = path.exists();
let file = if existing {
Self::_open(path, File::options().read(true).write(true))?
} else {
let mut file =
Self::_create(path, File::options().create(true).read(true).write(true))?;
if let Err(e) = init(&mut file) {
let _ = file;
let _ = std::fs::remove_file(path);
return Err(e);
}
file
};
Ok(file)
}
pub fn create(path: impl AsRef<Path>) -> std::io::Result<Self> {
Self::_create(path.as_ref(), File::options().create(true).write(true))
}
pub fn open(path: impl AsRef<Path>) -> std::io::Result<Self> {
let mut options = File::options();
options.read(true).write(true);
Self::_open(path.as_ref(), &options)
}
fn _create(path: &Path, options: &OpenOptions) -> std::io::Result<Self> {
let file = options.open(path)?;
Self::clear(&file)?;
Self::from_file(file)
}
fn _open(path: &Path, options: &OpenOptions) -> std::io::Result<Self> {
let file = options.open(path)?;
Self::from_file(file)
}
pub fn clear(file: &File) -> std::io::Result<()> {
assert_eq!(0, file.metadata()?.len());
file.set_len(Self::HEADER_LEN as u64)?;
let len_mmap = Self::_len_mmap(file)?;
let len = unsafe { &mut *(len_mmap.deref().as_ptr() as *mut usize) };
*len = 0;
Ok(())
}
pub fn from_file(file: File) -> std::io::Result<Self> {
let len_mmap = Self::_len_mmap(&file)?;
let len = unsafe { &mut *(len_mmap.deref().as_ptr() as *mut usize) };
let mut data_options = MmapOptions::new();
data_options.offset(Self::HEADER_LEN as u64);
let mmap_file = MmapFile::new(file, len, data_options)?;
Ok(Self {
mmap_file,
len_mmap,
})
}
fn _len_mmap(file: &File) -> std::io::Result<MmapMut> {
let mut len_options = MmapOptions::new();
len_options.len(Self::HEADER_LEN);
assert!(file.metadata()?.len() >= Self::HEADER_LEN as u64);
let len_mmap = unsafe { len_options.map_mut(file) }?;
{
let (prefix, body, suffix) = unsafe { len_mmap.deref().align_to::<u64>() };
assert_eq!(prefix.len(), 0);
assert_eq!(suffix.len(), 0);
assert_eq!(body.len(), 1);
}
Ok(len_mmap)
}
pub fn into_file(self) -> File {
self.mmap_file.into_file()
}
pub fn file(&self) -> &File {
self.mmap_file.file()
}
}
impl<'a> core::ops::Deref for VecFile<'a> {
type Target = [u8];
fn deref(&self) -> &Self::Target {
self.mmap_file.deref()
}
}
impl<'a> core::ops::DerefMut for VecFile<'a> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.mmap_file.deref_mut()
}
}
impl<'a> Memory for VecFile<'a>
where
Self: Deref<Target = [u8]> + DerefMut<Target = [u8]>,
{
type Error = std::io::Error;
fn as_ptr(&self) -> *const u8 {
self.mmap_file.as_ptr()
}
fn as_mut_ptr(&mut self) -> *mut u8 {
self.mmap_file.as_mut_ptr()
}
fn len(&self) -> usize {
self.mmap_file.len()
}
fn len_mut(&mut self) -> &mut usize {
self.mmap_file.len_mut()
}
fn reserve(&mut self, capacity: usize) -> std::io::Result<()> {
self.mmap_file.reserve(capacity)
}
#[cfg(not(windows))]
fn shrink(&mut self, capacity: usize) -> Result<(), Self::Error> {
self.mmap_file.shrink(capacity)
}
#[cfg(windows)]
fn shrink(&mut self, capacity: usize) -> Result<(), Self::Error> {
self.len_mmap = MmapOptions::new().len(0).map_anon()?;
let shrink_result = self.mmap_file.shrink(capacity);
self.len_mmap = Self::_len_mmap(self.file()).expect("broken mmap");
let remapped_len = self.len_mmap.deref().as_ptr() as *mut usize;
self.mmap_file.len = unsafe { &mut *remapped_len };
shrink_result
}
}