use crate::io::IsReadWrite;
use io_lifetimes::raw::{AsRawFilelike, FromRawFilelike};
#[cfg(not(any(windows, target_os = "redox")))]
use rustix::io::ioctl_fionread;
use std::io::{self, Seek, SeekFrom, Stdin, StdinLock};
#[cfg(not(target_os = "redox"))]
use std::net;
use std::process::{ChildStderr, ChildStdout};
#[cfg(windows)]
use {
std::{mem::MaybeUninit, os::windows::io::AsRawSocket},
windows_sys::Win32::Networking::WinSock::{ioctlsocket, FIONREAD, SOCKET},
};
pub trait ReadReady {
fn num_ready_bytes(&self) -> io::Result<u64>;
}
#[cfg(not(any(windows, target_os = "redox")))]
impl ReadReady for Stdin {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(any(windows, target_os = "redox"))]
impl ReadReady for Stdin {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
#[cfg(not(any(windows, target_os = "redox")))]
impl<'a> ReadReady for StdinLock<'a> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(any(windows, target_os = "redox"))]
impl<'a> ReadReady for StdinLock<'a> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
impl crate::io::ReadReady for std::fs::File {
#[inline]
fn num_ready_bytes(&self) -> std::io::Result<u64> {
let (read, _write) = self.is_read_write()?;
if read {
let metadata = self.metadata()?;
if metadata.is_file() {
let mut tmp = unsafe { std::fs::File::from_raw_filelike(self.as_raw_filelike()) };
let current = tmp.seek(SeekFrom::Current(0));
std::mem::forget(tmp);
return Ok(metadata.len() - current?);
}
#[cfg(unix)]
if let Ok(n) = ioctl_fionread(self) {
return Ok(n);
}
return Ok(0);
}
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"stream is not readable",
))
}
}
#[cfg(not(any(windows, target_os = "redox")))]
impl ReadReady for net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(not(any(windows, target_os = "redox")))]
#[cfg(feature = "cap_std_impls")]
impl ReadReady for cap_std::net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsSocketlike;
self.as_socketlike_view::<net::TcpStream>()
.num_ready_bytes()
}
}
#[cfg(target_os = "redox")]
impl ReadReady for net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
#[cfg(target_os = "redox")]
impl ReadReady for cap_std::net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsSocketlike;
self.as_socketlike_view::<net::TcpStream>()
.num_ready_bytes()
}
}
#[cfg(unix)]
impl ReadReady for std::os::unix::net::UnixStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
impl ReadReady for &[u8] {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(self.len() as u64)
}
}
impl ReadReady for std::io::Empty {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(1)
}
}
impl ReadReady for std::io::Repeat {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(u64::MAX)
}
}
impl<T> ReadReady for std::collections::VecDeque<T> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(self.len() as u64)
}
}
impl<R: ReadReady> ReadReady for Box<R> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
self.as_ref().num_ready_bytes()
}
}
impl<R: std::io::Read + ReadReady> ReadReady for std::io::BufReader<R> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
let buffer = self.buffer();
match self.get_ref().num_ready_bytes() {
Ok(n) => Ok(buffer.len() as u64 + n),
Err(_) if !buffer.is_empty() => Ok(buffer.len() as u64),
Err(e) => Err(e),
}
}
}
impl<T: AsRef<[u8]>> ReadReady for std::io::Cursor<T> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(self.get_ref().as_ref().len() as u64 - self.position())
}
}
impl<T: ReadReady> ReadReady for std::io::Take<T> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(std::cmp::min(
self.limit(),
self.get_ref().num_ready_bytes()?,
))
}
}
impl<T: ReadReady, U> ReadReady for std::io::Chain<T, U> {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
self.get_ref().0.num_ready_bytes()
}
}
#[cfg(windows)]
impl ReadReady for net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
let mut arg = MaybeUninit::<std::os::raw::c_ulong>::uninit();
if unsafe { ioctlsocket(self.as_raw_socket() as SOCKET, FIONREAD, arg.as_mut_ptr()) } == 0 {
Ok(unsafe { arg.assume_init() }.into())
} else {
Err(io::Error::last_os_error())
}
}
}
#[cfg(windows)]
#[cfg(feature = "cap_std_impls")]
impl ReadReady for cap_std::net::TcpStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsSocketlike;
self.as_socketlike_view::<net::TcpStream>()
.num_ready_bytes()
}
}
#[cfg(feature = "socket2")]
impl ReadReady for socket2::Socket {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsSocketlike;
self.as_socketlike_view::<std::net::TcpStream>()
.num_ready_bytes()
}
}
#[cfg(all(not(windows), feature = "os_pipe"))]
impl ReadReady for os_pipe::PipeReader {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(all(windows, feature = "os_pipe"))]
impl ReadReady for os_pipe::PipeReader {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
#[cfg(not(windows))]
impl ReadReady for ChildStdout {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(windows)]
impl ReadReady for ChildStdout {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
#[cfg(not(windows))]
impl ReadReady for ChildStderr {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(ioctl_fionread(self)?)
}
}
#[cfg(windows)]
impl ReadReady for ChildStderr {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(0)
}
}
#[cfg(feature = "socketpair")]
impl ReadReady for socketpair::SocketpairStream {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
socketpair::SocketpairStream::num_ready_bytes(self)
}
}
#[cfg(feature = "ssh2")]
impl ReadReady for ssh2::Channel {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
Ok(u64::from(self.read_window().available))
}
}
#[cfg(feature = "char-device")]
impl ReadReady for char_device::CharDevice {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
char_device::CharDevice::num_ready_bytes(self)
}
}
#[cfg(feature = "cap_std_impls")]
impl ReadReady for cap_std::fs::File {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsFilelike;
self.as_filelike_view::<std::fs::File>().num_ready_bytes()
}
}
#[cfg(feature = "cap_std_impls_fs_utf8")]
impl ReadReady for cap_std::fs_utf8::File {
#[inline]
fn num_ready_bytes(&self) -> io::Result<u64> {
use io_lifetimes::AsFilelike;
self.as_filelike_view::<std::fs::File>().num_ready_bytes()
}
}