#![doc(test(attr(deny(warnings))))]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(
feature = "nightly-specialization",
allow(incomplete_features),
feature(specialization)
)]
#![cfg_attr(feature = "nightly-iter", feature(trusted_len))]
use core::{mem, ops};
#[cfg(feature = "std-io")]
use std::io;
#[cfg(all(unix, feature = "std-io"))]
use std::{
fs::File,
io::{PipeReader, PipeWriter},
os::fd::AsRawFd,
};
#[cfg(all(unix, feature = "std-io"))]
use libc;
pub mod os;
#[cfg(feature = "vectored")]
pub mod vectored;
pub trait BufError {
fn would_block(&self) -> bool;
fn was_interrupted(&self) -> bool;
}
#[cfg(feature = "std-io")]
impl BufError for io::Error {
#[inline(always)]
fn would_block(&self) -> bool {
match self.kind() {
io::ErrorKind::WouldBlock => true,
_ => false,
}
}
#[inline(always)]
fn was_interrupted(&self) -> bool {
match self.kind() {
io::ErrorKind::Interrupted => true,
_ => false,
}
}
}
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct BlockingPipe<P>(P);
impl<P> BlockingPipe<P> {
#[inline(always)]
pub fn into_inner(self) -> P {
let Self(pipe) = self;
pipe
}
}
impl<P> ops::Deref for BlockingPipe<P> {
type Target = P;
#[inline(always)]
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<P> ops::DerefMut for BlockingPipe<P> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
#[cfg(all(unix, feature = "std-io"))]
impl BlockingPipe<PipeReader> {
#[inline(always)]
pub fn new_reader(pipe: PipeReader) -> io::Result<Self> {
let fd = pipe.as_raw_fd();
match unsafe { libc::fcntl(fd, libc::F_GETFL) } {
-1 => Err(io::Error::last_os_error()),
n if n | libc::O_NONBLOCK != 0 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"was given nonblocking pipe reader fd",
)),
_ => Ok(Self(pipe)),
}
}
}
#[cfg(all(unix, feature = "std-io"))]
impl BlockingPipe<PipeWriter> {
#[inline(always)]
pub fn new_writer(pipe: PipeWriter) -> io::Result<Self> {
let fd = pipe.as_raw_fd();
match unsafe { libc::fcntl(fd, libc::F_GETFL) } {
-1 => Err(io::Error::last_os_error()),
n if n | libc::O_NONBLOCK != 0 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"was given nonblocking pipe writer fd",
)),
_ => Ok(Self(pipe)),
}
}
}
#[derive(Debug, Copy, Clone)]
#[repr(transparent)]
pub struct NonBlockingPipe<P>(P);
impl<P> NonBlockingPipe<P> {
#[inline(always)]
pub fn into_inner(self) -> P {
let Self(pipe) = self;
pipe
}
}
impl<P> ops::Deref for NonBlockingPipe<P> {
type Target = P;
#[inline(always)]
fn deref(&self) -> &Self::Target { &self.0 }
}
impl<P> ops::DerefMut for NonBlockingPipe<P> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
#[cfg(all(unix, feature = "std-io"))]
impl NonBlockingPipe<PipeReader> {
#[inline(always)]
pub fn new_reader(pipe: PipeReader) -> io::Result<Self> {
let fd = pipe.as_raw_fd();
match unsafe { libc::fcntl(fd, libc::F_GETFL) } {
-1 => Err(io::Error::last_os_error()),
n if n | libc::O_NONBLOCK == 0 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"was given blocking pipe reader fd",
)),
_ => Ok(Self(pipe)),
}
}
}
#[cfg(all(unix, feature = "std-io"))]
impl NonBlockingPipe<PipeWriter> {
#[inline(always)]
pub fn new_writer(pipe: PipeWriter) -> io::Result<Self> {
let fd = pipe.as_raw_fd();
match unsafe { libc::fcntl(fd, libc::F_GETFL) } {
-1 => Err(io::Error::last_os_error()),
n if n | libc::O_NONBLOCK == 0 => Err(io::Error::new(
io::ErrorKind::InvalidInput,
"was given blocking pipe writer fd",
)),
_ => Ok(Self(pipe)),
}
}
}
pub trait ReadBuf<T> {
type E: BufError;
fn read_buf<'buf>(&mut self, buf: &'buf mut [mem::MaybeUninit<T>]) -> (usize, Option<Self::E>);
#[inline]
fn read_buf_exact<'buf>(
&mut self,
mut buf: &'buf mut [mem::MaybeUninit<T>],
) -> Result<(), (usize, Self::E)> {
let mut all_read: usize = 0;
while !buf.is_empty() {
let (cur_read, err) = self.read_buf(buf);
all_read += cur_read;
if let Some(err) = err {
if err.would_block() {
unreachable!("read_buf() is allowed to block!");
}
if !err.was_interrupted() {
return Err((all_read, err));
}
}
buf = &mut buf[cur_read..];
}
Ok(())
}
}
#[cfg(all(unix, feature = "std-io"))]
impl ReadBuf<u8> for File {
type E = io::Error;
#[inline]
fn read_buf<'buf>(&mut self, buf: &'buf mut [mem::MaybeUninit<u8>]) -> (usize, Option<Self::E>) {
let fd = self.as_raw_fd();
let count = buf.len();
let buf: *mut libc::c_void = buf.as_mut_ptr().cast();
match unsafe { libc::read(fd, buf, count) } {
-1 => (0, Some(io::Error::last_os_error())),
n => (n as usize, None),
}
}
}
#[cfg(all(unix, feature = "std-io"))]
impl ReadBuf<u8> for BlockingPipe<PipeReader> {
type E = io::Error;
#[inline]
fn read_buf<'buf>(&mut self, buf: &'buf mut [mem::MaybeUninit<u8>]) -> (usize, Option<Self::E>) {
let fd = self.as_raw_fd();
let count = buf.len();
let buf: *mut libc::c_void = buf.as_mut_ptr().cast();
match unsafe { libc::read(fd, buf, count) } {
-1 => (0, Some(io::Error::last_os_error())),
n => (n as usize, None),
}
}
}
pub trait ReadBufAtomic<T> {
type E: BufError;
fn read_buf_atomic<'buf>(&mut self, buf: &'buf mut [mem::MaybeUninit<T>]) -> Result<(), Self::E>;
}
pub trait ReadBufNonBlocking<T> {
type E: BufError;
fn read_buf_nonblocking<'buf>(
&mut self,
buf: &'buf mut [mem::MaybeUninit<T>],
) -> Result<usize, Self::E>;
}
#[cfg(all(unix, feature = "std-io"))]
impl ReadBufNonBlocking<u8> for NonBlockingPipe<PipeReader> {
type E = io::Error;
#[inline]
fn read_buf_nonblocking<'buf>(
&mut self,
buf: &'buf mut [mem::MaybeUninit<u8>],
) -> Result<usize, Self::E> {
let fd = self.as_raw_fd();
let count = buf.len();
let buf: *mut libc::c_void = buf.as_mut_ptr().cast();
match unsafe { libc::read(fd, buf, count) } {
-1 => Err(io::Error::last_os_error()),
n => Ok(n as usize),
}
}
}
pub trait WriteData<T> {
type E: BufError;
fn write_data<'data>(&mut self, data: &'data [T]) -> (usize, Option<Self::E>);
#[inline]
fn write_data_exact<'data>(&mut self, mut data: &'data [T]) -> Result<(), (usize, Self::E)> {
let mut all_written: usize = 0;
while !data.is_empty() {
let (cur_written, err) = self.write_data(data);
all_written += cur_written;
if let Some(err) = err {
if err.would_block() {
unreachable!("write_data() is allowed to block!");
}
if !err.was_interrupted() {
return Err((all_written, err));
}
}
data = &data[cur_written..];
}
Ok(())
}
}
#[cfg(all(unix, feature = "std-io"))]
impl WriteData<u8> for File {
type E = io::Error;
#[inline]
fn write_data<'data>(&mut self, data: &'data [u8]) -> (usize, Option<Self::E>) {
let fd = self.as_raw_fd();
let count = data.len();
let buf: *const libc::c_void = data.as_ptr().cast();
match unsafe { libc::write(fd, buf, count) } {
-1 => (0, Some(io::Error::last_os_error())),
n => (n as usize, None),
}
}
}
#[cfg(all(unix, feature = "std-io"))]
impl WriteData<u8> for BlockingPipe<PipeWriter> {
type E = io::Error;
#[inline]
fn write_data<'data>(&mut self, data: &'data [u8]) -> (usize, Option<Self::E>) {
let fd = self.as_raw_fd();
let count = data.len();
let buf: *const libc::c_void = data.as_ptr().cast();
match unsafe { libc::write(fd, buf, count) } {
-1 => (0, Some(io::Error::last_os_error())),
n => (n as usize, None),
}
}
}
pub trait WriteDataAtomic<T> {
type E: BufError;
fn write_data_atomic<'data>(&mut self, data: &'data [T]) -> Result<(), Self::E>;
}
pub trait WriteDataNonBlocking<T> {
type E: BufError;
fn write_data_nonblocking<'data>(&mut self, data: &'data [T]) -> Result<usize, Self::E>;
}
#[cfg(all(unix, feature = "std-io"))]
impl WriteDataNonBlocking<u8> for NonBlockingPipe<PipeWriter> {
type E = io::Error;
#[inline]
fn write_data_nonblocking<'data>(&mut self, data: &'data [u8]) -> Result<usize, Self::E> {
let fd = self.as_raw_fd();
let count = data.len();
let buf: *const libc::c_void = data.as_ptr().cast();
match unsafe { libc::write(fd, buf, count) } {
-1 => Err(io::Error::last_os_error()),
n => Ok(n as usize),
}
}
}