diff --git a/src/ffi.rs b/src/ffi.rs index 071aad3..31915d6 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -11,6 +11,7 @@ macro_rules! c_try { panic!("C call failed (invalid return {}): {}", $invalid, args) } + #[allow(unused_unsafe)] let res = unsafe { $call }; if res == $invalid { _panic_bad_c_call(format_args!($fmt $(, $args)*)) @@ -26,6 +27,7 @@ macro_rules! c_try { { panic!("C call failed (invalid return {}): {}", invalid, args) } + #[allow(unused_unsafe)] let res = unsafe { $call }; if $func(res) { _panic_bad_c_call(res, format_args!($fmt $(, $args)*)); @@ -35,6 +37,7 @@ macro_rules! c_try { }; (? $call:expr => if $func:expr; $fmt:literal $(, $args:expr)*) => { { + #[allow(unused_unsafe)] let res = unsafe { $call }; if $func(res) { Err(FFIError::from_last_error(res, format_args!($fmt $(, $args)*))) @@ -45,6 +48,7 @@ macro_rules! c_try { }; (? $call:expr => $invalid:literal; $fmt:literal $(, $args:expr)*) => { { + #[allow(unused_unsafe)] let res = unsafe { $call }; if res == $invalid { Err(FFIError::from_last_error($invalid, format_args!($fmt $(, $args)*))) diff --git a/src/file.rs b/src/file.rs index a3d8c9a..0ff5363 100644 --- a/src/file.rs +++ b/src/file.rs @@ -8,6 +8,9 @@ pub const STDOUT_FILENO: RawFd = libc::STDOUT_FILENO; /// Raw file-descriptor for standard error pub const STDERR_FILENO: RawFd = libc::STDERR_FILENO; +mod raw; +use raw::*; + mod managed; mod unmanaged; diff --git a/src/file/managed.rs b/src/file/managed.rs index 3e9f3e7..0ebeb7a 100644 --- a/src/file/managed.rs +++ b/src/file/managed.rs @@ -13,14 +13,14 @@ use libc::{ #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] -pub struct ManagedFD(RawFd); +pub struct ManagedFD(UnmanagedFD); impl Clone for ManagedFD { fn clone(&self) -> Self { - Self(c_try!(dup(self.0) => if |x| x < 0; "dup(): failed to duplicate file descriptor {}", self.0)) + Self(unsafe { UnmanagedFD::new_unchecked( c_try!(dup(self.0.get()) => if |x| x < 0; "dup(): failed to duplicate file descriptor {}", self.0.get()) ) }) } fn clone_from(&mut self, source: &Self) { - c_try!(dup2(self.0, source.0) => -1; "dup2(): failed to set file descriptor {} to alias {}", self.0, source.0); + c_try!(dup2(self.0.get(), source.0.get()) => -1; "dup2(): failed to set file descriptor {} to alias {}", self.0.get(), source.0.get()); } } @@ -28,9 +28,47 @@ impl ops::Drop for ManagedFD { fn drop(&mut self) { unsafe { - close(self.0); + close(self.0.get()); } } } +impl AsRawFd for ManagedFD +{ + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.get() + } +} + +impl FromRawFd for ManagedFD +{ + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(UnmanagedFD::new_unchecked(fd)) + } +} + +impl IntoRawFd for ManagedFD +{ + #[inline] + fn into_raw_fd(self) -> RawFd { + let raw = self.0.get(); + std::mem::forget(self); + raw + } +} + +impl From for std::fs::File +{ + #[inline] + fn from(from: ManagedFD) -> Self + { + unsafe { + Self::from_raw_fd(from.into_raw_fd()) + } + } +} + + //TODO: implement the rest of ManagedFD from `memfd` module in `utf8encode` diff --git a/src/file/memory.rs b/src/file/memory.rs index 59016f1..8ab25df 100644 --- a/src/file/memory.rs +++ b/src/file/memory.rs @@ -3,9 +3,59 @@ //! This can be useful for temporary buffers where a file descriptor is required. //! Huge-pages can also be used for this memory. use super::*; +use libc::{ + memfd_create, + MFD_CLOEXEC, + MFD_HUGETLB, +}; +/// A physical-memory backed file #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[repr(transparent)] pub struct MemoryFile(ManagedFD); +//TODO: impl `MemoryFile` (memfd_create() fd wrapper) + +impl AsRawFd for MemoryFile +{ + #[inline] + fn as_raw_fd(&self) -> RawFd { + self.0.as_raw_fd() + } +} + +impl FromRawFd for MemoryFile +{ + #[inline] + unsafe fn from_raw_fd(fd: RawFd) -> Self { + Self(ManagedFD::from_raw_fd(fd)) + } +} + +impl IntoRawFd for MemoryFile +{ + #[inline] + fn into_raw_fd(self) -> RawFd { + self.0.into_raw_fd() + } +} + +impl From for ManagedFD +{ + #[inline] + fn from(from: MemoryFile) -> Self + { + from.0 + } +} + +impl From for std::fs::File +{ + #[inline] + fn from(from: MemoryFile) -> Self + { + from.0.into() + } +} + //TODO: implement `memfd` from `utf8encode`. diff --git a/src/file/raw.rs b/src/file/raw.rs new file mode 100644 index 0000000..3e0f32a --- /dev/null +++ b/src/file/raw.rs @@ -0,0 +1,119 @@ +//! Internals for `RawFd` +use super::*; +use std::num::NonZeroU32; + +/// Used for the base of valid file descriptors for non-null optimisation. +#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy, PartialOrd, Ord)] +#[repr(transparent)] +pub(super) struct NonNegativeI32(NonZeroU32); + +const RANGE_MASK: u32 = u32::MAX >> 1; +const NEG_FLAG: u32 = !RANGE_MASK; + +#[inline(always)] +const fn is_negative(val: i32) -> bool +{ + ((val as u32) & NEG_FLAG) != 0 +} + +impl NonNegativeI32 +{ + #[inline] + pub fn new_or_else Self>(raw: i32, f: F) -> Self + { + Self::new(raw).unwrap_or_else(f) + } + #[inline] + pub fn new_or(raw: i32, or: Self) -> Self + { + Self::new(raw).unwrap_or(or) + } + #[inline] + pub const fn new_or_panic(raw: i32) -> Self + { + #[inline(never)] + #[cold] + const fn _panic_negative() -> ! + { + panic!("Negative integer passed to asserting panic") + } + match Self::new(raw) { + Some(v) => v, + None => _panic_negative() + } + } + #[inline] + pub const fn new(raw: i32) -> Option + { + if is_negative(raw) { + None + } else { + Some(Self(unsafe {NonZeroU32::new_unchecked(raw as u32 | NEG_FLAG)})) + } + } + + #[inline] + pub const fn get(self) -> i32 + { + (self.0.get() & RANGE_MASK) as i32 + } + + #[inline] + pub const unsafe fn new_unchecked(raw: i32) -> Self + { + Self(NonZeroU32::new_unchecked(raw as u32 | NEG_FLAG)) + } +} + +impl PartialOrd for NonNegativeI32 +{ + #[inline] + fn partial_cmp(&self, other: &i32) -> Option { + self.get().partial_cmp(other) + } +} +impl PartialEq for NonNegativeI32 +{ + #[inline] + fn eq(&self, other: &i32) -> bool + { + self.get().eq(other) + } +} + +impl PartialEq for i32 +{ + #[inline] + fn eq(&self, other: &NonNegativeI32) -> bool + { + self.eq(&other.get()) + } +} + +impl PartialOrd for i32 +{ + #[inline] + fn partial_cmp(&self, other: &NonNegativeI32) -> Option { + self.partial_cmp(&other.get()) + } +} + +impl From for i32 +{ + #[inline] + fn from(from: NonNegativeI32) -> Self + { + from.get() + } +} + +impl From for NonNegativeI32 +{ + // Convenience `From` impl (panicking) + #[inline(always)] + fn from(from: i32) -> Self + { + Self::new_or_panic(from) + } +} + diff --git a/src/file/unmanaged.rs b/src/file/unmanaged.rs index ffa3a3f..c94ae92 100644 --- a/src/file/unmanaged.rs +++ b/src/file/unmanaged.rs @@ -4,13 +4,32 @@ use super::*; /// Represents a `RawFd` but does not provide any ownership of it. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct UnmanagedFD(RawFd); +#[repr(transparent)] +pub struct UnmanagedFD(NonNegativeI32); impl UnmanagedFD { #[inline] pub fn new(alias: &(impl AsRawFd + ?Sized)) -> Self { - Self(alias.as_raw_fd()) + Self(alias.as_raw_fd().into()) + } + + #[inline] + pub(super) const fn new_or_panic(raw: RawFd) -> Self + { + Self(NonNegativeI32::new_or_panic(raw)) + } + + #[inline] + pub const unsafe fn new_unchecked(raw: RawFd) -> Self + { + Self(NonNegativeI32::new_unchecked(raw)) + } + + #[inline] + pub const fn get(&self) -> RawFd + { + self.0.get() } } @@ -20,14 +39,25 @@ impl From for UnmanagedFD fn from(from: RawFd) -> Self { debug_assert!(from >= 0, "Invalid file descriptor"); - Self(from) + Self(from.into()) } } +impl From for RawFd +{ + #[inline] + fn from(from: UnmanagedFD) -> Self + { + from.get() + } +} + +// No impl for `IntoRawFd` because `UnmanagedFD` is not owning + impl FromRawFd for UnmanagedFD { unsafe fn from_raw_fd(fd: RawFd) -> Self { - Self(fd) + Self(fd.into()) } } @@ -36,7 +66,7 @@ impl AsRawFd for UnmanagedFD { #[inline(always)] fn as_raw_fd(&self) -> RawFd { - self.0 + self.0.get() } } diff --git a/src/lib.rs b/src/lib.rs index a5bf66a..28d2998 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -82,6 +82,22 @@ fn _panic_invalid_address() -> ! panic!("Invalid/unsupported address returned from mmap()") } +/// Get the current system page size +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 MappedFile { /// Map the file `file` to `len` bytes with memory protection as provided by `perm`, and mapping flags provided by `flags`. /// # Mapping flags @@ -119,6 +135,37 @@ impl MappedFile { }) } + + + /// Returns a dual mapping `(tx, rx)`, into the same file. + /// + /// This essentially creates s "sender" `tx`, and "receiver" `rx` mapping over the same data. + /// The sender is *write only*, and the receiver is *read only*. + /// + /// When **both** of the mappings have been uunmapped (when each `MappedFile` has been dropped,) the inner value `file` is then dropped. + /// + /// # Sharing modes + /// `B` is used for the counter over the file handle `T`. Currently it can be + /// * `buffer::Shared` - A `Send`able mapping, use this for concurrent processing. + /// * `buffer::Private` - A `!Send` mapping, use this for when both returned maps are only used on the same thread that this function was called from. + /// + /// # Note + /// `len` **must** be a multiple of the used page size (see `get_page_size()`) (or hugepage size, if `flags` is set to use one) for this to work. + /// + /// # Panics + /// If the initial mapping fails, the file descriptor cannot be mapped, `len` was not a multiple of the correct page size, or if the fixed mappings fail. (see `try_shared()`.) + pub fn shared>(file: T, len: usize, flags: impl flags::MapFlags) -> (MappedFile, MappedFile) + { + #[cold] + #[inline(never)] + fn _panic_failed_with(error: Box) -> ! + { + Err(error).expect("Failed to create shared mapping") + } + Self::try_shared(file, len, flags).unwrap_or_else(|e| { + _panic_failed_with(e.error) + }) + } /// Returns a dual mapping `(tx, rx)`, into the same file. /// /// This essentially creates s "sender" `tx`, and "receiver" `rx` mapping over the same data. @@ -132,8 +179,8 @@ impl MappedFile { /// * `buffer::Private` - A `!Send` mapping, use this for when both returned maps are only used on the same thread that this function was called from. /// /// # Note - /// `len` **must** be a multiple of the used page size (or hugepage size, if `flags` is set to use one) for this to work. - pub fn shared>(file: T, len: usize, flags: impl flags::MapFlags) -> Result<(MappedFile, MappedFile), TryNewError> + /// `len` **must** be a multiple of the used page size (see `get_page_size()`) (or hugepage size, if `flags` is set to use one) for this to work. + pub fn try_shared>(file: T, len: usize, flags: impl flags::MapFlags) -> Result<(MappedFile, MappedFile), TryNewError> { Self::try_new_buffer_raw::(file, len, None, false, flags) }