use std::fmt;
use std::marker::PhantomData;
use std::mem::forget;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(target_os = "wasi")]
use std::os::wasi::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::{
convert::TryFrom,
os::windows::io::{
AsRawHandle, AsRawSocket, FromRawHandle, FromRawSocket, IntoRawHandle, IntoRawSocket,
RawHandle, RawSocket,
},
};
#[cfg(all(windows, feature = "close"))]
use {
windows_sys::Win32::Foundation::{
CloseHandle, DuplicateHandle, SetHandleInformation, BOOL, DUPLICATE_HANDLE_OPTIONS,
DUPLICATE_SAME_ACCESS, HANDLE, HANDLE_FLAG_INHERIT, INVALID_HANDLE_VALUE,
},
windows_sys::Win32::Networking::WinSock::{
closesocket, WSADuplicateSocketW, WSAGetLastError, WSASocketW, INVALID_SOCKET, SOCKET,
SOCKET_ERROR, WSAEINVAL, WSAEPROTOTYPE, WSAPROTOCOL_INFOW, WSA_FLAG_NO_HANDLE_INHERIT,
WSA_FLAG_OVERLAPPED,
},
windows_sys::Win32::System::Threading::{GetCurrentProcess, GetCurrentProcessId},
};
#[cfg(all(windows, not(feature = "close")))]
type HANDLE = isize;
#[cfg(all(windows, not(feature = "close")))]
const INVALID_HANDLE_VALUE: HANDLE = !0 as _;
#[cfg(all(windows, not(feature = "close")))]
const INVALID_SOCKET: usize = !0 as _;
#[cfg(any(unix, target_os = "wasi"))]
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct BorrowedFd<'fd> {
fd: RawFd,
_phantom: PhantomData<&'fd OwnedFd>,
}
#[cfg(windows)]
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct BorrowedHandle<'handle> {
handle: RawHandle,
_phantom: PhantomData<&'handle OwnedHandle>,
}
#[cfg(windows)]
#[derive(Copy, Clone)]
#[repr(transparent)]
pub struct BorrowedSocket<'socket> {
socket: RawSocket,
_phantom: PhantomData<&'socket OwnedSocket>,
}
#[cfg(any(unix, target_os = "wasi"))]
#[repr(transparent)]
pub struct OwnedFd {
fd: RawFd,
}
#[cfg(any(unix, target_os = "wasi"))]
impl OwnedFd {
pub fn try_clone(&self) -> std::io::Result<Self> {
crate::AsFd::as_fd(self).try_clone_to_owned()
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl BorrowedFd<'_> {
pub fn try_clone_to_owned(&self) -> std::io::Result<OwnedFd> {
#[cfg(feature = "close")]
{
#[cfg(unix)]
{
#[cfg(not(target_os = "espidf"))]
let cmd = libc::F_DUPFD_CLOEXEC;
#[cfg(target_os = "espidf")]
let cmd = libc::F_DUPFD;
let fd = match unsafe { libc::fcntl(self.as_raw_fd(), cmd, 0) } {
-1 => return Err(std::io::Error::last_os_error()),
fd => fd,
};
Ok(unsafe { OwnedFd::from_raw_fd(fd) })
}
#[cfg(target_os = "wasi")]
{
unreachable!("try_clone is not yet suppported on wasi");
}
}
#[cfg(not(feature = "close"))]
{
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
#[repr(transparent)]
pub struct OwnedHandle {
handle: RawHandle,
}
#[cfg(windows)]
impl OwnedHandle {
pub fn try_clone(&self) -> std::io::Result<Self> {
crate::AsHandle::as_handle(self).try_clone_to_owned()
}
}
#[cfg(windows)]
impl BorrowedHandle<'_> {
pub fn try_clone_to_owned(&self) -> std::io::Result<OwnedHandle> {
#[cfg(feature = "close")]
{
self.duplicate(0, false, DUPLICATE_SAME_ACCESS)
}
#[cfg(not(feature = "close"))]
{
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
}
}
#[cfg(feature = "close")]
pub(crate) fn duplicate(
&self,
access: u32,
inherit: bool,
options: DUPLICATE_HANDLE_OPTIONS,
) -> std::io::Result<OwnedHandle> {
let mut ret = 0 as HANDLE;
match unsafe {
let cur_proc = GetCurrentProcess();
DuplicateHandle(
cur_proc,
self.as_raw_handle() as HANDLE,
cur_proc,
&mut ret,
access,
inherit as BOOL,
options,
)
} {
0 => return Err(std::io::Error::last_os_error()),
_ => (),
}
unsafe { Ok(OwnedHandle::from_raw_handle(ret as RawHandle)) }
}
}
#[cfg(windows)]
#[repr(transparent)]
pub struct OwnedSocket {
socket: RawSocket,
}
#[cfg(windows)]
impl OwnedSocket {
pub fn try_clone(&self) -> std::io::Result<Self> {
crate::AsSocket::as_socket(self).try_clone_to_owned()
}
#[cfg(feature = "close")]
#[cfg(not(target_vendor = "uwp"))]
fn set_no_inherit(&self) -> std::io::Result<()> {
match unsafe {
SetHandleInformation(self.as_raw_socket() as HANDLE, HANDLE_FLAG_INHERIT, 0)
} {
0 => return Err(std::io::Error::last_os_error()),
_ => Ok(()),
}
}
#[cfg(feature = "close")]
#[cfg(target_vendor = "uwp")]
fn set_no_inherit(&self) -> std::io::Result<()> {
Err(io::Error::new_const(
std::io::ErrorKind::Unsupported,
&"Unavailable on UWP",
))
}
}
#[cfg(windows)]
impl BorrowedSocket<'_> {
pub fn try_clone_to_owned(&self) -> std::io::Result<OwnedSocket> {
#[cfg(feature = "close")]
{
let mut info = unsafe { std::mem::zeroed::<WSAPROTOCOL_INFOW>() };
let result = unsafe {
WSADuplicateSocketW(self.as_raw_socket() as _, GetCurrentProcessId(), &mut info)
};
match result {
SOCKET_ERROR => return Err(std::io::Error::last_os_error()),
0 => (),
_ => panic!(),
}
let socket = unsafe {
WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
WSA_FLAG_OVERLAPPED | WSA_FLAG_NO_HANDLE_INHERIT,
)
};
if socket != INVALID_SOCKET {
unsafe { Ok(OwnedSocket::from_raw_socket(socket as _)) }
} else {
let error = unsafe { WSAGetLastError() };
if error != WSAEPROTOTYPE && error != WSAEINVAL {
return Err(std::io::Error::from_raw_os_error(error));
}
let socket = unsafe {
WSASocketW(
info.iAddressFamily,
info.iSocketType,
info.iProtocol,
&mut info,
0,
WSA_FLAG_OVERLAPPED,
)
};
if socket == INVALID_SOCKET {
return Err(std::io::Error::last_os_error());
}
unsafe {
let socket = OwnedSocket::from_raw_socket(socket as _);
socket.set_no_inherit()?;
Ok(socket)
}
}
}
#[cfg(not(feature = "close"))]
{
unreachable!("try_clone called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
#[repr(transparent)]
#[derive(Debug)]
pub struct HandleOrInvalid(RawHandle);
#[cfg(windows)]
#[repr(transparent)]
#[derive(Debug)]
pub struct HandleOrNull(RawHandle);
#[cfg(windows)]
unsafe impl Send for OwnedHandle {}
#[cfg(windows)]
unsafe impl Send for HandleOrInvalid {}
#[cfg(windows)]
unsafe impl Send for HandleOrNull {}
#[cfg(windows)]
unsafe impl Send for BorrowedHandle<'_> {}
#[cfg(windows)]
unsafe impl Sync for OwnedHandle {}
#[cfg(windows)]
unsafe impl Sync for HandleOrInvalid {}
#[cfg(windows)]
unsafe impl Sync for HandleOrNull {}
#[cfg(windows)]
unsafe impl Sync for BorrowedHandle<'_> {}
#[cfg(any(unix, target_os = "wasi"))]
impl BorrowedFd<'_> {
#[inline]
pub const unsafe fn borrow_raw(fd: RawFd) -> Self {
#[cfg(panic_in_const_fn)]
debug_assert!(fd != -1_i32 as RawFd);
Self {
fd,
_phantom: PhantomData,
}
}
}
#[cfg(windows)]
impl BorrowedHandle<'_> {
#[inline]
pub const unsafe fn borrow_raw(handle: RawHandle) -> Self {
Self {
handle,
_phantom: PhantomData,
}
}
}
#[cfg(windows)]
impl BorrowedSocket<'_> {
#[inline]
pub const unsafe fn borrow_raw(socket: RawSocket) -> Self {
#[cfg(panic_in_const_fn)]
debug_assert!(socket != INVALID_SOCKET as RawSocket);
Self {
socket,
_phantom: PhantomData,
}
}
}
#[cfg(windows)]
impl TryFrom<HandleOrInvalid> for OwnedHandle {
type Error = InvalidHandleError;
#[inline]
fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, InvalidHandleError> {
let raw = handle_or_invalid.0;
if raw as HANDLE == INVALID_HANDLE_VALUE {
forget(handle_or_invalid);
Err(InvalidHandleError(()))
} else {
Ok(OwnedHandle { handle: raw })
}
}
}
#[cfg(windows)]
impl TryFrom<HandleOrNull> for OwnedHandle {
type Error = NullHandleError;
#[inline]
fn try_from(handle_or_null: HandleOrNull) -> Result<Self, NullHandleError> {
let raw = handle_or_null.0;
if raw.is_null() {
forget(handle_or_null);
Err(NullHandleError(()))
} else {
Ok(OwnedHandle { handle: raw })
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NullHandleError(());
impl fmt::Display for NullHandleError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
"A HandleOrNull could not be converted to a handle because it was null".fmt(fmt)
}
}
impl std::error::Error for NullHandleError {}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct InvalidHandleError(());
impl fmt::Display for InvalidHandleError {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
"A HandleOrInvalid could not be converted to a handle because it was INVALID_HANDLE_VALUE"
.fmt(fmt)
}
}
impl std::error::Error for InvalidHandleError {}
#[cfg(any(unix, target_os = "wasi"))]
impl AsRawFd for BorrowedFd<'_> {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
#[cfg(windows)]
impl AsRawHandle for BorrowedHandle<'_> {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.handle
}
}
#[cfg(windows)]
impl AsRawSocket for BorrowedSocket<'_> {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
self.socket
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl AsRawFd for OwnedFd {
#[inline]
fn as_raw_fd(&self) -> RawFd {
self.fd
}
}
#[cfg(windows)]
impl AsRawHandle for OwnedHandle {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
self.handle
}
}
#[cfg(windows)]
impl AsRawSocket for OwnedSocket {
#[inline]
fn as_raw_socket(&self) -> RawSocket {
self.socket
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl IntoRawFd for OwnedFd {
#[inline]
fn into_raw_fd(self) -> RawFd {
let fd = self.fd;
forget(self);
fd
}
}
#[cfg(windows)]
impl IntoRawHandle for OwnedHandle {
#[inline]
fn into_raw_handle(self) -> RawHandle {
let handle = self.handle;
forget(self);
handle
}
}
#[cfg(windows)]
impl IntoRawSocket for OwnedSocket {
#[inline]
fn into_raw_socket(self) -> RawSocket {
let socket = self.socket;
forget(self);
socket
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl FromRawFd for OwnedFd {
#[inline]
unsafe fn from_raw_fd(fd: RawFd) -> Self {
debug_assert_ne!(fd, -1_i32 as RawFd);
Self { fd }
}
}
#[cfg(windows)]
impl FromRawHandle for OwnedHandle {
#[inline]
unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self { handle }
}
}
#[cfg(windows)]
impl FromRawSocket for OwnedSocket {
#[inline]
unsafe fn from_raw_socket(socket: RawSocket) -> Self {
debug_assert_ne!(socket, INVALID_SOCKET as RawSocket);
Self { socket }
}
}
#[cfg(windows)]
impl HandleOrInvalid {
#[inline]
pub unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self(handle)
}
}
#[cfg(windows)]
impl HandleOrNull {
#[inline]
pub unsafe fn from_raw_handle(handle: RawHandle) -> Self {
Self(handle)
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl Drop for OwnedFd {
#[inline]
fn drop(&mut self) {
#[cfg(feature = "close")]
unsafe {
let _ = libc::close(self.fd as std::os::raw::c_int);
}
#[cfg(not(feature = "close"))]
{
unreachable!("drop called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
impl Drop for OwnedHandle {
#[inline]
fn drop(&mut self) {
#[cfg(feature = "close")]
unsafe {
let _ = CloseHandle(self.handle as HANDLE);
}
#[cfg(not(feature = "close"))]
{
unreachable!("drop called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
impl Drop for HandleOrInvalid {
#[inline]
fn drop(&mut self) {
#[cfg(feature = "close")]
unsafe {
let _ = CloseHandle(self.0 as HANDLE);
}
#[cfg(not(feature = "close"))]
{
unreachable!("drop called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
impl Drop for HandleOrNull {
#[inline]
fn drop(&mut self) {
#[cfg(feature = "close")]
unsafe {
let _ = CloseHandle(self.0 as HANDLE);
}
#[cfg(not(feature = "close"))]
{
unreachable!("drop called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(windows)]
impl Drop for OwnedSocket {
#[inline]
fn drop(&mut self) {
#[cfg(feature = "close")]
unsafe {
let _ = closesocket(self.socket as SOCKET);
}
#[cfg(not(feature = "close"))]
{
unreachable!("drop called without the \"close\" feature in io-lifetimes");
}
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl fmt::Debug for BorrowedFd<'_> {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedFd").field("fd", &self.fd).finish()
}
}
#[cfg(windows)]
impl fmt::Debug for BorrowedHandle<'_> {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedHandle")
.field("handle", &self.handle)
.finish()
}
}
#[cfg(windows)]
impl fmt::Debug for BorrowedSocket<'_> {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BorrowedSocket")
.field("socket", &self.socket)
.finish()
}
}
#[cfg(any(unix, target_os = "wasi"))]
impl fmt::Debug for OwnedFd {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedFd").field("fd", &self.fd).finish()
}
}
#[cfg(windows)]
impl fmt::Debug for OwnedHandle {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedHandle")
.field("handle", &self.handle)
.finish()
}
}
#[cfg(windows)]
impl fmt::Debug for OwnedSocket {
#[allow(clippy::missing_inline_in_public_items)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("OwnedSocket")
.field("socket", &self.socket)
.finish()
}
}