system_interface/io/
read_ready.rs1use crate::io::IsReadWrite;
2use io_lifetimes::raw::{AsRawFilelike, FromRawFilelike};
3#[cfg(not(any(windows, target_os = "redox")))]
4use rustix::io::ioctl_fionread;
5use std::io::{self, Seek, SeekFrom, Stdin, StdinLock};
6#[cfg(not(target_os = "redox"))]
7use std::net;
8use std::process::{ChildStderr, ChildStdout};
9#[cfg(windows)]
10use {
11 std::{mem::MaybeUninit, os::windows::io::AsRawSocket},
12 windows_sys::Win32::Networking::WinSock::{ioctlsocket, FIONREAD, SOCKET},
13};
14
15pub trait ReadReady {
18 fn num_ready_bytes(&self) -> io::Result<u64>;
24}
25
26#[cfg(not(any(windows, target_os = "redox")))]
28impl ReadReady for Stdin {
29 #[inline]
30 fn num_ready_bytes(&self) -> io::Result<u64> {
31 Ok(ioctl_fionread(self)?)
32 }
33}
34
35#[cfg(any(windows, target_os = "redox"))]
37impl ReadReady for Stdin {
38 #[inline]
39 fn num_ready_bytes(&self) -> io::Result<u64> {
40 Ok(0)
42 }
43}
44
45#[cfg(not(any(windows, target_os = "redox")))]
47impl<'a> ReadReady for StdinLock<'a> {
48 #[inline]
49 fn num_ready_bytes(&self) -> io::Result<u64> {
50 Ok(ioctl_fionread(self)?)
51 }
52}
53
54#[cfg(any(windows, target_os = "redox"))]
56impl<'a> ReadReady for StdinLock<'a> {
57 #[inline]
58 fn num_ready_bytes(&self) -> io::Result<u64> {
59 Ok(0)
61 }
62}
63
64impl crate::io::ReadReady for std::fs::File {
65 #[inline]
66 fn num_ready_bytes(&self) -> std::io::Result<u64> {
67 let (read, _write) = self.is_read_write()?;
68 if read {
69 let metadata = self.metadata()?;
71 if metadata.is_file() {
72 let mut tmp = unsafe { std::fs::File::from_raw_filelike(self.as_raw_filelike()) };
76 let current = tmp.seek(SeekFrom::Current(0));
77 std::mem::forget(tmp);
78 return Ok(metadata.len() - current?);
79 }
80
81 #[cfg(unix)]
83 if let Ok(n) = ioctl_fionread(self) {
84 return Ok(n);
85 }
86
87 return Ok(0);
90 }
91 Err(std::io::Error::new(
92 std::io::ErrorKind::InvalidInput,
93 "stream is not readable",
94 ))
95 }
96}
97
98#[cfg(not(any(windows, target_os = "redox")))]
100impl ReadReady for net::TcpStream {
101 #[inline]
102 fn num_ready_bytes(&self) -> io::Result<u64> {
103 Ok(ioctl_fionread(self)?)
104 }
105}
106
107#[cfg(not(any(windows, target_os = "redox")))]
109#[cfg(feature = "cap_std_impls")]
110impl ReadReady for cap_std::net::TcpStream {
111 #[inline]
112 fn num_ready_bytes(&self) -> io::Result<u64> {
113 use io_lifetimes::AsSocketlike;
114 self.as_socketlike_view::<net::TcpStream>()
115 .num_ready_bytes()
116 }
117}
118
119#[cfg(target_os = "redox")]
121impl ReadReady for net::TcpStream {
122 #[inline]
123 fn num_ready_bytes(&self) -> io::Result<u64> {
124 Ok(0)
126 }
127}
128
129#[cfg(target_os = "redox")]
131impl ReadReady for cap_std::net::TcpStream {
132 #[inline]
133 fn num_ready_bytes(&self) -> io::Result<u64> {
134 use io_lifetimes::AsSocketlike;
135 self.as_socketlike_view::<net::TcpStream>()
136 .num_ready_bytes()
137 }
138}
139
140#[cfg(unix)]
142impl ReadReady for std::os::unix::net::UnixStream {
143 #[inline]
144 fn num_ready_bytes(&self) -> io::Result<u64> {
145 Ok(ioctl_fionread(self)?)
146 }
147}
148
149impl ReadReady for &[u8] {
151 #[inline]
152 fn num_ready_bytes(&self) -> io::Result<u64> {
153 Ok(self.len() as u64)
154 }
155}
156
157impl ReadReady for std::io::Empty {
159 #[inline]
160 fn num_ready_bytes(&self) -> io::Result<u64> {
161 Ok(1)
164 }
165}
166
167impl ReadReady for std::io::Repeat {
169 #[inline]
170 fn num_ready_bytes(&self) -> io::Result<u64> {
171 Ok(u64::MAX)
173 }
174}
175
176impl<T> ReadReady for std::collections::VecDeque<T> {
178 #[inline]
179 fn num_ready_bytes(&self) -> io::Result<u64> {
180 Ok(self.len() as u64)
181 }
182}
183
184impl<R: ReadReady> ReadReady for Box<R> {
186 #[inline]
187 fn num_ready_bytes(&self) -> io::Result<u64> {
188 self.as_ref().num_ready_bytes()
189 }
190}
191
192impl<R: std::io::Read + ReadReady> ReadReady for std::io::BufReader<R> {
194 #[inline]
195 fn num_ready_bytes(&self) -> io::Result<u64> {
196 let buffer = self.buffer();
197 match self.get_ref().num_ready_bytes() {
198 Ok(n) => Ok(buffer.len() as u64 + n),
199 Err(_) if !buffer.is_empty() => Ok(buffer.len() as u64),
200 Err(e) => Err(e),
201 }
202 }
203}
204
205impl<T: AsRef<[u8]>> ReadReady for std::io::Cursor<T> {
207 #[inline]
208 fn num_ready_bytes(&self) -> io::Result<u64> {
209 Ok(self.get_ref().as_ref().len() as u64 - self.position())
210 }
211}
212
213impl<T: ReadReady> ReadReady for std::io::Take<T> {
215 #[inline]
216 fn num_ready_bytes(&self) -> io::Result<u64> {
217 Ok(std::cmp::min(
218 self.limit(),
219 self.get_ref().num_ready_bytes()?,
220 ))
221 }
222}
223
224impl<T: ReadReady, U> ReadReady for std::io::Chain<T, U> {
226 #[inline]
227 fn num_ready_bytes(&self) -> io::Result<u64> {
228 self.get_ref().0.num_ready_bytes()
231 }
232}
233
234#[cfg(windows)]
236impl ReadReady for net::TcpStream {
237 #[inline]
238 fn num_ready_bytes(&self) -> io::Result<u64> {
239 let mut arg = MaybeUninit::<std::os::raw::c_ulong>::uninit();
240 if unsafe { ioctlsocket(self.as_raw_socket() as SOCKET, FIONREAD, arg.as_mut_ptr()) } == 0 {
241 Ok(unsafe { arg.assume_init() }.into())
242 } else {
243 Err(io::Error::last_os_error())
244 }
245 }
246}
247
248#[cfg(windows)]
250#[cfg(feature = "cap_std_impls")]
251impl ReadReady for cap_std::net::TcpStream {
252 #[inline]
253 fn num_ready_bytes(&self) -> io::Result<u64> {
254 use io_lifetimes::AsSocketlike;
255 self.as_socketlike_view::<net::TcpStream>()
256 .num_ready_bytes()
257 }
258}
259
260#[cfg(feature = "socket2")]
262impl ReadReady for socket2::Socket {
263 #[inline]
264 fn num_ready_bytes(&self) -> io::Result<u64> {
265 use io_lifetimes::AsSocketlike;
266 self.as_socketlike_view::<std::net::TcpStream>()
267 .num_ready_bytes()
268 }
269}
270
271#[cfg(all(not(windows), feature = "os_pipe"))]
272impl ReadReady for os_pipe::PipeReader {
273 #[inline]
274 fn num_ready_bytes(&self) -> io::Result<u64> {
275 Ok(ioctl_fionread(self)?)
276 }
277}
278
279#[cfg(all(windows, feature = "os_pipe"))]
280impl ReadReady for os_pipe::PipeReader {
281 #[inline]
282 fn num_ready_bytes(&self) -> io::Result<u64> {
283 Ok(0)
285 }
286}
287
288#[cfg(not(windows))]
289impl ReadReady for ChildStdout {
290 #[inline]
291 fn num_ready_bytes(&self) -> io::Result<u64> {
292 Ok(ioctl_fionread(self)?)
293 }
294}
295
296#[cfg(windows)]
297impl ReadReady for ChildStdout {
298 #[inline]
299 fn num_ready_bytes(&self) -> io::Result<u64> {
300 Ok(0)
302 }
303}
304
305#[cfg(not(windows))]
306impl ReadReady for ChildStderr {
307 #[inline]
308 fn num_ready_bytes(&self) -> io::Result<u64> {
309 Ok(ioctl_fionread(self)?)
310 }
311}
312
313#[cfg(windows)]
314impl ReadReady for ChildStderr {
315 #[inline]
316 fn num_ready_bytes(&self) -> io::Result<u64> {
317 Ok(0)
319 }
320}
321
322#[cfg(feature = "socketpair")]
323impl ReadReady for socketpair::SocketpairStream {
324 #[inline]
325 fn num_ready_bytes(&self) -> io::Result<u64> {
326 socketpair::SocketpairStream::num_ready_bytes(self)
327 }
328}
329
330#[cfg(feature = "ssh2")]
331impl ReadReady for ssh2::Channel {
332 #[inline]
333 fn num_ready_bytes(&self) -> io::Result<u64> {
334 Ok(u64::from(self.read_window().available))
335 }
336}
337
338#[cfg(feature = "char-device")]
339impl ReadReady for char_device::CharDevice {
340 #[inline]
341 fn num_ready_bytes(&self) -> io::Result<u64> {
342 char_device::CharDevice::num_ready_bytes(self)
343 }
344}
345
346#[cfg(feature = "cap_std_impls")]
348impl ReadReady for cap_std::fs::File {
349 #[inline]
350 fn num_ready_bytes(&self) -> io::Result<u64> {
351 use io_lifetimes::AsFilelike;
352 self.as_filelike_view::<std::fs::File>().num_ready_bytes()
353 }
354}
355
356#[cfg(feature = "cap_std_impls_fs_utf8")]
358impl ReadReady for cap_std::fs_utf8::File {
359 #[inline]
360 fn num_ready_bytes(&self) -> io::Result<u64> {
361 use io_lifetimes::AsFilelike;
362 self.as_filelike_view::<std::fs::File>().num_ready_bytes()
363 }
364}