Added `{As,From,Into}RawFd` impls for ManagedFD and MemoryFile.

file: Made fd-wrappers backed by `NonNegativeI32`, a type that is backed by `NonZeroU32`, which takes only positive (including 0) i32 integers and allows for non-null optimisation in enums.

Fortune for mapped-file's current commit: Small blessing − 小吉
master
Avril 2 years ago
parent de56f67046
commit c1f1d00af2
Signed by: flanchan
GPG Key ID: 284488987C31F630

@ -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)*)))

@ -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;

@ -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<ManagedFD> 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`

@ -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<MemoryFile> for ManagedFD
{
#[inline]
fn from(from: MemoryFile) -> Self
{
from.0
}
}
impl From<MemoryFile> for std::fs::File
{
#[inline]
fn from(from: MemoryFile) -> Self
{
from.0.into()
}
}
//TODO: implement `memfd` from `utf8encode`.

@ -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<F: FnOnce() -> 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<Self>
{
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<i32> for NonNegativeI32
{
#[inline]
fn partial_cmp(&self, other: &i32) -> Option<std::cmp::Ordering> {
self.get().partial_cmp(other)
}
}
impl PartialEq<i32> for NonNegativeI32
{
#[inline]
fn eq(&self, other: &i32) -> bool
{
self.get().eq(other)
}
}
impl PartialEq<NonNegativeI32> for i32
{
#[inline]
fn eq(&self, other: &NonNegativeI32) -> bool
{
self.eq(&other.get())
}
}
impl PartialOrd<NonNegativeI32> for i32
{
#[inline]
fn partial_cmp(&self, other: &NonNegativeI32) -> Option<std::cmp::Ordering> {
self.partial_cmp(&other.get())
}
}
impl From<NonNegativeI32> for i32
{
#[inline]
fn from(from: NonNegativeI32) -> Self
{
from.get()
}
}
impl From<i32> for NonNegativeI32
{
// Convenience `From` impl (panicking)
#[inline(always)]
fn from(from: i32) -> Self
{
Self::new_or_panic(from)
}
}

@ -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<RawFd> for UnmanagedFD
fn from(from: RawFd) -> Self
{
debug_assert!(from >= 0, "Invalid file descriptor");
Self(from)
Self(from.into())
}
}
impl From<UnmanagedFD> 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()
}
}

@ -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<T: AsRawFd> MappedFile<T> {
/// 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<T: AsRawFd> MappedFile<T> {
})
}
/// 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<T>` 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<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)
})
}
/// 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<T: AsRawFd> MappedFile<T> {
/// * `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<B: buffer::TwoBufferProvider<T>>(file: T, len: usize, flags: impl flags::MapFlags) -> Result<(MappedFile<B>, MappedFile<B>), TryNewError<T>>
/// `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<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)
}

Loading…
Cancel
Save