diff options
| author | bors <bors@rust-lang.org> | 2024-12-31 13:21:27 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2024-12-31 13:21:27 +0000 |
| commit | 7a0cde96f83c6d38237bb8062df6300ecf4c2687 (patch) | |
| tree | 31492a1ca4f6c02f24e10def3500e03a8638524a /library | |
| parent | aea4e4370330388bf2906b79769d43e581d487b4 (diff) | |
| parent | fdb43ef0c456514d634741e46a1f4eba454f1a84 (diff) | |
| download | rust-7a0cde96f83c6d38237bb8062df6300ecf4c2687.tar.gz rust-7a0cde96f83c6d38237bb8062df6300ecf4c2687.zip | |
Auto merge of #134620 - ChrisDenton:line-writer, r=tgross35
Avoid short writes in LineWriter If the bytes written to `LineWriter` contains at least one new line but doesn't end in a new line (e.g. `"abc\ndef"`) then we: - write up to the last new line direct to the underlying `Writer`. - copy as many of the remaining bytes as will fit into our internal buffer. That last step is inefficient if the remaining bytes are larger than our buffer. It will needlessly split the bytes in two, requiring at least two writes to the underlying `Writer` (one to flush the buffer, one more to write the rest). This PR skips the extra buffering if the remaining bytes are larger than the buffer.
Diffstat (limited to 'library')
| -rw-r--r-- | library/std/src/io/buffered/linewritershim.rs | 9 | ||||
| -rw-r--r-- | library/std/src/io/buffered/tests.rs | 18 |
2 files changed, 21 insertions, 6 deletions
diff --git a/library/std/src/io/buffered/linewritershim.rs b/library/std/src/io/buffered/linewritershim.rs index 3d04ccd1c7d..5ebeada59bb 100644 --- a/library/std/src/io/buffered/linewritershim.rs +++ b/library/std/src/io/buffered/linewritershim.rs @@ -119,7 +119,14 @@ impl<'a, W: ?Sized + Write> Write for LineWriterShim<'a, W> { // the buffer? // - If not, scan for the last newline that *does* fit in the buffer let tail = if flushed >= newline_idx { - &buf[flushed..] + let tail = &buf[flushed..]; + // Avoid unnecessary short writes by not splitting the remaining + // bytes if they're larger than the buffer. + // They can be written in full by the next call to write. + if tail.len() >= self.buffer.capacity() { + return Ok(flushed); + } + tail } else if newline_idx - flushed <= self.buffer.capacity() { &buf[flushed..newline_idx] } else { diff --git a/library/std/src/io/buffered/tests.rs b/library/std/src/io/buffered/tests.rs index bff0f823c4b..17f6107aa03 100644 --- a/library/std/src/io/buffered/tests.rs +++ b/library/std/src/io/buffered/tests.rs @@ -847,8 +847,7 @@ fn long_line_flushed() { } /// Test that, given a very long partial line *after* successfully -/// flushing a complete line, the very long partial line is buffered -/// unconditionally, and no additional writes take place. This assures +/// flushing a complete line, no additional writes take place. This assures /// the property that `write` should make at-most-one attempt to write /// new data. #[test] @@ -856,13 +855,22 @@ fn line_long_tail_not_flushed() { let writer = ProgrammableSink::default(); let mut writer = LineWriter::with_capacity(5, writer); - // Assert that Line 1\n is flushed, and 01234 is buffered - assert_eq!(writer.write(b"Line 1\n0123456789").unwrap(), 12); + // Assert that Line 1\n is flushed and the long tail isn't. + let bytes = b"Line 1\n0123456789"; + writer.write(bytes).unwrap(); assert_eq!(&writer.get_ref().buffer, b"Line 1\n"); +} + +// Test that appending to a full buffer emits a single write, flushing the buffer. +#[test] +fn line_full_buffer_flushed() { + let writer = ProgrammableSink::default(); + let mut writer = LineWriter::with_capacity(5, writer); + assert_eq!(writer.write(b"01234").unwrap(), 5); // Because the buffer is full, this subsequent write will flush it assert_eq!(writer.write(b"5").unwrap(), 1); - assert_eq!(&writer.get_ref().buffer, b"Line 1\n01234"); + assert_eq!(&writer.get_ref().buffer, b"01234"); } /// Test that, if an attempt to pre-flush buffered data returns Ok(0), |
