#[macro_use] extern crate lazy_static;
use libc::{
mmap,
MAP_FAILED,
};
use std::{
os::unix::prelude::*,
ops,
mem,
ptr::{
self,
NonNull,
},
io,
fmt, error,
borrow::{
Borrow, BorrowMut,
}
};
mod ffi;
use ffi::c_try;
pub mod hugetlb;
pub mod file;
pub mod ring; use ring::buffer;
mod ext; use ext::*;
use hugetlb::{
HugePage,
MapHugeFlag,
};
mod uniq;
use uniq::UniqueSlice;
mod flags;
pub use flags::*;
pub mod err;
use err::{
os_error,
opaque,
};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)]
struct MappedSlice(UniqueSlice<u8>);
unsafe impl Send for MappedSlice{}
unsafe impl Sync for MappedSlice{}
impl ops::Drop for MappedSlice
{
#[inline]
fn drop(&mut self)
{
unsafe {
libc::munmap(self.0.as_mut_ptr() as *mut _, self.0.len());
}
}
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub struct MappedFile<T>
{
file: T,
map: MappedSlice,
}
#[inline(never)]
#[cold]
fn _panic_invalid_address() -> !
{
panic!("Invalid/unsupported address returned from mmap()")
}
pub fn get_page_size() -> usize
{
use libc::c_int;
extern "C" {
fn getpagesize() -> c_int;
}
lazy_static! {
static ref PAGESZ: c_int = unsafe {
getpagesize()
};
}
let v: c_int = *PAGESZ;
v as usize
}
impl<T> MappedFile<T> {
#[inline]
pub fn inner(&self) -> &T
{
&self.file
}
#[inline]
pub fn inner_mut(&mut self) -> &mut T
{
&mut self.file
}
#[inline]
pub fn into_inner(self) -> T
{
drop(self.map);
self.file
}
}
impl<T: AsRawFd> MappedFile<T> {
pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result<Self, TryNewError<T>>
{
const NULL: *mut libc::c_void = ptr::null_mut();
let fd = file.as_raw_fd();
let slice = match unsafe {
mmap(ptr::null_mut(), len, perm.get_prot(), flags.get_mmap_flags(), fd, 0)
} {
MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
NULL => _panic_invalid_address(),
ptr => unsafe {
UniqueSlice {
mem: NonNull::new_unchecked(ptr as *mut u8),
end: match NonNull::new((ptr as *mut u8).add(len)) {
Some(n) => n,
_ => _panic_invalid_address(),
},
}
},
};
Ok(Self {
file,
map: MappedSlice(slice)
})
}
pub fn shared<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, flags: impl flags::MapFlags) -> (MappedFile<B>, MappedFile<B>)
{
#[cold]
#[inline(never)]
fn _panic_failed_with(error: Box<io::Error>) -> !
{
Err(error).expect("Failed to create shared mapping")
}
Self::try_shared(file, len, flags).unwrap_or_else(|e| {
_panic_failed_with(e.error)
})
}
pub fn try_shared<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, flags: impl flags::MapFlags) -> Result<(MappedFile<B>, MappedFile<B>), TryNewError<T>>
{
Self::try_new_buffer_raw::<B>(file, len, None, false, flags)
}
#[inline]
pub(crate) fn try_new_buffer_raw<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, rings: impl Into<Option<std::num::NonZeroUsize>>, allow_unsafe_writes: bool, flags: impl flags::MapFlags) -> Result<(MappedFile<B>, MappedFile<B>), TryNewError<T>>
{
const NULL: *mut libc::c_void = ptr::null_mut();
macro_rules! try_map_or {
($($tt:tt)*) => {
match unsafe {
mmap($($tt)*)
} {
MAP_FAILED => Err(io::Error::last_os_error()),
NULL => _panic_invalid_address(),
ptr => Ok(unsafe {
UniqueSlice {
mem: NonNull::new_unchecked(ptr as *mut u8),
end: match NonNull::new((ptr as *mut u8).add(len)) {
Some(n) => n,
_ => _panic_invalid_address(),
}
}
})
}.map(MappedSlice)
};
}
macro_rules! try_map {
($($tt:tt)*) => {
MappedSlice(match unsafe {
mmap($($tt)*)
} {
MAP_FAILED => return Err(TryNewError::wrap_last_error(file)),
NULL => _panic_invalid_address(),
ptr => unsafe {
UniqueSlice {
mem: NonNull::new_unchecked(ptr as *mut u8),
end: match NonNull::new((ptr as *mut u8).add(len)) {
Some(n) => n,
_ => _panic_invalid_address(),
}
}
}
})
};
}
macro_rules! unwrap {
($err:expr) => {
match $err {
Ok(v) => v,
Err(e) => return Err(TryNewError::wrap((e, file))),
}
};
}
let (prot_w, prot_r) = if allow_unsafe_writes {
let p = Perm::ReadWrite.get_prot();
(p, p)
} else {
(Perm::Writeonly.get_prot(), Perm::Readonly.get_prot())
};
let (tx, rx) = match rings.into() {
None => {
let flags = flags.get_mmap_flags();
let mut root = try_map!(NULL, len * 2, libc::PROT_NONE, (flags & !libc::MAP_SHARED) | libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0);
let rawfd = file.as_raw_fd();
let rm = try_map!(root.0.as_mut_ptr().add(len) as *mut _, len, prot_r, flags | libc::MAP_FIXED, rawfd, 0); let tm = try_map!(root.0.as_mut_ptr() as *mut _, len, prot_w, flags | libc::MAP_FIXED, rawfd, 0);
let tf = B::from_value(file);
let rf = B::from_wrapper(tf.as_wrapper());
(MappedFile {
file: tf,
map: tm
}, MappedFile {
file: rf,
map: rm,
})
},
Some(pages) => {
let full_len = unwrap!((len * 2)
.checked_mul(pages.get())
.ok_or_else(|| io::Error::new(io::ErrorKind::OutOfMemory,
format!("Could not map {} pages of size {len}. Value would overflow", pages.get()))));
let flags = flags.get_mmap_flags();
let mut root = try_map!(NULL, full_len, libc::PROT_NONE, libc::MAP_PRIVATE | libc::MAP_ANONYMOUS, -1, 0);
let pivots = {
let rawfd = file.as_raw_fd();
let pivots: io::Result<Vec<_>> = std::iter::successors(unsafe { Some(root.0.as_mut_ptr().add(full_len - (len * 2))) }, |&x| unsafe { Some(x.sub(len * 2)) }) .take(pages.get())
.map(|base| {
let rm = try_map_or!(base.add(len) as *mut _, len, prot_r, flags | libc::MAP_FIXED,rawfd, 0 )?;
let tm = try_map_or!(base as *mut _, len, prot_w, flags | libc::MAP_FIXED, rawfd, 0)?;
Ok((tm, rm))
})
.collect();
unwrap!(pivots)
};
todo!("We can't carry `pivots` over to return from this function; the data is needed for unmapping the ring...");
todo!("The mapping we'd be using is `root`. But we need to unmap `pivots` in reverse order when the returned `MappedFile` is dropped...")
}
};
Ok((tx, rx))
}
#[inline]
pub fn new(file: T, len: usize, perm: Perm, flags: impl MapFlags) -> io::Result<Self>
{
Self::try_new(file, len, perm, flags).map_err(Into::into)
}
pub fn flush(&mut self, flush: Flush) -> io::Result<()>
{
use libc::msync;
match unsafe {
msync(self.map.0.as_mut_ptr() as *mut _, self.map.0.len(), flush.get_ms())
} {
0 => Ok(()),
_ => Err(io::Error::last_os_error())
}
}
pub fn replace_inner<U: AsRawFd>(self, other: U) -> MappedFile<U>
{
assert_eq!(self.file.as_raw_fd(), other.as_raw_fd(), "File descriptors must alias");
unsafe {
let (this, file) = self.replace_inner_unchecked(other);
mem::forget(file);
this
}
}
#[inline]
pub fn into_inner_synced(mut self, flush: Flush) -> T
{
self.flush(flush).expect("Failed to sync data");
drop(self.map);
self.file
}
}
impl<T> MappedFile<T> {
#[inline(always)]
fn raw_parts(&self) -> (*mut u8, usize)
{
(self.map.0.mem.as_ptr(), self.map.0.len())
}
pub fn advise(&mut self, adv: Advice, needed: Option<bool>) -> io::Result<()>
{
use libc::{
madvise,
MADV_WILLNEED,
MADV_DONTNEED
};
let (addr, len) = self.raw_parts();
match unsafe { madvise(addr as *mut _, len, adv.get_madv() | needed.map(|n| n.then(|| MADV_WILLNEED).unwrap_or(MADV_DONTNEED)).unwrap_or(0)) } {
0 => Ok(()),
_ => Err(io::Error::last_os_error())
}
}
#[inline(always)]
pub fn try_with_advice(mut self, adv: Advice, needed: Option<bool>) -> Result<Self, TryNewError<Self>>
{
match self.advise(adv, needed) {
Ok(_) => Ok(self),
Err(error) => Err(TryNewError {
error: Box::new(error),
value: self,
})
}
}
#[inline]
pub fn with_advice(self, adv: Advice, needed: Option<bool>) -> io::Result<Self>
{
self.try_with_advice(adv, needed).map_err(Into::into)
}
#[inline(always)]
pub unsafe fn replace_inner_unchecked<U>(self, other: U) -> (MappedFile<U>, T)
{
let MappedFile{ file, map } = self;
(MappedFile {
file: other,
map
}, file)
}
#[inline]
pub fn len(&self) -> usize
{
self.map.0.len()
}
#[inline]
pub fn as_slice(&self) -> &[u8]
{
&self.map.0[..]
}
#[inline]
pub fn as_slice_mut(&mut self) -> &mut [u8]
{
&mut self.map.0[..]
}
#[inline]
pub fn as_raw_slice(&self) -> *const [u8]
{
self.map.0.as_raw_slice()
}
#[inline]
pub fn as_raw_slice_mut(&mut self) -> *mut [u8]
{
self.map.0.as_raw_slice_mut()
}
#[inline]
pub fn is_empty(&self) -> bool
{
self.map.0.is_empty()
}
}
pub struct TryNewError<T: ?Sized>
{
error: Box<io::Error>,
value: T
}
impl<T:?Sized> error::Error for TryNewError<T>
{
#[inline]
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
Some(self.error.as_ref())
}
}
impl<T:?Sized> fmt::Display for TryNewError<T>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
write!(f, "error in mapping of type {}", std::any::type_name::<T>())
}
}
impl<T:?Sized> fmt::Debug for TryNewError<T>
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
f.debug_struct("TryNewError")
.field("error", &self.error)
.finish_non_exhaustive()
}
}
impl<T: ?Sized> TryNewError<T>
{
#[inline]
pub fn value(&self) -> &T
{
&self.value
}
#[inline]
pub fn value_mut(&mut self) -> &mut T
{
&mut self.value
}
#[inline]
pub fn error(&self) -> &io::Error
{
&self.error
}
#[inline]
pub fn into_error_box(self: Box<Self>) -> Box<io::Error>
{
self.error
}
}
impl<T> TryNewError<T>
{
#[inline]
fn wrap_last_error(value: T) -> Self
{
Self {
error: Box::new(io::Error::last_os_error()),
value,
}
}
#[inline]
fn wrap((error, value): (impl Into<io::Error>, T)) -> Self
{
Self {
error: Box::new(error.into()),
value
}
}
#[inline]
pub fn into_inner(self) -> T
{
self.value
}
#[inline]
pub fn into_error(self) -> io::Error
{
*self.error
}
#[inline]
pub fn into_parts(self) -> (T, io::Error)
{
(self.value, *self.error)
}
}
impl<T: ?Sized> From<Box<TryNewError<T>>> for io::Error
{
#[inline]
fn from(from: Box<TryNewError<T>>) -> Self
{
*from.error
}
}
impl<T> From<TryNewError<T>> for io::Error
{
#[inline]
fn from(from: TryNewError<T>) -> Self
{
from.into_error()
}
}
impl<T: AsRawFd> Borrow<T> for MappedFile<T>
{
#[inline]
fn borrow(&self) -> &T
{
&self.file
}
}
impl<T> Borrow<[u8]> for MappedFile<T>
{
#[inline]
fn borrow(&self) -> &[u8]
{
self.as_slice()
}
}
impl<T> BorrowMut<[u8]> for MappedFile<T>
{
#[inline]
fn borrow_mut(&mut self) -> &mut [u8]
{
self.as_slice_mut()
}
}
impl<T> AsRef<[u8]> for MappedFile<T>
{
#[inline]
fn as_ref(&self) -> &[u8]
{
self.as_slice()
}
}
impl<T> AsMut<[u8]> for MappedFile<T>
{
#[inline]
fn as_mut(&mut self) -> &mut [u8]
{
self.as_slice_mut()
}
}
impl<T> ops::Deref for MappedFile<T>
{
type Target= [u8];
#[inline]
fn deref(&self) -> &Self::Target
{
self.as_slice()
}
}
impl<T> ops::DerefMut for MappedFile<T>
{
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target
{
self.as_slice_mut()
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
pub struct Anonymous;
impl AsRawFd for Anonymous
{
#[inline(always)]
fn as_raw_fd(&self) -> RawFd {
-1
}
}