1- use crate :: io :: { self , ErrorKind , Read , Write } ;
1+ use super :: { BufWriter , ErrorKind , Read , Result , Write , DEFAULT_BUF_SIZE } ;
22use crate :: mem:: MaybeUninit ;
33
44/// Copies the entire contents of a reader into a writer.
@@ -40,7 +40,7 @@ use crate::mem::MaybeUninit;
4040/// }
4141/// ```
4242#[ stable( feature = "rust1" , since = "1.0.0" ) ]
43- pub fn copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> io :: Result < u64 >
43+ pub fn copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> Result < u64 >
4444where
4545 R : Read ,
4646 W : Write ,
@@ -54,14 +54,82 @@ where
5454 }
5555}
5656
57- /// The general read-write-loop implementation of
58- /// `io::copy` that is used when specializations are not available or not applicable.
59- pub ( crate ) fn generic_copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> io :: Result < u64 >
57+ /// The userspace read-write-loop implementation of `io::copy` that is used when
58+ /// OS-specific specializations for copy offloading are not available or not applicable.
59+ pub ( crate ) fn generic_copy < R : ?Sized , W : ?Sized > ( reader : & mut R , writer : & mut W ) -> Result < u64 >
6060where
6161 R : Read ,
6262 W : Write ,
6363{
64- let mut buf = MaybeUninit :: < [ u8 ; super :: DEFAULT_BUF_SIZE ] > :: uninit ( ) ;
64+ BufferedCopySpec :: copy_to ( reader, writer)
65+ }
66+
67+ /// Specialization of the read-write loop that either uses a stack buffer
68+ /// or reuses the internal buffer of a BufWriter
69+ trait BufferedCopySpec : Write {
70+ fn copy_to < R : Read + ?Sized > ( reader : & mut R , writer : & mut Self ) -> Result < u64 > ;
71+ }
72+
73+ impl < W : Write + ?Sized > BufferedCopySpec for W {
74+ default fn copy_to < R : Read + ?Sized > ( reader : & mut R , writer : & mut Self ) -> Result < u64 > {
75+ stack_buffer_copy ( reader, writer)
76+ }
77+ }
78+
79+ impl < I : Write > BufferedCopySpec for BufWriter < I > {
80+ fn copy_to < R : Read + ?Sized > ( reader : & mut R , writer : & mut Self ) -> Result < u64 > {
81+ if writer. capacity ( ) < DEFAULT_BUF_SIZE {
82+ return stack_buffer_copy ( reader, writer) ;
83+ }
84+
85+ // FIXME: #42788
86+ //
87+ // - This creates a (mut) reference to a slice of
88+ // _uninitialized_ integers, which is **undefined behavior**
89+ //
90+ // - Only the standard library gets to soundly "ignore" this,
91+ // based on its privileged knowledge of unstable rustc
92+ // internals;
93+ unsafe {
94+ let spare_cap = writer. buffer_mut ( ) . spare_capacity_mut ( ) ;
95+ reader. initializer ( ) . initialize ( MaybeUninit :: slice_assume_init_mut ( spare_cap) ) ;
96+ }
97+
98+ let mut len = 0 ;
99+
100+ loop {
101+ let buf = writer. buffer_mut ( ) ;
102+ let spare_cap = buf. spare_capacity_mut ( ) ;
103+
104+ if spare_cap. len ( ) >= DEFAULT_BUF_SIZE {
105+ match reader. read ( unsafe { MaybeUninit :: slice_assume_init_mut ( spare_cap) } ) {
106+ Ok ( 0 ) => return Ok ( len) , // EOF reached
107+ Ok ( bytes_read) => {
108+ assert ! ( bytes_read <= spare_cap. len( ) ) ;
109+ // Safety: The initializer contract guarantees that either it or `read`
110+ // will have initialized these bytes. And we just checked that the number
111+ // of bytes is within the buffer capacity.
112+ unsafe { buf. set_len ( buf. len ( ) + bytes_read) } ;
113+ len += bytes_read as u64 ;
114+ // Read again if the buffer still has enough capacity, as BufWriter itself would do
115+ // This will occur if the reader returns short reads
116+ continue ;
117+ }
118+ Err ( ref e) if e. kind ( ) == ErrorKind :: Interrupted => continue ,
119+ Err ( e) => return Err ( e) ,
120+ }
121+ }
122+
123+ writer. flush_buf ( ) ?;
124+ }
125+ }
126+ }
127+
128+ fn stack_buffer_copy < R : Read + ?Sized , W : Write + ?Sized > (
129+ reader : & mut R ,
130+ writer : & mut W ,
131+ ) -> Result < u64 > {
132+ let mut buf = MaybeUninit :: < [ u8 ; DEFAULT_BUF_SIZE ] > :: uninit ( ) ;
65133 // FIXME: #42788
66134 //
67135 // - This creates a (mut) reference to a slice of
0 commit comments