use std::io::{BufRead, Read};
#[derive(Debug)]
pub struct HashBufReader<R, H> {
r: R,
hash: H,
buf: Vec<u8>,
pos: usize,
filled: usize,
}
impl<R: Read, H: sha2::Digest> Read for HashBufReader<R, H> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
let res = self.r.read(buf)?;
self.hash.update(&buf[..res]);
Ok(res)
}
}
impl<R: Read, H: sha2::Digest> BufRead for HashBufReader<R, H> {
fn fill_buf(&mut self) -> std::io::Result<&[u8]> {
if self.pos >= self.filled {
self.pos = 0;
self.filled = 0;
match self.r.read(&mut self.buf) {
Ok(r) => {
self.filled = r;
}
Err(e) => return Err(e),
}
}
Ok(&self.buf[self.pos..self.filled])
}
fn consume(&mut self, amount: usize) {
let new_pos = std::cmp::min(self.pos + amount, self.filled);
self.hash.update(&self.buf[self.pos..new_pos]);
self.pos = new_pos;
}
}
impl<R: Read, H: sha2::Digest> HashBufReader<R, H> {
pub fn new(r: R) -> Self {
Self {
r,
hash: H::new(),
buf: vec![0; 8192], pos: 0,
filled: 0,
}
}
pub fn boxed(r: R) -> HashBufReader<Box<dyn Read>, H>
where
R: 'static,
{
HashBufReader {
r: Box::new(r),
hash: H::new(),
buf: vec![0; 8192], pos: 0,
filled: 0,
}
}
pub fn hash(self) -> Vec<u8> {
self.hash.finalize().to_vec()
}
pub fn next_line(&mut self) -> Option<std::io::Result<String>> {
let mut additional_buf = Vec::new();
match read_until(self, b'\n', &mut additional_buf) {
Ok(0) => None,
Ok(_) => {
if additional_buf.last().copied() == Some(b'\n') {
additional_buf.pop();
if additional_buf.last().copied() == Some(b'\r') {
additional_buf.pop();
}
}
Some(
String::from_utf8(additional_buf)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e)),
)
}
Err(e) => Some(Err(e)),
}
}
pub const fn lines(&mut self) -> Lines<'_, R, H> {
Lines { reader: self }
}
}
#[derive(Debug)]
pub struct Lines<'a, R, H> {
reader: &'a mut HashBufReader<R, H>,
}
impl<R: Read, H: sha2::Digest> Iterator for Lines<'_, R, H> {
type Item = std::io::Result<String>;
fn next(&mut self) -> Option<Self::Item> {
self.reader.next_line()
}
}
fn read_until<R: BufRead + ?Sized>(
r: &mut R,
delim: u8,
buf: &mut Vec<u8>,
) -> std::io::Result<usize> {
let mut read = 0;
loop {
let (done, used) = {
let available = match r.fill_buf() {
Ok(n) => n,
Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
if let Some(i) = available.iter().position(|e| *e == delim) {
buf.extend_from_slice(&available[..=i]);
(true, i + 1)
} else {
buf.extend_from_slice(available);
(false, available.len())
}
};
r.consume(used);
read += used;
if done || used == 0 {
return Ok(read);
}
}
}