1
- use crate :: io :: { self , ErrorKind , Read , Write } ;
1
+ use super :: { BufWriter , ErrorKind , Read , Result , Write , DEFAULT_BUF_SIZE } ;
2
2
use crate :: mem:: MaybeUninit ;
3
3
4
4
/// Copies the entire contents of a reader into a writer.
@@ -40,7 +40,7 @@ use crate::mem::MaybeUninit;
40
40
/// }
41
41
/// ```
42
42
#[ 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 >
44
44
where
45
45
R : Read ,
46
46
W : Write ,
@@ -54,14 +54,82 @@ where
54
54
}
55
55
}
56
56
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 >
60
60
where
61
61
R : Read ,
62
62
W : Write ,
63
63
{
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 ( ) ;
65
133
// FIXME: #42788
66
134
//
67
135
// - This creates a (mut) reference to a slice of
0 commit comments