#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![forbid(unsafe_code)]
use std::io::{self, BufRead, Read};
#[cfg(feature = "async-runtime-tokio")]
use std::pin::{pin, Pin};
#[cfg(feature = "async-runtime-tokio")]
use std::task::{Context, Poll};
use chksum_core::Hash;
#[cfg(feature = "async-runtime-tokio")]
use tokio::io::{AsyncRead, AsyncReadExt, ReadBuf};
pub fn new<R, H>(inner: R) -> Reader<R, H>
where
R: Read,
H: Hash,
{
Reader::new(inner)
}
pub fn with_hash<R, H>(inner: R, hash: H) -> Reader<R, H>
where
R: Read,
H: Hash,
{
Reader::with_hash(inner, hash)
}
#[cfg(feature = "async-runtime-tokio")]
pub fn async_new<R, H>(inner: R) -> AsyncReader<R, H>
where
R: AsyncReadExt,
H: Hash,
{
AsyncReader::new(inner)
}
#[cfg(feature = "async-runtime-tokio")]
pub fn async_with_hash<R, H>(inner: R, hash: H) -> AsyncReader<R, H>
where
R: AsyncReadExt,
H: Hash,
{
AsyncReader::with_hash(inner, hash)
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Reader<R, H>
where
R: Read,
H: Hash,
{
inner: R,
hash: H,
}
impl<R, H> Reader<R, H>
where
R: Read,
H: Hash,
{
pub fn new(inner: R) -> Self {
let hash = H::default();
Self::with_hash(inner, hash)
}
#[must_use]
pub const fn with_hash(inner: R, hash: H) -> Self {
Self { inner, hash }
}
#[must_use]
pub fn into_inner(self) -> R {
let Self { inner, .. } = self;
inner
}
#[must_use]
pub fn digest(&self) -> H::Digest {
self.hash.digest()
}
}
impl<R, H> Read for Reader<R, H>
where
R: Read,
H: Hash,
{
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let n = self.inner.read(buf)?;
self.hash.update(&buf[..n]);
Ok(n)
}
}
impl<R, H> BufRead for Reader<R, H>
where
R: BufRead,
H: Hash,
{
fn consume(&mut self, amt: usize) {
self.inner.consume(amt);
}
fn fill_buf(&mut self) -> io::Result<&[u8]> {
self.inner.fill_buf()
}
}
#[cfg(feature = "async-runtime-tokio")]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct AsyncReader<R, H>
where
R: AsyncReadExt,
H: Hash,
{
inner: R,
hash: H,
}
#[cfg(feature = "async-runtime-tokio")]
impl<R, H> AsyncReader<R, H>
where
R: AsyncReadExt,
H: Hash,
{
pub fn new(inner: R) -> Self {
let hash = H::default();
Self::with_hash(inner, hash)
}
#[must_use]
pub const fn with_hash(inner: R, hash: H) -> Self {
Self { inner, hash }
}
#[must_use]
pub fn into_inner(self) -> R {
let Self { inner, .. } = self;
inner
}
#[must_use]
pub fn digest(&self) -> H::Digest {
self.hash.digest()
}
}
#[cfg(feature = "async-runtime-tokio")]
impl<R, H> AsyncRead for AsyncReader<R, H>
where
R: AsyncRead + Unpin,
H: Hash + Unpin,
{
fn poll_read(self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>) -> Poll<io::Result<()>> {
let Self { inner, hash } = self.get_mut();
match pin!(inner).poll_read(cx, buf) {
Poll::Ready(Ok(())) => {
hash.update(buf.filled());
Poll::Ready(Ok(()))
},
poll => poll,
}
}
}