#[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; 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); impl ops::Drop for MappedSlice { #[inline] fn drop(&mut self) { unsafe { libc::munmap(self.0.as_mut_ptr() as *mut _, self.0.len()); } } } /// A memory mapping over file `T`. #[derive(Debug, PartialEq, Eq, Hash)] pub struct MappedFile { file: T, map: MappedSlice, } impl MappedFile { /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`. /// # Mapping flags /// The trait `MapFlags` is used to allow user-defined configurations of `mmap()`, but the `Flags` enum should usually be used for this, or `()`, which behaves the same as `Flags::default()`. /// /// # Returns /// If `mmap()` fails, then the current `errno` is returned alongside the `file` that was passed in, otherwise, a new mapping is /// constructed over `file`, and that is returned. /// /// # Panics /// If `mmap()` succeeds, but returns an invalid address (e.g. 0) pub fn try_new(file: T, len: usize, perm: Perm, flags: impl flags::MapFlags) -> Result> { #[inline(never)] #[cold] fn _panic_invalid_address() -> ! { panic!("Invalid/unsupported address returned from mmap()") } 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) }) } /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and /// mapping flags provided by `flags`. /// /// # Returns /// If `mmap()` fails, then the current `errno` set by `mmap()` is returned, otherwise, a new mapping is /// constructed over `file`, and that is returned. /// If `mmap()` fails, `file` is dropped. To retain `file`, use `try_new()`. /// /// # Panics /// If `mmap()` succeeds, but returns an invalid address (e.g. 0) #[inline] pub fn new(file: T, len: usize, perm: Perm, flags: impl MapFlags) -> io::Result { Self::try_new(file, len, perm, flags).map_err(Into::into) } /// Sync the mapped memory to the backing file store via `msync()`. /// /// If this is a private mapping, or is mapped over a private file descriptor that does not refer to on-disk persistent storage, syncing the data is usually pointless. /// /// # Returns /// If `msync()` fails. 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()) } } /// Replace the mapped file object with another that aliases the same file descriptor. /// /// # Warning /// * The old file object is *not* dropped to prevent the file descriptor being closed. (see `replace_inner_unchecked()`). /// If `T` contains other resources, this can cause a memory leak. /// /// # Panics /// If `other`'s `AsRawFd` impl *does not* alias the already contained `T`'s. pub fn replace_inner(self, other: U) -> MappedFile { 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 } } /// Unmap the memory contained in `T` and return it. /// Before the memory is unmapped, it is `msync()`'d according to `flush`. /// /// # Panics /// If `msync()` fails. #[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 MappedFile { #[inline(always)] fn raw_parts(&self) -> (*mut u8, usize) { (self.map.0.mem.as_ptr(), self.map.0.len()) } /// Set advise according to `adv`, and optionally advise the kernel on if the memory will be needed or not. pub fn advise(&mut self, adv: Advice, needed: Option) -> 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()) } } /// With advice, used as a builder-pattern alternative for `advise()`. /// /// # Returns /// If `madvise()` fails, then the `io::Error` along with the previous instance is returned. #[inline(always)] pub fn try_with_advice(mut self, adv: Advice, needed: Option) -> Result> { match self.advise(adv, needed) { Ok(_) => Ok(self), Err(error) => Err(TryNewError { error: Box::new(error), value: self, }) } } /// With advice, used as a builder-pattern alternative for `advise()`. /// /// # Returns /// If `madvise()` fails, then the mapping is dropped and the error is returned. To keep the previous instance if the call failes, use `try_with_advice()`. #[inline] pub fn with_advice(self, adv: Advice, needed: Option) -> io::Result { self.try_with_advice(adv, needed).map_err(Into::into) } /// Replace the inner file with another without checking static or dynamic bounding. /// This function is extremely unsafe if the following conditions are not met in entirity. /// /// # Safety /// * `U` and `T` **must** have an `AsRawFd::as_raw_fd()` impl that returns the same `RawFd` value unconditionally. /// * The returned `T` in the tuple **must not** attempt close the file descriptor while the returned `MappedFile` in the tuple is alive. /// * The returned values **should not** *both* attempt to close the file descriptor when dropped. To prevent the `MappedFile` from attempting to close the file descriptor, use `MappedFile::into_inner()` and ensure `U` does not close the file descriptor while `T` is alive. Alternatively, use a mechanism of `T` to prevent it from closing the file descriptor while `U` is alive. #[inline(always)] pub unsafe fn replace_inner_unchecked(self, other: U) -> (MappedFile, T) { let MappedFile{ file, map } = self; (MappedFile { file: other, map }, file) } /// Unmap the memory contained in `T` and return it. /// /// # Warning /// If the map is shared, or refers to a persistent file on disk, you should call `flush()` /// first or use `into_inner_synced()` #[inline] pub fn into_inner(self) -> T { drop(self.map); self.file } /// The size of the mapped memory #[inline] pub fn len(&self) -> usize { self.map.0.len() } /// Get a slice of the mapped memory #[inline] pub fn as_slice(&self) -> &[u8] { &self.map.0[..] } /// Get a mutable slice of the mapped memory #[inline] pub fn as_slice_mut(&mut self) -> &mut [u8] { &mut self.map.0[..] } /// Get a raw slice of the mapped memory #[inline] pub fn as_raw_slice(&self) -> *const [u8] { self.map.0.as_raw_slice() } /// Get a raw mutable slice of the mapped memory #[inline] pub fn as_raw_slice_mut(&mut self) -> *mut [u8] { self.map.0.as_raw_slice_mut() } /// Checks if the mapping dangles (i.e. `len() == 0`.) #[inline] pub fn is_empty(&self) -> bool { self.map.0.is_empty() } } /// Error returned when mapping operation fails. /// /// Also returns the value passed in. pub struct TryNewError { error: Box, value: T } impl error::Error for TryNewError { #[inline] fn source(&self) -> Option<&(dyn error::Error + 'static)> { Some(self.error.as_ref()) } } impl fmt::Display for TryNewError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "error in mapping of type {}", std::any::type_name::()) } } impl fmt::Debug for TryNewError { #[inline] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("TryNewError") .field("error", &self.error) .finish_non_exhaustive() } } impl TryNewError { /// A reference to the value #[inline] pub fn value(&self) -> &T { &self.value } /// A mutable reference to the value #[inline] pub fn value_mut(&mut self) -> &mut T { &mut self.value } /// A reference to the IO error #[inline] pub fn error(&self) -> &io::Error { &self.error } /// Consume a boxed instance and return the boxed IO error. #[inline] pub fn into_error_box(self: Box) -> Box { self.error } } impl TryNewError { #[inline] fn wrap_last_error(value: T) -> Self { Self { error: Box::new(io::Error::last_os_error()), value, } } /// Consume into the contained value #[inline] pub fn into_inner(self) -> T { self.value } /// Consume into the IO error #[inline] pub fn into_error(self) -> io::Error { *self.error } /// Consume into the value and the error. #[inline] pub fn into_parts(self) -> (T, io::Error) { (self.value, *self.error) } } impl From>> for io::Error { #[inline] fn from(from: Box>) -> Self { *from.error } } impl From> for io::Error { #[inline] fn from(from: TryNewError) -> Self { from.into_error() } } impl Borrow for MappedFile { #[inline] fn borrow(&self) -> &T { &self.file } } impl Borrow<[u8]> for MappedFile { #[inline] fn borrow(&self) -> &[u8] { self.as_slice() } } impl BorrowMut<[u8]> for MappedFile { #[inline] fn borrow_mut(&mut self) -> &mut [u8] { self.as_slice_mut() } } impl ops::Deref for MappedFile { type Target= [u8]; #[inline] fn deref(&self) -> &Self::Target { self.as_slice() } } impl ops::DerefMut for MappedFile { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { self.as_slice_mut() } } /// Used for anonymous mappings with `MappedFile`. /// /// # Safety /// The `AsRawFd` impl of this structure always returns `-1`. It should only be used with `MappedFile`, as this is an invlalid file descriptor in all other contexts. #[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 } } //TODO: Continue copying from `utf8encode` at the //TODO (cont.) line