use std::io::{BufRead, Read};
pub struct LowMarkBufReader<R> {
inner: R,
buf: Box<[u8]>,
pos: usize,
cap: usize,
low_mark: usize,
empty_last_read: bool,
}
const CACHE_LINE_SIZE: usize = 4096;
impl<R: Read> LowMarkBufReader<R> {
pub fn new(inner: R, capacity: usize, low_mark: usize) -> LowMarkBufReader<R> {
assert!(low_mark + CACHE_LINE_SIZE <= capacity);
assert!(low_mark > 0);
let buf = vec![0u8; capacity].into_boxed_slice();
LowMarkBufReader {
inner,
buf,
pos: 0,
cap: 0,
low_mark,
empty_last_read: false,
}
}
pub fn buffer(&self) -> &[u8] {
&self.buf[self.pos..self.cap]
}
pub fn capacity(&self) -> usize {
self.buf.len()
}
}
impl<R: Read> Read for LowMarkBufReader<R> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let nread = {
let mut rem = self.fill_buf()?;
rem.read(buf)?
};
self.consume(nread);
Ok(nread)
}
}
impl<R: Read> BufRead for LowMarkBufReader<R> {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
if !self.empty_last_read {
loop {
let in_buf = self.cap - self.pos;
if in_buf < self.low_mark {
if self.pos >= CACHE_LINE_SIZE {
let new_cap = self.cap - self.pos;
let mut offset = CACHE_LINE_SIZE - (new_cap % CACHE_LINE_SIZE);
if offset == CACHE_LINE_SIZE {
offset = 0;
}
let new_cap = new_cap + offset;
self.buf.copy_within(self.pos..self.cap, offset);
self.cap = new_cap;
self.pos = offset;
}
let read = self.inner.read(&mut self.buf[self.cap..])?;
if read == 0 {
self.empty_last_read = true;
break;
} else {
let max_read = self.buf.len() - self.cap;
self.cap += read;
if read == max_read {
break;
} }
} else {
break;
}
}
}
Ok(&self.buf[self.pos..self.cap])
}
fn consume(&mut self, amt: usize) {
self.pos = std::cmp::min(self.pos + amt, self.cap)
}
}