@@ -527,7 +527,8 @@ impl<W: Write> BufWriter<W> {
527
527
/// errors from this method.
528
528
fn flush_buf ( & mut self ) -> io:: Result < ( ) > {
529
529
/// 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.
531
532
struct BufGuard < ' a > {
532
533
buffer : & ' a mut Vec < u8 > ,
533
534
written : usize ,
@@ -942,12 +943,13 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
942
943
/// success, the remaining data will be buffered.
943
944
///
944
945
/// 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.
947
948
fn write ( & mut self , buf : & [ u8 ] ) -> io:: Result < usize > {
948
949
let newline_idx = match memchr:: memrchr ( b'\n' , buf) {
949
950
// 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)
951
953
None => {
952
954
self . flush_if_completed_line ( ) ?;
953
955
return self . buffer . write ( buf) ;
@@ -957,7 +959,11 @@ impl<'a, W: Write> Write for LineWriterShim<'a, W> {
957
959
Some ( newline_idx) => newline_idx + 1 ,
958
960
} ;
959
961
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.
961
967
self . buffer . flush_buf ( ) ?;
962
968
963
969
// 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> {
1121
1127
/// writer, it will also flush the existing buffer if it contains any
1122
1128
/// newlines, even if the incoming data does not contain any newlines.
1123
1129
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) {
1125
1131
// 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)
1127
1134
None => {
1128
1135
self . flush_if_completed_line ( ) ?;
1129
- return self . buffer . write_all ( buf) ;
1136
+ self . buffer . write_all ( buf)
1130
1137
}
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 ) ;
1138
1140
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
+ }
1145
1153
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
+ }
1150
1157
}
1151
1158
}
1152
1159
@@ -1401,7 +1408,11 @@ mod tests {
1401
1408
// rustfmt-on-save.
1402
1409
impl Read for ShortReader {
1403
1410
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
+ }
1405
1416
}
1406
1417
}
1407
1418
@@ -2260,4 +2271,46 @@ mod tests {
2260
2271
writer. flush ( ) . unwrap ( ) ;
2261
2272
assert_eq ! ( writer. get_ref( ) . buffer, * b"AAAAABBBBB" ) ;
2262
2273
}
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
+ }
2263
2316
}
0 commit comments