use super::bytes;
use std::io::{Read, Result};
pub struct LineReader<R> {
block: Vec<u8>,
inner: R,
buf: Vec<u8>,
pos: usize,
cap: usize,
}
static DEFAULT_BUF_SIZE: usize = 64 * 1024;
impl<R: Read> LineReader<R> {
pub fn with_capacity(cap: usize, inner: R) -> LineReader<R> {
LineReader {
block: Vec::new(),
inner,
buf: vec![0; cap],
pos: 0,
cap: 0,
}
}
pub fn new(inner: R) -> LineReader<R> {
LineReader::with_capacity(DEFAULT_BUF_SIZE, inner)
}
fn fill_buf(&mut self) -> Result<usize> {
if self.pos == self.cap {
self.cap = self.inner.read(&mut self.buf[..])?;
self.pos = 0;
}
Ok(self.cap - self.pos)
}
fn read_until(&mut self, byte: u8) -> Result<&[u8]> {
self.block.clear();
loop {
match self.fill_buf() {
Ok(0) => {
return Ok(&self.block[..]);
}
Ok(_) => {}
Err(e) => return Err(e),
};
let b = &self.buf[self.pos..self.cap];
match bytes::index(b, byte) {
Some(mut i) => {
i += 1;
let b = if self.block.is_empty() {
&self.buf[self.pos..self.pos + i]
} else {
let b: &[u8] = &b[..i];
self.block.extend_from_slice(b);
&self.block[..]
};
self.pos += i;
return Ok(b);
}
None => {
self.block.extend_from_slice(b);
self.pos += b.len();
}
}
}
}
#[inline]
pub fn read_line(&mut self) -> Result<&[u8]> {
self.read_until(b'\n')
}
}
#[macro_export]
macro_rules! read_lines {
($inp:ident in $expr:expr, $b:block) => {{
let r = &mut $expr;
loop {
let $inp = r.read_line();
match $inp {
Ok(b) if b.is_empty() => break,
_ => $b,
};
}
}};
}
#[macro_export]
macro_rules! try_read_lines {
($inp:ident in $expr:expr, $b:block) => {{
let r = &mut $expr;
loop {
let $inp = r.read_line()?;
if $inp.is_empty() {
break;
} else {
$b
};
}
}};
}
pub fn count_lines<R: Read>(mut r: LineReader<R>) -> Result<usize> {
let mut lines = 0usize;
try_read_lines!(line in r, {
lines += 1;
});
Ok(lines)
}