use std::io::{self, Seek, SeekFrom};
use std::marker::PhantomData;
use memmap2;
use page_size;
use tempfile::NamedTempFile;
use crate::Bytes;
#[cfg(feature = "zerocopy")]
use zerocopy::{FromBytes, Immutable};
#[cfg(feature = "zerocopy")]
use crate::view::{View, ViewError};
fn align_up(val: usize, align: usize) -> usize {
(val + align - 1) & !(align - 1)
}
#[derive(Debug)]
pub struct ByteArea {
file: NamedTempFile,
len: usize,
}
impl ByteArea {
pub fn new() -> io::Result<Self> {
let file = NamedTempFile::new()?;
Ok(Self { file, len: 0 })
}
pub fn sections(&mut self) -> SectionWriter<'_> {
SectionWriter { area: self }
}
pub fn freeze(self) -> io::Result<Bytes> {
let file = self.file.into_file();
let mmap = unsafe { memmap2::MmapOptions::new().map(&file)? };
Ok(Bytes::from_source(mmap))
}
pub fn persist<P: AsRef<std::path::Path>>(self, path: P) -> io::Result<std::fs::File> {
self.file.persist(path).map_err(Into::into)
}
}
#[derive(Debug)]
pub struct SectionWriter<'area> {
area: &'area mut ByteArea,
}
impl<'area> SectionWriter<'area> {
pub fn reserve<T>(&mut self, elems: usize) -> io::Result<Section<'area, T>>
where
T: FromBytes + Immutable,
{
let page = page_size::get();
let align = core::mem::align_of::<T>();
let len_bytes = core::mem::size_of::<T>() * elems;
let start = align_up(self.area.len, align);
let end = start + len_bytes;
let aligned_offset = start & !(page - 1);
let offset = start - aligned_offset;
let map_len = end - aligned_offset;
let file = &mut self.area.file;
file.as_file_mut().set_len(end as u64)?;
file.as_file_mut().seek(SeekFrom::Start(end as u64))?;
let mmap = unsafe {
memmap2::MmapOptions::new()
.offset(aligned_offset as u64)
.len(map_len)
.map_mut(file.as_file())?
};
self.area.len = end;
Ok(Section {
mmap,
offset,
start,
elems,
_marker: PhantomData,
})
}
}
#[derive(Debug)]
pub struct Section<'arena, T> {
mmap: memmap2::MmapMut,
offset: usize,
start: usize,
elems: usize,
_marker: PhantomData<(&'arena ByteArea, *mut T)>,
}
impl<'arena, T> Section<'arena, T>
where
T: FromBytes + Immutable,
{
pub fn as_mut_slice(&mut self) -> &mut [T] {
unsafe {
let ptr = self.mmap.as_mut_ptr().add(self.offset) as *mut T;
core::slice::from_raw_parts_mut(ptr, self.elems)
}
}
pub fn freeze(self) -> io::Result<Bytes> {
if self.mmap.len() > 0 {
self.mmap.flush()?;
}
let len_bytes = self.elems * core::mem::size_of::<T>();
let offset = self.offset;
let map = self.mmap.make_read_only()?;
Ok(Bytes::from_source(map).slice(offset..offset + len_bytes))
}
pub fn handle(&self) -> SectionHandle<T> {
SectionHandle {
offset: self.start,
len: self.elems * core::mem::size_of::<T>(),
_type: PhantomData,
}
}
}
impl<'arena, T> core::ops::Deref for Section<'arena, T>
where
T: FromBytes + Immutable,
{
type Target = [T];
fn deref(&self) -> &Self::Target {
unsafe {
let ptr = self.mmap.as_ptr().add(self.offset) as *const T;
core::slice::from_raw_parts(ptr, self.elems)
}
}
}
impl<'arena, T> core::ops::DerefMut for Section<'arena, T>
where
T: FromBytes + Immutable,
{
fn deref_mut(&mut self) -> &mut [T] {
unsafe {
let ptr = self.mmap.as_mut_ptr().add(self.offset) as *mut T;
core::slice::from_raw_parts_mut(ptr, self.elems)
}
}
}
impl<'arena, T> AsRef<[T]> for Section<'arena, T>
where
T: FromBytes + Immutable,
{
fn as_ref(&self) -> &[T] {
self
}
}
impl<'arena, T> AsMut<[T]> for Section<'arena, T>
where
T: FromBytes + Immutable,
{
fn as_mut(&mut self) -> &mut [T] {
self
}
}
#[derive(Clone, Copy, Debug)]
#[repr(C)]
#[cfg_attr(
feature = "zerocopy",
derive(zerocopy::FromBytes, zerocopy::KnownLayout, zerocopy::Immutable,)
)]
pub struct SectionHandle<T> {
pub offset: usize,
pub len: usize,
_type: PhantomData<T>,
}
impl<T> SectionHandle<T>
where
T: FromBytes + Immutable,
{
pub fn bytes<'a>(&self, area: &'a Bytes) -> Bytes {
area.slice(self.offset..self.offset + self.len)
}
pub fn view(&self, area: &Bytes) -> Result<View<[T]>, ViewError> {
self.bytes(area).view::<[T]>()
}
}