@@ -527,7 +527,8 @@ impl<W: Write> BufWriter<W> {
527527 /// errors from this method.
528528 fn flush_buf ( & mut self ) -> io:: Result < ( ) > {
529529 /// Helper struct to ensure the buffer is updated after all the writes
530- /// are complete
530+ /// are complete. It tracks the number of written bytes and drains them
531+ /// all from the front of the buffer when dropped.
531532 struct BufGuard < ' a > {
532533 buffer : & ' a mut Vec < u8 > ,
533534 written : usize ,
@@ -942,12 +943,13 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
942943 /// success, the remaining data will be buffered.
943944 ///
944945 /// Because this function attempts to send completed lines to the underlying
945- /// writer, it will also flush the existing buffer if it contains any
946- /// newlines , even if the incoming data does not contain any newlines.
946+ /// writer, it will also flush the existing buffer if it ends with a
947+ /// newline , even if the incoming data does not contain any newlines.
947948 fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
948949 let newline_idx = match memchr:: memrchr ( b'\n' , buf) {
949950 // If there are no new newlines (that is, if this write is less than
950- // one line), just do a regular buffered write
951+ // one line), just do a regular buffered write (which may flush if
952+ // we exceed the inner buffer's size)
951953 None => {
952954 self . flush_if_completed_line ( ) ?;
953955 return self . buffer . write ( buf) ;
@@ -957,7 +959,11 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
957959 Some ( newline_idx) => newline_idx + 1 ,
958960 } ;
959961
960- // Flush existing content to prepare for our write
962+ // Flush existing content to prepare for our write. We have to do this
963+ // before attempting to write `buf` in order to maintain consistency;
964+ // if we add `buf` to the buffer then try to flush it all at once,
965+ // we're obligated to return Ok(), which would mean supressing any
966+ // errors that occur during flush.
961967 self . buffer . flush_buf ( ) ?;
962968
963969 // This is what we're going to try to write directly to the inner
@@ -1121,32 +1127,33 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
11211127 /// writer, it will also flush the existing buffer if it contains any
11221128 /// newlines, even if the incoming data does not contain any newlines.
11231129 fn write_all ( & mut self , buf : & [ u8 ] ) -> io:: Result < ( ) > {
1124- let newline_idx = match memchr:: memrchr ( b'\n' , buf) {
1130+ match memchr:: memrchr ( b'\n' , buf) {
11251131 // If there are no new newlines (that is, if this write is less than
1126- // one line), just do a regular buffered write
1132+ // one line), just do a regular buffered write (which may flush if
1133+ // we exceed the inner buffer's size)
11271134 None => {
11281135 self . flush_if_completed_line ( ) ?;
1129- return self . buffer . write_all ( buf) ;
1136+ self . buffer . write_all ( buf)
11301137 }
1131- // Otherwise, arrange for the lines to be written directly to the
1132- // inner writer.
1133- Some ( newline_idx) => newline_idx,
1134- } ;
1135-
1136- // Flush existing content to prepare for our write
1137- self . buffer . flush_buf ( ) ?;
1138+ Some ( newline_idx) => {
1139+ let ( lines, tail) = buf. split_at ( newline_idx + 1 ) ;
11381140
1139- // This is what we're going to try to write directly to the inner
1140- // writer. The rest will be buffered, if nothing goes wrong.
1141- let ( lines, tail) = buf. split_at ( newline_idx + 1 ) ;
1142-
1143- // Write `lines` directly to the inner writer, bypassing the buffer.
1144- self . inner_mut ( ) . write_all ( lines) ?;
1141+ if self . buffered ( ) . is_empty ( ) {
1142+ self . inner_mut ( ) . write_all ( lines) ?;
1143+ } else {
1144+ // If there is any buffered data, we add the incoming lines
1145+ // to that buffer before flushing, which saves us at lease
1146+ // one write call. We can't really do this with `write`,
1147+ // since we can't do this *and* not suppress errors *and*
1148+ // report a consistent state to the caller in a return
1149+ // value, but here in write_all it's fine.
1150+ self . buffer . write_all ( lines) ?;
1151+ self . buffer . flush_buf ( ) ?;
1152+ }
11451153
1146- // Now that the write has succeeded, buffer the rest with
1147- // BufWriter::write_all. This will buffer as much as possible, but
1148- // continue flushing as necessary if our tail is huge.
1149- self . buffer . write_all ( tail)
1154+ self . buffer . write_all ( tail)
1155+ }
1156+ }
11501157 }
11511158}
11521159
@@ -1401,7 +1408,11 @@ mod tests {
14011408 // rustfmt-on-save.
14021409 impl Read for ShortReader {
14031410 fn read ( & mut self , _: & mut [ u8 ] ) -> io:: Result < usize > {
1404- if self . lengths . is_empty ( ) { Ok ( 0 ) } else { Ok ( self . lengths . remove ( 0 ) ) }
1411+ if self . lengths . is_empty ( ) {
1412+ Ok ( 0 )
1413+ } else {
1414+ Ok ( self . lengths . remove ( 0 ) )
1415+ }
14051416 }
14061417 }
14071418
@@ -2260,4 +2271,46 @@ mod tests {
22602271 writer. flush ( ) . unwrap ( ) ;
22612272 assert_eq ! ( writer. get_ref( ) . buffer, * b"AAAAABBBBB" ) ;
22622273 }
2274+
2275+ #[ derive( Debug , Clone , PartialEq , Eq ) ]
2276+ enum RecordedEvent {
2277+ Write ( String ) ,
2278+ Flush ,
2279+ }
2280+
2281+ #[ derive( Debug , Clone , Default ) ]
2282+ struct WriteRecorder {
2283+ pub events : Vec < RecordedEvent > ,
2284+ }
2285+
2286+ impl Write for WriteRecorder {
2287+ fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
2288+ use crate :: str:: from_utf8;
2289+
2290+ self . events . push ( RecordedEvent :: Write ( from_utf8 ( buf) . unwrap ( ) . to_string ( ) ) ) ;
2291+ Ok ( buf. len ( ) )
2292+ }
2293+
2294+ fn flush ( & mut self ) -> io:: Result < ( ) > {
2295+ self . events . push ( RecordedEvent :: Flush ) ;
2296+ Ok ( ( ) )
2297+ }
2298+ }
2299+
2300+ /// Test that a normal, formatted writeln only results in a single write
2301+ /// call to the underlying writer. A naive implementaton of
2302+ /// LineWriter::write_all results in two writes: one of the buffered data,
2303+ /// and another of the final substring in the formatted set
2304+ #[ test]
2305+ fn single_formatted_write ( ) {
2306+ let writer = WriteRecorder :: default ( ) ;
2307+ let mut writer = LineWriter :: new ( writer) ;
2308+
2309+ // Under a naive implementation of LineWriter, this will result in two
2310+ // writes: "hello, world" and "!\n", because write() has to flush the
2311+ // buffer before attempting to write the last "!\n". write_all shouldn't
2312+ // have this limitation.
2313+ writeln ! ( & mut writer, "{}, {}!" , "hello" , "world" ) . unwrap ( ) ;
2314+ assert_eq ! ( writer. get_ref( ) . events, [ RecordedEvent :: Write ( "hello, world!\n " . to_string( ) ) ] ) ;
2315+ }
22632316}
0 commit comments