blob: 86315da08a2acd4db226e41c3cb226ad43223f0f [file] [log] [blame]
//! Typed views using temporary objects.
//!
//! This module defines the return types for [`AsFilelike::as_filelike_view`]
//! and [`AsSocketlike::as_socketlike_view`].
//!
//! [`AsSocketlike::as_socketlike_view`]: crate::AsSocketlike::as_socketlike_view
use crate::raw::{
AsRawFilelike, AsRawSocketlike, FromRawFilelike, FromRawSocketlike, IntoRawFilelike,
IntoRawSocketlike, RawFilelike, RawSocketlike,
};
#[cfg(any(unix, target_os = "wasi", target_os = "hermit"))]
use crate::OwnedFd;
use crate::{
AsFilelike, AsSocketlike, FromFilelike, FromSocketlike, IntoFilelike, IntoSocketlike,
OwnedFilelike, OwnedSocketlike,
};
#[cfg(windows)]
use crate::{OwnedHandle, OwnedSocket};
use std::fmt;
use std::marker::PhantomData;
use std::mem::ManuallyDrop;
use std::ops::Deref;
/// Declare that a type is safe to use in a [`FilelikeView`].
///
/// # Safety
///
/// Types implementing this trait declare that if they are constructed with
/// [`FromFilelike`] and consumed with [`IntoFilelike`], their `IntoFilelike`
/// will return the same `OwnedFd` value that was passed to their
/// `FromFilelike`.
pub unsafe trait FilelikeViewType: FromFilelike + IntoFilelike {}
/// Declare that a type is safe to use in a [`SocketlikeView`].
///
/// # Safety
///
/// Types implementing this trait declare that if they are constructed with
/// [`FromSocketlike`] and consumed with [`IntoSocketlike`], their
/// `IntoSocketlike` will return the same `OwnedFd` value that was passed to
/// their `FromSocketlike`.
pub unsafe trait SocketlikeViewType: FromSocketlike + IntoSocketlike {}
/// A non-owning view of a resource which dereferences to a `&Target` or
/// `&mut Target`. These are returned by [`AsFilelike::as_filelike_view`].
pub struct FilelikeView<'filelike, Target: FilelikeViewType> {
/// The value to dereference to. This is a `ManuallyDrop` so that we can
/// consume it in our `Drop` impl.
target: ManuallyDrop<Target>,
/// `FilelikeViewType` implementors guarantee that their `Into<OwnedFd>`
/// returns the same fd as their `From<OwnedFd>` gave them. This field
/// allows us to verify this.
#[cfg(debug_assertions)]
orig: RawFilelike,
/// This field exists because we don't otherwise explicitly use
/// `'filelike`.
_phantom: PhantomData<&'filelike OwnedFilelike>,
}
/// A non-owning view of a resource which dereferences to a `&Target` or
/// `&mut Target`. These are returned by [`AsSocketlike::as_socketlike_view`].
pub struct SocketlikeView<'socketlike, Target: SocketlikeViewType> {
/// The value to dereference to. This is a `ManuallyDrop` so that we can
/// consume it in our `Drop` impl.
target: ManuallyDrop<Target>,
/// `SocketlikeViewType` implementors guarantee that their `Into<OwnedFd>`
/// returns the same fd as their `From<OwnedFd>` gave them. This field
/// allows us to verify this.
#[cfg(debug_assertions)]
orig: RawSocketlike,
/// This field exists because we don't otherwise explicitly use
/// `'socketlike`.
_phantom: PhantomData<&'socketlike OwnedSocketlike>,
}
impl<Target: FilelikeViewType> FilelikeView<'_, Target> {
/// Construct a temporary `Target` and wrap it in a `FilelikeView` object.
#[inline]
pub(crate) fn new<T: AsFilelike>(filelike: &T) -> Self {
// Safety: The returned `FilelikeView` is scoped to the lifetime of
// `filelike`, which we've borrowed here, so the view won't outlive
// the object it's borrowed from.
unsafe { Self::view_raw(filelike.as_filelike().as_raw_filelike()) }
}
/// Construct a temporary `Target` from raw and wrap it in a `FilelikeView`
/// object.
///
/// # Safety
///
/// `raw` must be a valid raw filelike referencing a resource that outlives
/// the resulting view.
#[inline]
pub unsafe fn view_raw(raw: RawFilelike) -> Self {
let owned = OwnedFilelike::from_raw_filelike(raw);
Self {
target: ManuallyDrop::new(Target::from_filelike(owned)),
#[cfg(debug_assertions)]
orig: raw,
_phantom: PhantomData,
}
}
}
impl<Target: SocketlikeViewType> SocketlikeView<'_, Target> {
/// Construct a temporary `Target` and wrap it in a `SocketlikeView`
/// object.
#[inline]
pub(crate) fn new<T: AsSocketlike>(socketlike: &T) -> Self {
// Safety: The returned `SocketlikeView` is scoped to the lifetime of
// `socketlike`, which we've borrowed here, so the view won't outlive
// the object it's borrowed from.
unsafe { Self::view_raw(socketlike.as_socketlike().as_raw_socketlike()) }
}
/// Construct a temporary `Target` from raw and wrap it in a
/// `SocketlikeView` object.
///
/// # Safety
///
/// `raw` must be a valid raw socketlike referencing a resource that
/// outlives the resulting view.
#[inline]
pub unsafe fn view_raw(raw: RawSocketlike) -> Self {
let owned = OwnedSocketlike::from_raw_socketlike(raw);
Self {
target: ManuallyDrop::new(Target::from_socketlike(owned)),
#[cfg(debug_assertions)]
orig: raw,
_phantom: PhantomData,
}
}
}
impl<Target: FilelikeViewType> Deref for FilelikeView<'_, Target> {
type Target = Target;
#[inline]
fn deref(&self) -> &Self::Target {
&self.target
}
}
impl<Target: SocketlikeViewType> Deref for SocketlikeView<'_, Target> {
type Target = Target;
#[inline]
fn deref(&self) -> &Self::Target {
&self.target
}
}
impl<Target: FilelikeViewType> Drop for FilelikeView<'_, Target> {
#[inline]
fn drop(&mut self) {
// Use `Into*` to consume `self.target` without freeing its resource.
//
// Safety: Using `ManuallyDrop::take` requires us to ensure that
// `self.target` is not used again. We don't use it again here, and
// this is the `drop` function, so we know it's not used afterward.
let _raw = unsafe { ManuallyDrop::take(&mut self.target) }
.into_filelike()
.into_raw_filelike();
#[cfg(debug_assertions)]
debug_assert_eq!(self.orig, _raw);
}
}
impl<Target: SocketlikeViewType> Drop for SocketlikeView<'_, Target> {
#[inline]
fn drop(&mut self) {
// Use `Into*` to consume `self.target` without freeing its resource.
//
// Safety: Using `ManuallyDrop::take` requires us to ensure that
// `self.target` is not used again. We don't use it again here, and
// this is the `drop` function, so we know it's not used afterward.
let _raw = unsafe { ManuallyDrop::take(&mut self.target) }
.into_socketlike()
.into_raw_socketlike();
#[cfg(debug_assertions)]
debug_assert_eq!(self.orig, _raw);
}
}
impl<Target: FilelikeViewType> fmt::Debug for FilelikeView<'_, Target> {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("FilelikeView")
.field("target", &*self)
.finish()
}
}
impl<Target: SocketlikeViewType> fmt::Debug for SocketlikeView<'_, Target> {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SocketlikeView")
.field("target", &*self)
.finish()
}
}
#[cfg(any(unix, target_os = "wasi", target_os = "hermit"))]
unsafe impl FilelikeViewType for OwnedFd {}
#[cfg(windows)]
unsafe impl FilelikeViewType for OwnedHandle {}
#[cfg(windows)]
unsafe impl SocketlikeViewType for OwnedSocket {}
unsafe impl FilelikeViewType for std::fs::File {}
unsafe impl SocketlikeViewType for std::net::TcpStream {}
unsafe impl SocketlikeViewType for std::net::TcpListener {}
unsafe impl SocketlikeViewType for std::net::UdpSocket {}
#[cfg(unix)]
unsafe impl SocketlikeViewType for std::os::unix::net::UnixStream {}
#[cfg(unix)]
unsafe impl SocketlikeViewType for std::os::unix::net::UnixListener {}
#[cfg(unix)]
unsafe impl SocketlikeViewType for std::os::unix::net::UnixDatagram {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "os_pipe")]
unsafe impl FilelikeViewType for os_pipe::PipeWriter {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "os_pipe")]
unsafe impl FilelikeViewType for os_pipe::PipeReader {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "socket2")]
unsafe impl SocketlikeViewType for socket2::Socket {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::net::TcpStream {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::net::TcpListener {}
#[cfg(not(any(target_os = "wasi", target_os = "hermit")))]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::net::UdpSocket {}
#[cfg(unix)]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixStream {}
#[cfg(unix)]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixListener {}
#[cfg(unix)]
#[cfg(feature = "async_std")]
unsafe impl SocketlikeViewType for async_std::os::unix::net::UnixDatagram {}
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::TcpStream {}
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::TcpListener {}
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::UdpSocket {}
#[cfg(unix)]
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::UnixDatagram {}
#[cfg(unix)]
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::UnixListener {}
#[cfg(unix)]
#[cfg(feature = "mio")]
unsafe impl SocketlikeViewType for mio::net::UnixStream {}
#[cfg(unix)]
#[cfg(feature = "mio")]
unsafe impl FilelikeViewType for mio::unix::pipe::Receiver {}
#[cfg(unix)]
#[cfg(feature = "mio")]
unsafe impl FilelikeViewType for mio::unix::pipe::Sender {}