use super::buf_duplexer::BufDuplexerBackend;
use duplex::HalfDuplex;
use std::io::{self, IoSlice, Write};
#[derive(Debug)]
pub(super) struct BufReaderLineWriterShim<'a, Inner: HalfDuplex> {
buffer: &'a mut BufDuplexerBackend<Inner>,
}
impl<'a, Inner: HalfDuplex> BufReaderLineWriterShim<'a, Inner> {
#[inline]
pub fn new(buffer: &'a mut BufDuplexerBackend<Inner>) -> Self {
Self { buffer }
}
#[inline]
fn inner_mut(&mut self) -> &mut Inner {
self.buffer.get_mut()
}
#[inline]
fn writer_buffered(&self) -> &[u8] {
self.buffer.writer_buffer()
}
fn flush_if_completed_line(&mut self) -> io::Result<()> {
match self.writer_buffered().last().copied() {
Some(b'\n') => self.buffer.flush_buf(),
_ => Ok(()),
}
}
}
impl<'a, Inner: HalfDuplex> Write for BufReaderLineWriterShim<'a, Inner> {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let newline_idx = match memchr::memrchr(b'\n', buf) {
None => {
self.flush_if_completed_line()?;
return self.buffer.write(buf);
}
Some(newline_idx) => newline_idx + 1,
};
self.buffer.flush_buf()?;
let lines = &buf[..newline_idx];
let flushed = self.inner_mut().write(lines)?;
if flushed == 0 {
return Ok(0);
}
let tail = if flushed >= newline_idx {
&buf[flushed..]
} else if newline_idx - flushed <= self.buffer.writer_capacity() {
&buf[flushed..newline_idx]
} else {
let scan_area = &buf[flushed..];
let scan_area = &scan_area[..self.buffer.writer_capacity()];
match memchr::memrchr(b'\n', scan_area) {
Some(newline_idx) => &scan_area[..=newline_idx],
None => scan_area,
}
};
let buffered = self.buffer.write_to_buf(tail);
Ok(flushed + buffered)
}
#[inline]
fn flush(&mut self) -> io::Result<()> {
self.buffer.flush()
}
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
#[cfg(can_vector)]
if !self.is_write_vectored() {
return match bufs.iter().find(|buf| !buf.is_empty()) {
Some(buf) => self.write(buf),
None => Ok(0),
};
}
let last_newline_buf_idx = bufs
.iter()
.enumerate()
.rev()
.find_map(|(i, buf)| memchr::memchr(b'\n', buf).map(|_| i));
let last_newline_buf_idx = match last_newline_buf_idx {
None => {
self.flush_if_completed_line()?;
return self.buffer.write_vectored(bufs);
}
Some(i) => i,
};
self.buffer.flush_buf()?;
let (lines, tail) = bufs.split_at(last_newline_buf_idx + 1);
let flushed = self.inner_mut().write_vectored(lines)?;
if flushed == 0 {
return Ok(0);
}
let lines_len = lines.iter().map(|buf| buf.len()).sum();
if flushed < lines_len {
return Ok(flushed);
}
let buffered: usize = tail
.iter()
.filter(|buf| !buf.is_empty())
.map(|buf| self.buffer.write_to_buf(buf))
.take_while(|&n| n > 0)
.sum();
Ok(flushed + buffered)
}
#[cfg(can_vector)]
#[inline]
fn is_write_vectored(&self) -> bool {
self.buffer.is_write_vectored()
}
fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
match memchr::memrchr(b'\n', buf) {
None => {
self.flush_if_completed_line()?;
self.buffer.write_all(buf)
}
Some(newline_idx) => {
let (lines, tail) = buf.split_at(newline_idx + 1);
if self.writer_buffered().is_empty() {
self.inner_mut().write_all(lines)?;
} else {
self.buffer.write_all(lines)?;
self.buffer.flush_buf()?;
}
self.buffer.write_all(tail)
}
}
}
}