use std::convert::TryFrom;
use blake2b_simd::{Params as Blake2bParams, State as Blake2b};
use blake2s_simd::{Params as Blake2sParams, State as Blake2s};
use crate::digests::{wrap, Multihash, MultihashDigest, Multihasher};
use crate::errors::DecodeError;
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! impl_code {
($(
#[$doc:meta]
$name:ident => $code:expr,
)*) => {
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Code {
$(
#[$doc]
$name,
)*
}
impl Code {
pub fn digest(&self, data: &[u8]) -> Multihash {
match self {
$(Self::$name => $name::digest(data),)*
}
}
}
impl From<Code> for Box<dyn MultihashDigest<Code>> {
fn from(code: Code) -> Self {
match code {
$(Code::$name => Box::new($name::default()),)*
}
}
}
impl From<Code> for u64 {
fn from(code: Code) -> Self {
match code {
$(Code::$name => $code,)*
}
}
}
impl TryFrom<u64> for Code {
type Error = DecodeError;
fn try_from(raw: u64) -> Result<Self, Self::Error> {
match raw {
$($code => Ok(Self::$name),)*
_ => Err(DecodeError::UnknownCode),
}
}
}
};
}
#[doc(hidden)]
#[macro_export(local_inner_macros)]
macro_rules! derive_digest {
($(
#[$doc:meta]
@sha $type:ty as $name:ident;
@code_doc $code_doc:literal,
)*) => {
$(
#[$doc]
#[derive(Clone, Debug, Default)]
pub struct $name($type);
impl $name {
#[doc = $code_doc]
pub const CODE: Code = Code::$name;
#[inline]
pub fn digest(data: &[u8]) -> Multihash {
let digest = <$type as digest::Digest>::digest(&data);
wrap(Self::CODE, &digest)
}
}
impl Multihasher<Code> for $name {
const CODE: Code = Code::$name;
#[inline]
fn digest(data: &[u8]) -> Multihash {
Self::digest(data)
}
}
impl MultihashDigest<Code> for $name {
#[inline]
fn code(&self) -> Code {
Self::CODE
}
#[inline]
fn digest(&self, data: &[u8]) -> Multihash {
Self::digest(data)
}
#[inline]
fn input(&mut self, data: &[u8]) {
<$type as digest::Digest>::update(&mut self.0, data)
}
#[inline]
fn result(self) -> Multihash {
wrap(Self::CODE, <$type as digest::Digest>::finalize(self.0).as_slice())
}
#[inline]
fn result_reset(&mut self) -> Multihash {
wrap(Self::CODE, <$type as digest::Digest>::finalize_reset(&mut self.0).as_slice())
}
#[inline]
fn reset(&mut self) {
<$type as digest::Digest>::reset(&mut self.0)
}
}
impl ::std::io::Write for $name {
#[inline]
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
<$name as MultihashDigest<Code>>::input(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> ::std::io::Result<()> {
Ok(())
}
}
)*
};
($(
#[$doc:meta]
@blake $type:ty | $params:ty as $name:ident $len:expr;
@code_doc $code_doc:literal,
)*) => {
$(
#[$doc]
#[derive(Clone, Debug)]
pub struct $name($type);
impl $name {
#[doc = $code_doc]
pub const CODE: Code = Code::$name;
pub fn digest(data: &[u8]) -> Multihash {
let digest = <$params>::new().hash_length($len).hash(data);
wrap(Self::CODE, &digest.as_bytes())
}
}
impl Default for $name {
fn default() -> Self {
$name(<$params>::new().hash_length($len).to_state())
}
}
impl Multihasher<Code> for $name {
const CODE: Code = Code::$name;
#[inline]
fn digest(data: &[u8]) -> Multihash {
Self::digest(data)
}
}
impl MultihashDigest<Code> for $name {
#[inline]
fn code(&self) -> Code {
Self::CODE
}
#[inline]
fn digest(&self, data: &[u8]) -> Multihash {
Self::digest(data)
}
#[inline]
fn input(&mut self, data: &[u8]) {
self.0.update(data);
}
#[inline]
fn result(self) -> Multihash {
let digest = self.0.finalize();
wrap(Self::CODE, digest.as_bytes())
}
#[inline]
fn result_reset(&mut self) -> Multihash {
let digest = self.0.finalize();
let hash = wrap(Self::CODE, digest.as_bytes());
self.reset();
hash
}
#[inline]
fn reset(&mut self) {
self.0 = Self::default().0;
}
}
impl ::std::io::Write for $name {
#[inline]
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
<$name as MultihashDigest<Code>>::input(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> ::std::io::Result<()> {
self.0.finalize();
Ok(())
}
}
)*
};
($(
#[$doc:meta]
@blake3 $type:ty as $name:ident;
@code_doc $code_doc:literal,
)*) => {
$(
#[$doc]
#[derive(Clone, Debug, Default)]
pub struct $name($type);
impl $name {
#[doc = $code_doc]
pub const CODE: Code = Code::$name;
pub fn digest(data: &[u8]) -> Multihash {
let digest = blake3::hash(data);
wrap(Self::CODE, digest.as_bytes())
}
}
impl Multihasher<Code> for $name {
const CODE: Code = Code::$name;
#[inline]
fn digest(data: &[u8]) -> Multihash {
Self::digest(data)
}
}
impl MultihashDigest<Code> for $name {
#[inline]
fn code(&self) -> Code {
Self::CODE
}
#[inline]
fn digest(&self, data: &[u8]) -> Multihash {
Self::digest(data)
}
#[inline]
fn input(&mut self, data: &[u8]) {
self.0.update(data);
}
#[inline]
fn result(self) -> Multihash {
let digest = self.0.finalize();
wrap(Self::CODE, digest.as_bytes())
}
#[inline]
fn result_reset(&mut self) -> Multihash {
let digest = self.0.finalize();
let hash = wrap(Self::CODE, digest.as_bytes());
self.0.reset();
hash
}
#[inline]
fn reset(&mut self) {
self.0.reset();
}
}
impl ::std::io::Write for $name {
#[inline]
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
<$name as MultihashDigest<Code>>::input(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> ::std::io::Result<()> {
self.0.finalize();
Ok(())
}
}
)*
}
}
#[cfg(not(feature = "use_blake3"))]
impl_code! {
Identity => 0x00,
Sha1 => 0x11,
Sha2_256 => 0x12,
Sha2_512 => 0x13,
Sha3_224 => 0x17,
Sha3_256 => 0x16,
Sha3_384 => 0x15,
Sha3_512 => 0x14,
Keccak224 => 0x1a,
Keccak256 => 0x1b,
Keccak384 => 0x1c,
Keccak512 => 0x1d,
Blake2b256 => 0xb220,
Blake2b512 => 0xb240,
Blake2s128 => 0xb250,
Blake2s256 => 0xb260,
}
#[cfg(feature = "use_blake3")]
impl_code! {
Identity => 0x00,
Sha1 => 0x11,
Sha2_256 => 0x12,
Sha2_512 => 0x13,
Sha3_224 => 0x17,
Sha3_256 => 0x16,
Sha3_384 => 0x15,
Sha3_512 => 0x14,
Keccak224 => 0x1a,
Keccak256 => 0x1b,
Keccak384 => 0x1c,
Keccak512 => 0x1d,
Blake2b256 => 0xb220,
Blake2b512 => 0xb240,
Blake2s128 => 0xb250,
Blake2s256 => 0xb260,
Blake3 => 0x1e,
}
#[derive(Clone, Debug, Default)]
pub struct Identity(Vec<u8>);
impl Identity {
pub const CODE: Code = Code::Identity;
pub fn digest(data: &[u8]) -> Multihash {
if (data.len() as u64) >= u64::from(std::u32::MAX) {
panic!("Input data for identity hash is too large, it needs to be less than 2^32.")
}
wrap(Self::CODE, data)
}
}
impl Multihasher<Code> for Identity {
const CODE: Code = Code::Identity;
#[inline]
fn digest(data: &[u8]) -> Multihash {
Self::digest(data)
}
}
impl MultihashDigest<Code> for Identity {
#[inline]
fn code(&self) -> Code {
Self::CODE
}
#[inline]
fn digest(&self, data: &[u8]) -> Multihash {
Self::digest(data)
}
#[inline]
fn input(&mut self, data: &[u8]) {
if ((self.0.len() as u64) + (data.len() as u64)) >= u64::from(std::u32::MAX) {
panic!("Input data for identity hash is too large, it needs to be less than 2^32.")
}
self.0.extend_from_slice(data)
}
#[inline]
fn result(self) -> Multihash {
wrap(Self::CODE, &self.0)
}
#[inline]
fn result_reset(&mut self) -> Multihash {
let hash = wrap(Self::CODE, &self.0);
self.reset();
hash
}
#[inline]
fn reset(&mut self) {
self.0.clear()
}
}
impl ::std::io::Write for Identity {
#[inline]
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
<Identity as MultihashDigest<Code>>::input(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> ::std::io::Result<()> {
Ok(())
}
}
derive_digest! {
@sha ::sha1::Sha1 as Sha1;
@code_doc "The code of the SHA-1 hasher, 0x11.",
@sha ::sha2::Sha256 as Sha2_256;
@code_doc "The code of the SHA2-256 hasher, 0x12.",
@sha ::sha2::Sha512 as Sha2_512;
@code_doc "The code of the SHA2-512 hasher, 0x13.",
@sha ::sha3::Sha3_224 as Sha3_224;
@code_doc "The code of the SHA3-224 hasher, 0x17.",
@sha ::sha3::Sha3_256 as Sha3_256;
@code_doc "The code of the SHA3-256 hasher, 0x16.",
@sha ::sha3::Sha3_384 as Sha3_384;
@code_doc "The code of the SHA3-384 hasher, 0x15.",
@sha ::sha3::Sha3_512 as Sha3_512;
@code_doc "The code of the SHA3-512 hasher, 0x14.",
@sha ::sha3::Keccak224 as Keccak224;
@code_doc "The code of the Keccak-224 hasher, 0x1a.",
@sha ::sha3::Keccak256 as Keccak256;
@code_doc "The code of the Keccak-256 hasher, 0x1b.",
@sha ::sha3::Keccak384 as Keccak384;
@code_doc "The code of the Keccak-384 hasher, 0x1c.",
@sha ::sha3::Keccak512 as Keccak512;
@code_doc "The code of the Keccak-512 hasher, 0x1d.",
}
derive_digest! {
@blake Blake2b | Blake2bParams as Blake2b256 32;
@code_doc "The code of the Blake2-256 hasher, 0xb220.",
@blake Blake2b | Blake2bParams as Blake2b512 64;
@code_doc "The code of the Blake2-512 hasher, 0xb240.",
@blake Blake2s | Blake2sParams as Blake2s128 16;
@code_doc "The code of the Blake2-128 hasher, 0xb250.",
@blake Blake2s | Blake2sParams as Blake2s256 32;
@code_doc "The code of the Blake2-256 hasher, 0xb260.",
}
#[cfg(feature = "use_blake3")]
derive_digest! {
@blake3 blake3::Hasher as Blake3;
@code_doc "The code of the Blake3 hasher, 0x1e.",
}