system_interface/io/
io_ext.rs

1use io_lifetimes::{AsFilelike, AsSocketlike};
2use std::fmt::Arguments;
3use std::io::{self, IoSlice, IoSliceMut, Read, Write};
4use std::slice;
5
6/// Extension trait for I/O handles that are exterior-mutable readable
7/// and writeable.
8pub trait IoExt {
9    /// Pull some bytes from this source into the specified buffer, returning
10    /// how many bytes were read.
11    ///
12    /// This is similar to [`std::io::Read::read`], except it takes `self` by
13    /// immutable reference since the entire side effect is I/O.
14    ///
15    /// [`std::io::Read::read`]: https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read
16    fn read(&self, buf: &mut [u8]) -> io::Result<usize>;
17
18    /// Read the exact number of bytes required to fill `buf`.
19    ///
20    /// This is similar to [`std::io::Read::read_exact`], except it takes
21    /// `self` by immutable reference since the entire side effect is I/O.
22    ///
23    /// [`std::io::Read::read_exact`]: https://doc.rust-lang.org/std/io/trait.Read.html#tymethod.read_exact
24    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()>;
25
26    /// Like `read`, except that it reads into a slice of buffers.
27    ///
28    /// This is similar to [`std::io::Read::read_vectored`], except it takes
29    /// `self` by immutable reference since the entire side effect is I/O.
30    ///
31    /// [`std::io::Read::read_vectored`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_vectored
32    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize>;
33
34    /// Is to `read_vectored` what `read_exact` is to `read`.
35    fn read_exact_vectored(&self, mut bufs: &mut [IoSliceMut]) -> io::Result<()> {
36        bufs = skip_leading_empties(bufs);
37        while !bufs.is_empty() {
38            match self.read_vectored(bufs) {
39                Ok(0) => {
40                    return Err(io::Error::new(
41                        io::ErrorKind::UnexpectedEof,
42                        "failed to fill whole buffer",
43                    ))
44                }
45                Ok(nread) => bufs = advance_mut(bufs, nread),
46                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
47                Err(e) => return Err(e),
48            }
49            bufs = skip_leading_empties(bufs);
50        }
51        Ok(())
52    }
53
54    /// Read all bytes until EOF in this source, placing them into `buf`.
55    ///
56    /// This is similar to [`std::io::Read::read_to_end`], except it takes
57    /// `self` by immutable reference since the entire side effect is I/O.
58    ///
59    /// [`std::io::Read::read_to_end`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_end
60    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize>;
61
62    /// Read all bytes until EOF in this source, appending them to `buf`.
63    ///
64    /// This is similar to [`std::io::Read::read_to_string`], except it takes
65    /// `self` by immutable reference since the entire side effect is I/O.
66    ///
67    /// [`std::io::Read::read_to_string`]: https://doc.rust-lang.org/std/io/trait.Read.html#method.read_to_string
68    fn read_to_string(&self, buf: &mut String) -> io::Result<usize>;
69
70    /// Read bytes from the current position without advancing the current
71    /// position.
72    ///
73    /// This is similar to [`crate::io::Peek::peek`], except it takes
74    /// `self` by immutable reference since the entire side effect is I/O.
75    fn peek(&self, buf: &mut [u8]) -> io::Result<usize>;
76
77    /// Write a buffer into this writer, returning how many bytes were written.
78    ///
79    /// This is similar to [`std::io::Write::write`], except it takes `self` by
80    /// immutable reference since the entire side effect is I/O.
81    ///
82    /// [`std::io::Write::write`]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write
83    fn write(&self, buf: &[u8]) -> io::Result<usize>;
84
85    /// Attempts to write an entire buffer into this writer.
86    ///
87    /// This is similar to [`std::io::Write::write_all`], except it takes
88    /// `self` by immutable reference since the entire side effect is I/O.
89    ///
90    /// [`std::io::Write::write_all`]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write_all
91    fn write_all(&self, buf: &[u8]) -> io::Result<()>;
92
93    /// Like `write`, except that it writes from a slice of buffers.
94    ///
95    /// This is similar to [`std::io::Write::write_vectored`], except it takes
96    /// `self` by immutable reference since the entire side effect is I/O.
97    ///
98    /// [`std::io::Write::write_vectored`]: https://doc.rust-lang.org/std/io/trait.Write.html#method.write_vectored
99    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize>;
100
101    /// Is to `write_vectored` what `write_all` is to `write`.
102    fn write_all_vectored(&self, mut bufs: &mut [IoSlice]) -> io::Result<()> {
103        // TODO: Use [rust-lang/rust#70436] once it stabilizes.
104        // [rust-lang/rust#70436]: https://github.com/rust-lang/rust/issues/70436
105        while !bufs.is_empty() {
106            match self.write_vectored(bufs) {
107                Ok(nwritten) => bufs = advance(bufs, nwritten),
108                Err(ref e) if e.kind() == io::ErrorKind::Interrupted => (),
109                Err(e) => return Err(e),
110            }
111        }
112        Ok(())
113    }
114
115    /// Writes a formatted string into this writer, returning any error
116    /// encountered.
117    ///
118    /// This is similar to [`std::io::Write::write_fmt`], except it takes
119    /// `self` by immutable reference since the entire side effect is I/O.
120    ///
121    /// [`std::io::Write::write_fmt`]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.write_fmt
122    fn write_fmt(&self, fmt: Arguments) -> io::Result<()>;
123
124    /// Flush this output stream, ensuring that all intermediately buffered
125    /// contents reach their destination.
126    ///
127    /// This is similar to [`std::io::Write::flush`], except it takes `self` by
128    /// immutable reference since the entire side effect is I/O.
129    ///
130    /// [`std::io::Write::flush`]: https://doc.rust-lang.org/std/io/trait.Write.html#tymethod.flush
131    fn flush(&self) -> io::Result<()>;
132}
133
134/// Skip any leading elements in `bufs` which are empty buffers.
135fn skip_leading_empties<'a, 'b>(mut bufs: &'b mut [IoSliceMut<'a>]) -> &'b mut [IoSliceMut<'a>] {
136    while !bufs.is_empty() {
137        if !bufs[0].is_empty() {
138            break;
139        }
140        bufs = &mut bufs[1..];
141    }
142    bufs
143}
144
145/// This will be obviated by [rust-lang/rust#62726].
146///
147/// [rust-lang/rust#62726]: https://github.com/rust-lang/rust/issues/62726.
148fn advance<'a, 'b>(bufs: &'b mut [IoSlice<'a>], n: usize) -> &'b mut [IoSlice<'a>] {
149    // Number of buffers to remove.
150    let mut remove = 0;
151    // Total length of all the to be removed buffers.
152    let mut accumulated_len = 0;
153    for buf in bufs.iter() {
154        if accumulated_len + buf.len() > n {
155            break;
156        } else {
157            accumulated_len += buf.len();
158            remove += 1;
159        }
160    }
161
162    #[allow(clippy::indexing_slicing)]
163    let bufs = &mut bufs[remove..];
164    if let Some(first) = bufs.first_mut() {
165        let advance_by = n - accumulated_len;
166        let mut ptr = first.as_ptr();
167        let mut len = first.len();
168        unsafe {
169            ptr = ptr.add(advance_by);
170            len -= advance_by;
171            *first = IoSlice::<'a>::new(slice::from_raw_parts::<'a>(ptr, len));
172        }
173    }
174    bufs
175}
176
177/// This will be obviated by [rust-lang/rust#62726].
178///
179/// [rust-lang/rust#62726]: https://github.com/rust-lang/rust/issues/62726.
180fn advance_mut<'a, 'b>(bufs: &'b mut [IoSliceMut<'a>], n: usize) -> &'b mut [IoSliceMut<'a>] {
181    // Number of buffers to remove.
182    let mut remove = 0;
183    // Total length of all the to be removed buffers.
184    let mut accumulated_len = 0;
185    for buf in bufs.iter() {
186        if accumulated_len + buf.len() > n {
187            break;
188        } else {
189            accumulated_len += buf.len();
190            remove += 1;
191        }
192    }
193
194    #[allow(clippy::indexing_slicing)]
195    let bufs = &mut bufs[remove..];
196    if let Some(first) = bufs.first_mut() {
197        let advance_by = n - accumulated_len;
198        let mut ptr = first.as_mut_ptr();
199        let mut len = first.len();
200        unsafe {
201            ptr = ptr.add(advance_by);
202            len -= advance_by;
203            *first = IoSliceMut::<'a>::new(slice::from_raw_parts_mut::<'a>(ptr, len));
204        }
205    }
206    bufs
207}
208
209/// Implement `IoExt` for any type which implements `AsRawFd`.
210#[cfg(not(windows))]
211impl<T: AsFilelike + AsSocketlike> IoExt for T {
212    #[inline]
213    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
214        Read::read(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
215    }
216
217    #[inline]
218    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
219        Read::read_exact(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
220    }
221
222    #[inline]
223    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
224        Read::read_vectored(&mut &*self.as_filelike_view::<std::fs::File>(), bufs)
225    }
226
227    #[inline]
228    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
229        Read::read_to_end(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
230    }
231
232    #[inline]
233    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
234        Read::read_to_string(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
235    }
236
237    #[inline]
238    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
239        match self.as_socketlike_view::<std::net::TcpStream>().peek(buf) {
240            Err(err) if err.raw_os_error() == Some(rustix::io::Errno::NOTSOCK.raw_os_error()) => {
241                match self.as_filelike_view::<std::fs::File>().peek(buf) {
242                    Err(err)
243                        if err.raw_os_error() == Some(rustix::io::Errno::SPIPE.raw_os_error()) =>
244                    {
245                        Ok(0)
246                    }
247                    otherwise => otherwise,
248                }
249            }
250            otherwise => otherwise,
251        }
252    }
253
254    #[inline]
255    fn write(&self, buf: &[u8]) -> io::Result<usize> {
256        Write::write(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
257    }
258
259    #[inline]
260    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
261        Write::write_all(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
262    }
263
264    #[inline]
265    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
266        Write::write_vectored(&mut &*self.as_filelike_view::<std::fs::File>(), bufs)
267    }
268
269    #[inline]
270    fn flush(&self) -> io::Result<()> {
271        Write::flush(&mut &*self.as_filelike_view::<std::fs::File>())
272    }
273
274    #[inline]
275    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
276        Write::write_fmt(&mut &*self.as_filelike_view::<std::fs::File>(), fmt)
277    }
278}
279
280#[cfg(windows)]
281impl IoExt for std::fs::File {
282    #[inline]
283    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
284        Read::read(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
285    }
286
287    #[inline]
288    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
289        Read::read_exact(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
290    }
291
292    #[inline]
293    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
294        Read::read_vectored(&mut &*self.as_filelike_view::<std::fs::File>(), bufs)
295    }
296
297    #[inline]
298    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
299        Read::read_to_end(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
300    }
301
302    #[inline]
303    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
304        Read::read_to_string(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
305    }
306
307    #[inline]
308    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
309        use std::os::windows::io::AsRawHandle;
310
311        let mut bytes_read = std::mem::MaybeUninit::<u32>::uninit();
312        let len = std::cmp::min(buf.len(), u32::MAX as usize) as u32;
313        let res = unsafe {
314            windows_sys::Win32::System::Pipes::PeekNamedPipe(
315                self.as_filelike().as_raw_handle() as _,
316                buf.as_mut_ptr() as *mut std::ffi::c_void,
317                len,
318                bytes_read.as_mut_ptr(),
319                std::ptr::null_mut(),
320                std::ptr::null_mut(),
321            )
322        };
323        if res == 0 {
324            return Err(io::Error::last_os_error());
325        }
326        Ok(unsafe { bytes_read.assume_init() } as usize)
327    }
328
329    #[inline]
330    fn write(&self, buf: &[u8]) -> io::Result<usize> {
331        Write::write(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
332    }
333
334    #[inline]
335    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
336        Write::write_all(&mut &*self.as_filelike_view::<std::fs::File>(), buf)
337    }
338
339    #[inline]
340    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
341        Write::write_vectored(&mut &*self.as_filelike_view::<std::fs::File>(), bufs)
342    }
343
344    #[inline]
345    fn flush(&self) -> io::Result<()> {
346        Write::flush(&mut &*self.as_filelike_view::<std::fs::File>())
347    }
348
349    #[inline]
350    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
351        Write::write_fmt(&mut &*self.as_filelike_view::<std::fs::File>(), fmt)
352    }
353}
354
355#[cfg(windows)]
356#[cfg(feature = "cap_std_impls")]
357impl IoExt for cap_std::fs::File {
358    #[inline]
359    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
360        self.as_filelike_view::<std::fs::File>().read(buf)
361    }
362
363    #[inline]
364    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
365        self.as_filelike_view::<std::fs::File>().read_exact(buf)
366    }
367
368    #[inline]
369    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
370        self.as_filelike_view::<std::fs::File>().read_vectored(bufs)
371    }
372
373    #[inline]
374    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
375        self.as_filelike_view::<std::fs::File>().read_to_end(buf)
376    }
377
378    #[inline]
379    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
380        self.as_filelike_view::<std::fs::File>().read_to_string(buf)
381    }
382
383    #[inline]
384    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
385        self.as_filelike_view::<std::fs::File>().peek(buf)
386    }
387
388    #[inline]
389    fn write(&self, buf: &[u8]) -> io::Result<usize> {
390        self.as_filelike_view::<std::fs::File>().write(buf)
391    }
392
393    #[inline]
394    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
395        self.as_filelike_view::<std::fs::File>().write_all(buf)
396    }
397
398    #[inline]
399    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
400        self.as_filelike_view::<std::fs::File>()
401            .write_vectored(bufs)
402    }
403
404    #[inline]
405    fn flush(&self) -> io::Result<()> {
406        self.as_filelike_view::<std::fs::File>().flush()
407    }
408
409    #[inline]
410    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
411        self.as_filelike_view::<std::fs::File>().write_fmt(fmt)
412    }
413}
414
415#[cfg(windows)]
416#[cfg(feature = "cap_std_impls_fs_utf8")]
417impl IoExt for cap_std::fs_utf8::File {
418    #[inline]
419    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
420        self.as_filelike_view::<std::fs::File>().read(buf)
421    }
422
423    #[inline]
424    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
425        self.as_filelike_view::<std::fs::File>().read_exact(buf)
426    }
427
428    #[inline]
429    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
430        self.as_filelike_view::<std::fs::File>().read_vectored(bufs)
431    }
432
433    #[inline]
434    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
435        self.as_filelike_view::<std::fs::File>().read_to_end(buf)
436    }
437
438    #[inline]
439    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
440        self.as_filelike_view::<std::fs::File>().read_to_string(buf)
441    }
442
443    #[inline]
444    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
445        self.as_filelike_view::<std::fs::File>().peek(buf)
446    }
447
448    #[inline]
449    fn write(&self, buf: &[u8]) -> io::Result<usize> {
450        self.as_filelike_view::<std::fs::File>().write(buf)
451    }
452
453    #[inline]
454    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
455        self.as_filelike_view::<std::fs::File>().write_all(buf)
456    }
457
458    #[inline]
459    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
460        self.as_filelike_view::<std::fs::File>()
461            .write_vectored(bufs)
462    }
463
464    #[inline]
465    fn flush(&self) -> io::Result<()> {
466        self.as_filelike_view::<std::fs::File>().flush()
467    }
468
469    #[inline]
470    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
471        self.as_filelike_view::<std::fs::File>().write_fmt(fmt)
472    }
473}
474
475#[cfg(windows)]
476impl IoExt for std::net::TcpStream {
477    #[inline]
478    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
479        Read::read(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
480    }
481
482    #[inline]
483    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
484        Read::read_exact(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
485    }
486
487    #[inline]
488    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
489        Read::read_vectored(
490            &mut &*self.as_socketlike_view::<std::net::TcpStream>(),
491            bufs,
492        )
493    }
494
495    #[inline]
496    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
497        Read::read_to_end(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
498    }
499
500    #[inline]
501    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
502        Read::read_to_string(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
503    }
504
505    #[inline]
506    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
507        self.as_socketlike_view::<std::net::TcpStream>().peek(buf)
508    }
509
510    #[inline]
511    fn write(&self, buf: &[u8]) -> io::Result<usize> {
512        Write::write(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
513    }
514
515    #[inline]
516    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
517        Write::write_all(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), buf)
518    }
519
520    #[inline]
521    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
522        Write::write_vectored(
523            &mut &*self.as_socketlike_view::<std::net::TcpStream>(),
524            bufs,
525        )
526    }
527
528    #[inline]
529    fn flush(&self) -> io::Result<()> {
530        Write::flush(&mut &*self.as_socketlike_view::<std::net::TcpStream>())
531    }
532
533    #[inline]
534    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
535        Write::write_fmt(&mut &*self.as_socketlike_view::<std::net::TcpStream>(), fmt)
536    }
537}
538
539#[cfg(windows)]
540#[cfg(feature = "cap_std_impls")]
541impl IoExt for cap_std::net::TcpStream {
542    #[inline]
543    fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
544        self.as_socketlike_view::<std::net::TcpStream>().read(buf)
545    }
546
547    #[inline]
548    fn read_exact(&self, buf: &mut [u8]) -> io::Result<()> {
549        self.as_socketlike_view::<std::net::TcpStream>()
550            .read_exact(buf)
551    }
552
553    #[inline]
554    fn read_vectored(&self, bufs: &mut [IoSliceMut]) -> io::Result<usize> {
555        self.as_socketlike_view::<std::net::TcpStream>()
556            .read_vectored(bufs)
557    }
558
559    #[inline]
560    fn read_to_end(&self, buf: &mut Vec<u8>) -> io::Result<usize> {
561        self.as_socketlike_view::<std::net::TcpStream>()
562            .read_to_end(buf)
563    }
564
565    #[inline]
566    fn read_to_string(&self, buf: &mut String) -> io::Result<usize> {
567        self.as_socketlike_view::<std::net::TcpStream>()
568            .read_to_string(buf)
569    }
570
571    #[inline]
572    fn peek(&self, buf: &mut [u8]) -> io::Result<usize> {
573        self.as_socketlike_view::<std::net::TcpStream>().peek(buf)
574    }
575
576    #[inline]
577    fn write(&self, buf: &[u8]) -> io::Result<usize> {
578        self.as_socketlike_view::<std::net::TcpStream>().write(buf)
579    }
580
581    #[inline]
582    fn write_all(&self, buf: &[u8]) -> io::Result<()> {
583        self.as_socketlike_view::<std::net::TcpStream>()
584            .write_all(buf)
585    }
586
587    #[inline]
588    fn write_vectored(&self, bufs: &[IoSlice]) -> io::Result<usize> {
589        self.as_socketlike_view::<std::net::TcpStream>()
590            .write_vectored(bufs)
591    }
592
593    #[inline]
594    fn flush(&self) -> io::Result<()> {
595        self.as_socketlike_view::<std::net::TcpStream>().flush()
596    }
597
598    #[inline]
599    fn write_fmt(&self, fmt: Arguments) -> io::Result<()> {
600        self.as_socketlike_view::<std::net::TcpStream>()
601            .write_fmt(fmt)
602    }
603}
604
605fn _io_ext_can_be_trait_object(_: &dyn IoExt) {}