Skip to content

Commit d86cf13

Browse files
committed
Auto merge of #38062 - alexcrichton:fix-line-writer, r=brson
std: Fix partial writes in LineWriter Previously the `LineWriter` could successfully write some bytes but then fail to report that it has done so. Additionally, an erroneous flush after a successful write was permanently ignored. This commit fixes these two issues by (a) maintaining a `need_flush` flag to indicate whether a flush should be the first operation in `LineWriter::write` and (b) avoiding returning an error once some bytes have been successfully written. Closes #37807
2 parents 8d65c8d + ecc6010 commit d86cf13

File tree

1 file changed

+86
-13
lines changed

1 file changed

+86
-13
lines changed

src/libstd/io/buffered.rs

+86-13
Original file line numberDiff line numberDiff line change
@@ -652,6 +652,7 @@ impl<W> fmt::Display for IntoInnerError<W> {
652652
#[stable(feature = "rust1", since = "1.0.0")]
653653
pub struct LineWriter<W: Write> {
654654
inner: BufWriter<W>,
655+
need_flush: bool,
655656
}
656657

657658
impl<W: Write> LineWriter<W> {
@@ -692,7 +693,10 @@ impl<W: Write> LineWriter<W> {
692693
/// ```
693694
#[stable(feature = "rust1", since = "1.0.0")]
694695
pub fn with_capacity(cap: usize, inner: W) -> LineWriter<W> {
695-
LineWriter { inner: BufWriter::with_capacity(cap, inner) }
696+
LineWriter {
697+
inner: BufWriter::with_capacity(cap, inner),
698+
need_flush: false,
699+
}
696700
}
697701

698702
/// Gets a reference to the underlying writer.
@@ -759,28 +763,57 @@ impl<W: Write> LineWriter<W> {
759763
#[stable(feature = "rust1", since = "1.0.0")]
760764
pub fn into_inner(self) -> Result<W, IntoInnerError<LineWriter<W>>> {
761765
self.inner.into_inner().map_err(|IntoInnerError(buf, e)| {
762-
IntoInnerError(LineWriter { inner: buf }, e)
766+
IntoInnerError(LineWriter {
767+
inner: buf,
768+
need_flush: false,
769+
}, e)
763770
})
764771
}
765772
}
766773

767774
#[stable(feature = "rust1", since = "1.0.0")]
768775
impl<W: Write> Write for LineWriter<W> {
769776
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
770-
match memchr::memrchr(b'\n', buf) {
771-
Some(i) => {
772-
let n = self.inner.write(&buf[..i + 1])?;
773-
if n != i + 1 || self.inner.flush().is_err() {
774-
// Do not return errors on partial writes.
775-
return Ok(n);
776-
}
777-
self.inner.write(&buf[i + 1..]).map(|i| n + i)
778-
}
779-
None => self.inner.write(buf),
777+
if self.need_flush {
778+
self.flush()?;
779+
}
780+
781+
// Find the last newline character in the buffer provided. If found then
782+
// we're going to write all the data up to that point and then flush,
783+
// otherewise we just write the whole block to the underlying writer.
784+
let i = match memchr::memrchr(b'\n', buf) {
785+
Some(i) => i,
786+
None => return self.inner.write(buf),
787+
};
788+
789+
790+
// Ok, we're going to write a partial amount of the data given first
791+
// followed by flushing the newline. After we've successfully written
792+
// some data then we *must* report that we wrote that data, so future
793+
// errors are ignored. We set our internal `need_flush` flag, though, in
794+
// case flushing fails and we need to try it first next time.
795+
let n = self.inner.write(&buf[..i + 1])?;
796+
self.need_flush = true;
797+
if self.flush().is_err() || n != i + 1 {
798+
return Ok(n)
799+
}
800+
801+
// At this point we successfully wrote `i + 1` bytes and flushed it out,
802+
// meaning that the entire line is now flushed out on the screen. While
803+
// we can attempt to finish writing the rest of the data provided.
804+
// Remember though that we ignore errors here as we've successfully
805+
// written data, so we need to report that.
806+
match self.inner.write(&buf[i + 1..]) {
807+
Ok(i) => Ok(n + i),
808+
Err(_) => Ok(n),
780809
}
781810
}
782811

783-
fn flush(&mut self) -> io::Result<()> { self.inner.flush() }
812+
fn flush(&mut self) -> io::Result<()> {
813+
self.inner.flush()?;
814+
self.need_flush = false;
815+
Ok(())
816+
}
784817
}
785818

786819
#[stable(feature = "rust1", since = "1.0.0")]
@@ -1153,4 +1186,44 @@ mod tests {
11531186
BufWriter::new(io::sink())
11541187
});
11551188
}
1189+
1190+
struct AcceptOneThenFail {
1191+
written: bool,
1192+
flushed: bool,
1193+
}
1194+
1195+
impl Write for AcceptOneThenFail {
1196+
fn write(&mut self, data: &[u8]) -> io::Result<usize> {
1197+
if !self.written {
1198+
assert_eq!(data, b"a\nb\n");
1199+
self.written = true;
1200+
Ok(data.len())
1201+
} else {
1202+
Err(io::Error::new(io::ErrorKind::NotFound, "test"))
1203+
}
1204+
}
1205+
1206+
fn flush(&mut self) -> io::Result<()> {
1207+
assert!(self.written);
1208+
assert!(!self.flushed);
1209+
self.flushed = true;
1210+
Err(io::Error::new(io::ErrorKind::Other, "test"))
1211+
}
1212+
}
1213+
1214+
#[test]
1215+
fn erroneous_flush_retried() {
1216+
let a = AcceptOneThenFail {
1217+
written: false,
1218+
flushed: false,
1219+
};
1220+
1221+
let mut l = LineWriter::new(a);
1222+
assert_eq!(l.write(b"a\nb\na").unwrap(), 4);
1223+
assert!(l.get_ref().written);
1224+
assert!(l.get_ref().flushed);
1225+
l.get_mut().flushed = false;
1226+
1227+
assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other)
1228+
}
11561229
}

0 commit comments

Comments
 (0)