Skip to content

Commit 2fee28e

Browse files
committed
std: Implement LineWriter::write_vectored
This commit implements the `write_vectored` method of the `LineWriter` type. First discovered in bytecodealliance/wasmtime#629 the `write_vectored` method of `Stdout` bottoms out here but only ends up writing the first buffer due to the default implementation of `write_vectored`. Like `BufWriter`, however, `LineWriter` can have a non-default implementation of `write_vectored` which tries to preserve the vectored-ness as much as possible. Namely we can have a vectored write for everything before the newline and everything after the newline if all the stars align well. Also like `BufWriter`, though, special care is taken to ensure that whenever bytes are written we're sure to signal success since that represents a "commit" of writing bytes.
1 parent e9469a6 commit 2fee28e

File tree

1 file changed

+171
-1
lines changed

1 file changed

+171
-1
lines changed

src/libstd/io/buffered.rs

+171-1
Original file line numberDiff line numberDiff line change
@@ -989,6 +989,68 @@ impl<W: Write> Write for LineWriter<W> {
989989
}
990990
}
991991

992+
// Vectored writes are very similar to the writes above, but adjusted for
993+
// the list of buffers that we have to write.
994+
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
995+
if self.need_flush {
996+
self.flush()?;
997+
}
998+
999+
// Find the last newline, and failing that write the whole buffer
1000+
let last_newline = bufs
1001+
.iter()
1002+
.enumerate()
1003+
.rev()
1004+
.filter_map(|(i, buf)| {
1005+
let pos = memchr::memrchr(b'\n', buf)?;
1006+
Some((i, pos))
1007+
})
1008+
.next();
1009+
let (i, j) = match last_newline {
1010+
Some(pair) => pair,
1011+
None => return self.inner.write_vectored(bufs),
1012+
};
1013+
let (prefix, suffix) = bufs.split_at(i);
1014+
let (buf, suffix) = suffix.split_at(1);
1015+
let buf = &buf[0];
1016+
1017+
// Write everything up to the last newline, flushing afterwards. Note
1018+
// that only if we finished our entire `write_vectored` do we try the
1019+
// subsequent
1020+
// `write`
1021+
let mut n = 0;
1022+
let prefix_amt = prefix.iter().map(|i| i.len()).sum();
1023+
if prefix_amt > 0 {
1024+
n += self.inner.write_vectored(prefix)?;
1025+
self.need_flush = true;
1026+
}
1027+
if n == prefix_amt {
1028+
match self.inner.write(&buf[..=j]) {
1029+
Ok(m) => n += m,
1030+
Err(e) if n == 0 => return Err(e),
1031+
Err(_) => return Ok(n),
1032+
}
1033+
self.need_flush = true;
1034+
}
1035+
if self.flush().is_err() || n != j + 1 + prefix_amt {
1036+
return Ok(n);
1037+
}
1038+
1039+
// ... and now write out everything remaining
1040+
match self.inner.write(&buf[j + 1..]) {
1041+
Ok(i) => n += i,
1042+
Err(_) => return Ok(n),
1043+
}
1044+
1045+
if suffix.iter().map(|s| s.len()).sum::<usize>() == 0 {
1046+
return Ok(n)
1047+
}
1048+
match self.inner.write_vectored(suffix) {
1049+
Ok(i) => Ok(n + i),
1050+
Err(_) => Ok(n),
1051+
}
1052+
}
1053+
9921054
fn flush(&mut self) -> io::Result<()> {
9931055
self.inner.flush()?;
9941056
self.need_flush = false;
@@ -1015,7 +1077,7 @@ where
10151077
#[cfg(test)]
10161078
mod tests {
10171079
use crate::io::prelude::*;
1018-
use crate::io::{self, BufReader, BufWriter, LineWriter, SeekFrom};
1080+
use crate::io::{self, BufReader, BufWriter, LineWriter, SeekFrom, IoSlice};
10191081
use crate::sync::atomic::{AtomicUsize, Ordering};
10201082
use crate::thread;
10211083

@@ -1483,4 +1545,112 @@ mod tests {
14831545

14841546
assert_eq!(l.write(b"a").unwrap_err().kind(), io::ErrorKind::Other)
14851547
}
1548+
1549+
#[test]
1550+
fn line_vectored() {
1551+
let mut a = LineWriter::new(Vec::new());
1552+
assert_eq!(
1553+
a.write_vectored(&[
1554+
IoSlice::new(&[]),
1555+
IoSlice::new(b"\n"),
1556+
IoSlice::new(&[]),
1557+
IoSlice::new(b"a"),
1558+
])
1559+
.unwrap(),
1560+
2,
1561+
);
1562+
assert_eq!(a.get_ref(), b"\n");
1563+
1564+
assert_eq!(
1565+
a.write_vectored(&[
1566+
IoSlice::new(&[]),
1567+
IoSlice::new(b"b"),
1568+
IoSlice::new(&[]),
1569+
IoSlice::new(b"a"),
1570+
IoSlice::new(&[]),
1571+
IoSlice::new(b"c"),
1572+
])
1573+
.unwrap(),
1574+
3,
1575+
);
1576+
assert_eq!(a.get_ref(), b"\n");
1577+
a.flush().unwrap();
1578+
assert_eq!(a.get_ref(), b"\nabac");
1579+
assert_eq!(a.write_vectored(&[]).unwrap(), 0);
1580+
assert_eq!(
1581+
a.write_vectored(&[
1582+
IoSlice::new(&[]),
1583+
IoSlice::new(&[]),
1584+
IoSlice::new(&[]),
1585+
IoSlice::new(&[]),
1586+
])
1587+
.unwrap(),
1588+
0,
1589+
);
1590+
assert_eq!(a.write_vectored(&[IoSlice::new(b"a\nb"),]).unwrap(), 3);
1591+
assert_eq!(a.get_ref(), b"\nabaca\n");
1592+
}
1593+
1594+
#[test]
1595+
fn line_vectored_partial_and_errors() {
1596+
enum Call {
1597+
Write { inputs: Vec<&'static [u8]>, output: io::Result<usize> },
1598+
Flush { output: io::Result<()> },
1599+
}
1600+
struct Writer {
1601+
calls: Vec<Call>,
1602+
}
1603+
1604+
impl Write for Writer {
1605+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
1606+
self.write_vectored(&[IoSlice::new(buf)])
1607+
}
1608+
1609+
fn write_vectored(&mut self, buf: &[IoSlice<'_>]) -> io::Result<usize> {
1610+
match self.calls.pop().unwrap() {
1611+
Call::Write { inputs, output } => {
1612+
assert_eq!(inputs, buf.iter().map(|b| &**b).collect::<Vec<_>>());
1613+
output
1614+
}
1615+
_ => panic!("unexpected call to write"),
1616+
}
1617+
}
1618+
1619+
fn flush(&mut self) -> io::Result<()> {
1620+
match self.calls.pop().unwrap() {
1621+
Call::Flush { output } => output,
1622+
_ => panic!("unexpected call to flush"),
1623+
}
1624+
}
1625+
}
1626+
1627+
impl Drop for Writer {
1628+
fn drop(&mut self) {
1629+
if !thread::panicking() {
1630+
assert_eq!(self.calls.len(), 0);
1631+
}
1632+
}
1633+
}
1634+
1635+
// partial writes keep going
1636+
let mut a = LineWriter::new(Writer { calls: Vec::new() });
1637+
a.write_vectored(&[IoSlice::new(&[]), IoSlice::new(b"abc")]).unwrap();
1638+
a.get_mut().calls.push(Call::Flush { output: Ok(()) });
1639+
a.get_mut().calls.push(Call::Write { inputs: vec![b"bcx\n"], output: Ok(4) });
1640+
a.get_mut().calls.push(Call::Write { inputs: vec![b"abcx\n"], output: Ok(1) });
1641+
a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\n")]).unwrap();
1642+
a.get_mut().calls.push(Call::Flush { output: Ok(()) });
1643+
a.flush().unwrap();
1644+
1645+
// erroneous writes stop and don't write more
1646+
a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Err(err()) });
1647+
assert_eq!(a.write_vectored(&[IoSlice::new(b"x"), IoSlice::new(b"\na")]).unwrap(), 2);
1648+
a.get_mut().calls.push(Call::Flush { output: Ok(()) });
1649+
a.get_mut().calls.push(Call::Write { inputs: vec![b"x\n"], output: Ok(2) });
1650+
a.flush().unwrap();
1651+
1652+
fn err() -> io::Error {
1653+
io::Error::new(io::ErrorKind::Other, "x")
1654+
}
1655+
}
14861656
}

0 commit comments

Comments
 (0)