diff --git a/hashes/src/internal_macros.rs b/hashes/src/internal_macros.rs index 72ac69bca..5bf196161 100644 --- a/hashes/src/internal_macros.rs +++ b/hashes/src/internal_macros.rs @@ -209,6 +209,13 @@ macro_rules! hash_type { Self::from_engine(engine) } + + /// Hashes the entire contents of the `reader`. + #[cfg(feature = "bitcoin-io")] + pub fn hash_reader(reader: &mut R) -> Result { + ::hash_reader(reader) + } + /// Returns the underlying byte array. pub const fn to_byte_array(self) -> [u8; $bits / 8] { self.0 } diff --git a/hashes/src/lib.rs b/hashes/src/lib.rs index 09e115a21..3994cf649 100644 --- a/hashes/src/lib.rs +++ b/hashes/src/lib.rs @@ -80,6 +80,9 @@ extern crate alloc; #[cfg(any(test, feature = "std"))] extern crate core; +#[cfg(feature = "bitcoin-io")] +extern crate bitcoin_io as io; + #[cfg(feature = "serde")] /// A generic serialization/deserialization framework. pub extern crate serde; @@ -232,6 +235,27 @@ pub trait GeneralHash: Hash { } Self::from_engine(engine) } + + /// Hashes the entire contents of the `reader`. + #[cfg(feature = "bitcoin-io")] + fn hash_reader(reader: &mut R) -> Result + where + Self::Engine: Default, + { + let mut engine = Self::engine(); + loop { + let bytes = reader.fill_buf()?; + + let read = bytes.len(); + if read == 0 { // Empty slice means EOF. + break + } + + engine.input(bytes); + reader.consume(read); + } + Ok(Self::from_engine(engine)) + } } /// Trait which applies to hashes of all types. @@ -298,7 +322,8 @@ impl std::error::Error for FromSliceError {} #[cfg(test)] mod tests { - use crate::sha256d; + use super::*; + use crate::{sha256, sha256d}; hash_newtype! { /// A test newtype @@ -323,4 +348,13 @@ mod tests { let rinsed = hex.parse::().expect("failed to parse hex"); assert_eq!(rinsed, orig) } + + #[test] + fn hash_reader() { + let mut reader: &[u8] = b"hello"; + assert_eq!( + sha256::Hash::hash_reader(&mut reader).unwrap(), + sha256::Hash::hash(b"hello"), + ) + } } diff --git a/hashes/src/sha256t.rs b/hashes/src/sha256t.rs index edc3169f7..411897cba 100644 --- a/hashes/src/sha256t.rs +++ b/hashes/src/sha256t.rs @@ -96,6 +96,24 @@ where Self::from_engine(engine) } + /// Hashes the entire contents of the `reader`. + #[cfg(feature = "bitcoin-io")] + pub fn hash_reader(reader: &mut R) -> Result { + let mut engine = Self::engine(); + loop { + let bytes = reader.fill_buf()?; + + let read = bytes.len(); + if read == 0 { // Empty slice means EOF. + break + } + + engine.input(bytes); + reader.consume(read); + } + Ok(Self::from_engine(engine)) + } + /// Returns the underlying byte array. pub const fn to_byte_array(self) -> [u8; 32] { self.0 } @@ -231,6 +249,13 @@ macro_rules! sha256t_hash_newtype { } Self::from_engine(engine) } + + /// Hashes the entire contents of the `reader`. + #[cfg(feature = "bitcoin-io")] + #[allow(unused)] // the user of macro may not need this + fn hash_reader(reader: &mut R) -> Result { + <$hash_name as $crate::GeneralHash>::hash_reader(reader) + } } impl $crate::GeneralHash for $hash_name {