use std::ops::{Bound, Index, IndexMut, RangeBounds};
use std::slice::SliceIndex;
use std::sync::Arc;
use bitflags::bitflags;
#[cfg_attr(target_os = "linux", path = "impl/linux.rs")]
#[cfg_attr(target_os = "windows", path = "impl/win.rs")]
mod r#impl;
use r#impl::{OwnedFileDescriptor, RawFileDescriptor};
pub type ArcView = View<Arc<Snapshot>>;
pub type CowView<'a> = View<&'a Snapshot>;
pub type MutView<'a> = View<&'a mut Snapshot>;
#[derive(Debug)]
pub struct Snapshot {
file: OwnedFileDescriptor,
size: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum ViewMode {
Cow,
Mutable,
}
#[derive(Debug)]
pub struct View<S> {
fd: RawFileDescriptor,
ptr: *mut u8,
size: usize,
mode: ViewMode,
_snapshot: S,
}
unsafe impl<S> Send for View<S> {}
unsafe impl<S> Sync for View<S> {}
impl Snapshot {
pub fn from_file(file: std::fs::File) -> std::io::Result<Self> {
Self::from_file_impl(file)
}
pub fn zeroed(size: usize) -> std::io::Result<Self> {
Self::zeroed_impl(size)
}
pub fn from_slice(buf: &[u8]) -> std::io::Result<Self> {
let mut this = Self::zeroed(buf.len())?;
this.view_mut()?.as_mut_slice()[0..buf.len()].copy_from_slice(buf);
Ok(this)
}
pub fn try_clone(&self) -> std::io::Result<Self> {
Self::from_slice(self.view()?.as_slice())
}
}
impl Snapshot {
pub fn view(&self) -> std::io::Result<CowView> {
CowView::new(self, self.as_raw_fd(), self.size, ViewMode::Cow)
}
pub fn view_mut(&mut self) -> std::io::Result<MutView> {
MutView::new(self, self.as_raw_fd(), self.size, ViewMode::Mutable)
}
pub fn view_arc(self: &Arc<Self>) -> std::io::Result<ArcView> {
ArcView::new(self.clone(), self.as_raw_fd(), self.size, ViewMode::Cow)
}
}
impl<S> View<S> {
pub fn len(&self) -> usize {
self.size
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
pub fn as_slice(&self) -> &[u8] {
unsafe { std::slice::from_raw_parts(self.ptr, self.size) }
}
pub fn as_mut_slice(&mut self) -> &mut [u8] {
unsafe { std::slice::from_raw_parts_mut(self.ptr, self.size) }
}
pub fn as_ptr(&self) -> *const u8 {
self.ptr
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.ptr
}
pub fn take_snapshot(&self) -> std::io::Result<Snapshot> {
Snapshot::from_slice(self.as_slice())
}
#[allow(dead_code)] pub(crate) fn protect(
&mut self,
region: impl RangeBounds<usize>,
allow: Access,
) -> std::io::Result<()> {
let start = match region.start_bound() {
Bound::Included(&s) => s,
Bound::Excluded(&s) => s + 1,
Bound::Unbounded => 0,
};
let end = match region.end_bound() {
Bound::Included(&s) => s + 1,
Bound::Excluded(&s) => s,
Bound::Unbounded => self.size,
};
if end <= start || end > self.size {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Invalid range for memory protection",
));
}
if start != start.next_multiple_of(page_size::get())
|| end != end.next_multiple_of(page_size::get())
{
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"Memory protection range must be page-aligned",
));
}
self.protect_impl(start..end, allow)
}
pub fn restore(&mut self) -> std::io::Result<()> {
if self.mode == ViewMode::Mutable {
return Ok(());
}
self.restore_impl()
}
}
impl<I: SliceIndex<[u8]>, S> Index<I> for View<S> {
type Output = I::Output;
#[inline]
fn index(&self, index: I) -> &Self::Output {
Index::index(self.as_slice(), index)
}
}
impl<I: SliceIndex<[u8]>, S> IndexMut<I> for View<S> {
#[inline]
fn index_mut(&mut self, index: I) -> &mut Self::Output {
IndexMut::index_mut(self.as_mut_slice(), index)
}
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Access: u8 {
const NONE = 0x00;
const READ = 0x01;
const WRITE = 0x02;
const EXEC = 0x04;
}
}
pub fn page_size() -> usize {
page_size::get()
}
fn effective_size(size: usize) -> usize {
size.max(page_size::get())
}
#[cfg(test)]
mod tests;
#[cfg(doctest)]
mod readme;