use crate::util::preview::Preview;
use core::{cmp, fmt};
use std::io;
use std::io::{BufRead, Error, ErrorKind, Read};
use std::str::Utf8Error;
const DEFAULT_BUF_SIZE: usize = 8 * 1024;
pub struct ChopperBufReader<R> {
inner: R,
buf: Box<[u8]>,
pos: usize,
cap: usize,
}
pub struct ChopperBufPreviewer<R> {
reader: ChopperBufReader<R>,
trimmed_buf: Box<[u8]>,
lines: Option<Vec<String>>,
}
impl<R: 'static + Read> Preview for ChopperBufPreviewer<R> {
fn get_lines(&self) -> &Option<Vec<String>> {
&self.lines
}
fn get_buf(&self) -> &Box<[u8]> {
&self.trimmed_buf
}
fn get_reader(self: Box<Self>) -> Box<dyn Read> {
Box::new(self.reader)
}
}
impl<R: Read> ChopperBufPreviewer<R> {
pub fn new(inner: R) -> io::Result<ChopperBufPreviewer<R>> {
let mut reader = ChopperBufReader::with_capacity(DEFAULT_BUF_SIZE, inner);
reader.fill_buffer()?;
let trimmed_slice = &reader.buf[..reader.cap];
let trimmed_buf: Box<[u8]> = trimmed_slice.into();
let mut previewer = ChopperBufPreviewer {
reader,
trimmed_buf,
lines: None,
};
previewer.prepare_preview()?;
Ok(previewer)
}
pub fn get_reader(self) -> ChopperBufReader<R> {
self.reader
}
fn prepare_preview(&mut self) -> io::Result<()> {
self.lines = match Self::get_lines(&self.reader) {
Ok(lines) => Some(lines),
Err(_) => None,
};
Ok(())
}
fn get_lines(reader: &ChopperBufReader<R>) -> Result<Vec<String>, Utf8Error> {
let mut lines: Vec<String> = Vec::new();
let mut next_line_start = 0;
loop {
if next_line_start >= reader.cap {
break;
}
next_line_start =
match Self::add_next_line_to_lines(reader, next_line_start, &mut lines) {
Ok(Some(next_line_start)) => next_line_start,
Ok(None) => {
break;
}
Err(e) => {
return Err(e);
}
};
}
Ok(lines)
}
fn add_next_line_to_lines(
reader: &ChopperBufReader<R>,
next_line_start: usize,
lines: &mut Vec<String>,
) -> Result<Option<usize>, Utf8Error> {
let next_line = Self::get_line(&reader.buf, next_line_start, reader.cap);
match next_line {
None => Ok(None),
Some(result) => match result {
Ok((line, next_line_start)) => {
lines.push(line);
Ok(Some(next_line_start))
}
Err(e) => Err(e),
},
}
}
fn get_line(
buf: &[u8],
start: usize,
cap: usize,
) -> Option<Result<(String, usize), Utf8Error>> {
let end = match buf[start..cap].iter().position(|&c| c == b'\n') {
None => {
if cap >= buf.len() {
return None;
} else {
cap
}
}
Some(p) => start + p,
};
let next_start = end + 1;
let result = match Self::make_string_no_cr(&buf[start..end]) {
Ok(s) => Ok((s, next_start)),
Err(e) => Err(e),
};
Some(result)
}
fn make_string_no_cr(slice: &[u8]) -> Result<String, Utf8Error> {
if slice.len() == 0 {
return Ok(String::new());
}
let end = if slice[slice.len() - 1] == b'\r' {
slice.len() - 1
} else {
slice.len()
};
match std::str::from_utf8(&slice[..end]) {
Ok(s) => Ok(s.to_string()),
Err(e) => Err(e),
}
}
}
impl<R: Read> ChopperBufReader<R> {
pub fn new(inner: R) -> ChopperBufReader<R> {
Self::with_capacity(DEFAULT_BUF_SIZE, inner)
}
pub fn with_capacity(capacity: usize, inner: R) -> ChopperBufReader<R> {
assert!(capacity > 0);
let buf: Vec<u8> = vec![0; capacity];
ChopperBufReader {
inner,
buf: buf.into_boxed_slice(),
pos: 0,
cap: 0,
}
}
}
impl<R> ChopperBufReader<R> {
#[inline]
fn discard_buffer(&mut self) {
self.pos = 0;
self.cap = 0;
}
}
impl<R: Read> ChopperBufReader<R> {
fn fill_buffer(&mut self) -> io::Result<()> {
debug_assert!(self.pos == 0);
debug_assert!(self.cap == 0);
loop {
let bytes_read = self.fill_buffer_one_shot()?;
if bytes_read == 0 || self.cap == self.buf.len() {
break;
}
}
Ok(())
}
fn fill_buffer_one_shot(&mut self) -> io::Result<usize> {
if self.cap == self.buf.len() {
return Err(Error::new(
ErrorKind::Other,
"asked to fill buffer when it was already at capacity",
));
}
let bytes_read = self.inner.read(&mut self.buf[self.cap..])?;
self.cap += bytes_read;
debug_assert!(self.cap <= self.buf.len());
Ok(bytes_read)
}
}
impl<R: Read> Read for ChopperBufReader<R> {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
if self.pos == self.cap && buf.len() >= self.buf.len() {
self.discard_buffer();
return self.inner.read(buf);
}
let bytes_read = {
let mut rem = self.fill_buf()?;
rem.read(buf)?
};
self.consume(bytes_read);
Ok(bytes_read)
}
}
impl<R: Read> BufRead for ChopperBufReader<R> {
fn fill_buf(&mut self) -> io::Result<&[u8]> {
if self.pos >= self.cap {
debug_assert!(self.pos == self.cap);
self.cap = self.inner.read(&mut self.buf)?;
self.pos = 0;
}
Ok(&self.buf[self.pos..self.cap])
}
fn consume(&mut self, amt: usize) {
self.pos = cmp::min(self.pos + amt, self.cap);
}
}
impl<R: fmt::Debug> fmt::Debug for ChopperBufPreviewer<R> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ChopperHeaderPreview")
.field("reader", &self.reader)
.field("lines", &self.lines)
.finish()
}
}
impl<R: fmt::Debug> fmt::Debug for ChopperBufReader<R> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_struct("ChopperBufReader")
.field("reader", &self.inner)
.field(
"buffer",
&format_args!("{}/{}", self.cap - self.pos, self.buf.len()),
)
.finish()
}
}
#[cfg(test)]
mod tests {
use std::io::{BufRead, BufReader, Read};
use crate::util::preview::Preview;
use crate::util::reader::{ChopperBufPreviewer, ChopperBufReader};
const TEST_BYTES: &[u8] = "aaaaa\nbbbbb\nccccc".as_bytes();
#[test]
fn test_capacity_too_small() {
let inner = BufReader::new(TEST_BYTES);
let mut reader = ChopperBufReader::with_capacity(5, inner);
let result = reader.fill_buffer();
assert!(result.is_ok());
assert_eq!(reader.cap, reader.buf.len());
}
#[test]
fn test_multiple_capacities() {
for capacity in 6..20 {
test_normal_with_capacity(capacity);
}
}
fn test_normal_with_capacity(capacity: usize) {
let inner = BufReader::new(TEST_BYTES);
let mut reader = ChopperBufReader::with_capacity(capacity, inner);
let result = reader.fill_buffer();
assert!(result.is_ok());
check_line(&mut reader, 6, "aaaaa\n");
check_line(&mut reader, 6, "bbbbb\n");
check_line(&mut reader, 5, "ccccc");
check_line(&mut reader, 0, "");
}
fn check_line<R: Read>(
reader: &mut ChopperBufReader<R>,
expect_result: usize,
expect_line: &str,
) {
let mut line = String::new();
let result = reader.read_line(&mut line);
assert!(result.is_ok());
assert_eq!(result.unwrap(), expect_result);
assert_eq!(line, expect_line);
}
#[test]
fn test_preview_empty() {
let inner = BufReader::new("".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert!(lines.is_empty());
}
#[test]
fn test_preview_1_1() {
let inner = BufReader::new("\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "");
}
#[test]
fn test_preview_1_2() {
let inner = BufReader::new("\r\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "");
}
#[test]
fn test_preview_1_3() {
let inner = BufReader::new("zzz\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "zzz");
}
#[test]
fn test_preview_1_4() {
let inner = BufReader::new("zzz\r\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "zzz");
}
#[test]
fn test_preview_1_5() {
let inner = BufReader::new("z".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "z");
}
#[test]
fn test_preview_2_1() {
let inner = BufReader::new("zzz\n\r\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 2);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "");
}
#[test]
fn test_preview_2_2() {
let inner = BufReader::new("zzz\r\nxxx".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 2);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "xxx");
}
#[test]
fn test_preview_2_3() {
let inner = BufReader::new("zzz\nxxx\rx\n".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 2);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "xxx\rx");
}
#[test]
fn test_preview_2_4() {
let inner = BufReader::new("zzz\r\nxxx\nyyy".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert!(previewer.lines.is_some());
let lines = previewer.lines.unwrap();
assert_eq!(lines.len(), 3);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "xxx");
assert_eq!(lines.get(2).unwrap(), "yyy");
}
fn setup_test_preview_3(capacity: usize) -> Box<dyn Preview> {
let inner = BufReader::new("zzz\nxxx\nyyy".as_bytes());
let mut reader = ChopperBufReader::with_capacity(capacity, inner);
reader.fill_buffer().unwrap();
let mut previewer = ChopperBufPreviewer {
reader,
trimmed_buf: [].into(),
lines: None,
};
previewer.prepare_preview().unwrap();
Box::new(previewer)
}
#[test]
fn test_preview_3_1_3() {
for capacity in 1..=3 {
inner_test_preview_3_1_3(capacity);
}
}
fn inner_test_preview_3_1_3(capacity: usize) {
let previewer = setup_test_preview_3(capacity);
let lines = previewer.get_lines();
assert!(lines.is_some());
let lines = lines.as_ref().unwrap();
assert!(lines.is_empty());
}
#[test]
fn test_preview_3_4_7() {
for capacity in 4..=7 {
inner_test_preview_3_4_7(capacity);
}
}
fn inner_test_preview_3_4_7(capacity: usize) {
let previewer = setup_test_preview_3(capacity);
let lines = previewer.get_lines();
assert!(lines.is_some());
let lines = lines.as_ref().unwrap();
assert_eq!(lines.len(), 1);
assert_eq!(lines.get(0).unwrap(), "zzz");
}
#[test]
fn test_preview_3_8_11() {
for capacity in 8..=11 {
inner_test_preview_3_8_11(capacity);
}
}
fn inner_test_preview_3_8_11(capacity: usize) {
let previewer = setup_test_preview_3(capacity);
let lines = previewer.get_lines();
assert!(lines.is_some());
let lines = lines.as_ref().unwrap();
assert_eq!(lines.len(), 2);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "xxx");
}
#[test]
fn test_preview_3_12_20() {
for capacity in 12..=20 {
inner_test_preview_3_12_20(capacity);
}
}
fn inner_test_preview_3_12_20(capacity: usize) {
let previewer = setup_test_preview_3(capacity);
let lines = previewer.get_lines();
assert!(lines.is_some());
let lines = lines.as_ref().unwrap();
assert_eq!(lines.len(), 3);
assert_eq!(lines.get(0).unwrap(), "zzz");
assert_eq!(lines.get(1).unwrap(), "xxx");
assert_eq!(lines.get(2).unwrap(), "yyy");
}
#[test]
fn test_preview_trimmed_buf() {
let inner = BufReader::new("zzz".as_bytes());
let previewer = ChopperBufPreviewer::new(inner).unwrap();
assert_eq!(previewer.get_buf().len(), 3);
assert_eq!(previewer.get_buf().as_ref(), "zzz".as_bytes());
}
}