#![cfg_attr(not(any(feature = "std", test)), no_std)]
mod hasher;
mod hex;
pub mod keccak;
#[cfg(feature = "serde")]
mod serde;
pub use crate::hasher::Hasher;
use crate::hex::{Alphabet, FormattingBuffer, ParseHexError};
use core::{
array::{IntoIter, TryFromSliceError},
fmt::{self, Debug, Display, Formatter, LowerHex, UpperHex},
ops::{Deref, DerefMut},
slice::Iter,
str::FromStr,
};
#[macro_export]
macro_rules! digest {
($digest:expr $(,)?) => {{
const VALUE: $crate::Digest = $crate::Digest::const_from_str($digest);
VALUE
}};
}
#[macro_export]
macro_rules! keccak {
($data:expr $(,)?) => {{
const VALUE: $crate::Digest = $crate::Digest::const_of($data);
VALUE
}};
($($part:expr),* $(,)?) => {{
const VALUE: $crate::Digest = $crate::Digest::const_of_parts(&[$($part),*]);
VALUE
}};
}
#[repr(transparent)]
#[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Digest(pub [u8; 32]);
impl Digest {
pub fn from_slice(slice: &[u8]) -> Self {
slice.try_into().unwrap()
}
pub fn from_ref(array: &[u8; 32]) -> &'_ Self {
unsafe { &*(array as *const [u8; 32]).cast::<Self>() }
}
pub fn from_mut(array: &mut [u8; 32]) -> &'_ mut Self {
unsafe { &mut *(array as *mut [u8; 32]).cast::<Self>() }
}
pub fn of(data: impl AsRef<[u8]>) -> Self {
let mut hasher = Hasher::new();
hasher.update(data);
hasher.finalize()
}
#[doc(hidden)]
pub const fn const_of(data: &[u8]) -> Self {
Self(keccak::v256(data))
}
#[doc(hidden)]
pub const fn const_of_parts(parts: &[&[u8]]) -> Self {
let mut hasher = keccak::V256::new();
let mut i = 0;
while i < parts.len() {
hasher = hasher.absorb(parts[i]);
i += 1;
}
Self(hasher.squeeze())
}
#[doc(hidden)]
pub const fn const_from_str(src: &str) -> Self {
Self(hex::const_decode(src))
}
fn fmt_buffer(&self, alphabet: Alphabet) -> FormattingBuffer<66> {
hex::encode(self, alphabet)
}
}
impl Debug for Digest {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.debug_tuple("Digest")
.field(&format_args!("{self}"))
.finish()
}
}
impl Display for Digest {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.pad(self.fmt_buffer(Alphabet::default()).as_str())
}
}
impl LowerHex for Digest {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let buffer = self.fmt_buffer(Alphabet::default());
f.pad(if f.alternate() {
buffer.as_str()
} else {
buffer.as_bytes_str()
})
}
}
impl UpperHex for Digest {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
let buffer = hex::encode::<32, 66>(self, Alphabet::Upper);
f.pad(if f.alternate() {
buffer.as_str()
} else {
buffer.as_bytes_str()
})
}
}
impl AsRef<[u8; 32]> for Digest {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsMut<[u8; 32]> for Digest {
fn as_mut(&mut self) -> &mut [u8; 32] {
&mut self.0
}
}
impl AsMut<[u8]> for Digest {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0
}
}
impl Deref for Digest {
type Target = [u8; 32];
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for Digest {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl FromStr for Digest {
type Err = ParseDigestError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(hex::decode(s)?))
}
}
impl IntoIterator for Digest {
type Item = u8;
type IntoIter = IntoIter<u8, 32>;
fn into_iter(self) -> Self::IntoIter {
self.0.into_iter()
}
}
impl<'a> IntoIterator for &'a Digest {
type Item = &'a u8;
type IntoIter = Iter<'a, u8>;
fn into_iter(self) -> Self::IntoIter {
self.0.iter()
}
}
impl PartialEq<[u8; 32]> for Digest {
fn eq(&self, other: &'_ [u8; 32]) -> bool {
**self == *other
}
}
impl PartialEq<[u8]> for Digest {
fn eq(&self, other: &'_ [u8]) -> bool {
**self == *other
}
}
impl PartialEq<&'_ [u8]> for Digest {
fn eq(&self, other: &&'_ [u8]) -> bool {
**self == **other
}
}
impl PartialEq<&'_ mut [u8]> for Digest {
fn eq(&self, other: &&'_ mut [u8]) -> bool {
**self == **other
}
}
#[cfg(feature = "std")]
impl PartialEq<Vec<u8>> for Digest {
fn eq(&self, other: &Vec<u8>) -> bool {
**self == **other
}
}
impl TryFrom<&'_ [u8]> for Digest {
type Error = TryFromSliceError;
fn try_from(value: &'_ [u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl TryFrom<&'_ mut [u8]> for Digest {
type Error = TryFromSliceError;
fn try_from(value: &'_ mut [u8]) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
impl<'a> TryFrom<&'a [u8]> for &'a Digest {
type Error = TryFromSliceError;
fn try_from(value: &'a [u8]) -> Result<Self, Self::Error> {
Ok(Digest::from_ref(value.try_into()?))
}
}
impl<'a> TryFrom<&'a mut [u8]> for &'a mut Digest {
type Error = TryFromSliceError;
fn try_from(value: &'a mut [u8]) -> Result<Self, Self::Error> {
Ok(Digest::from_mut(value.try_into()?))
}
}
#[cfg(feature = "std")]
impl TryFrom<Vec<u8>> for Digest {
type Error = Vec<u8>;
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Ok(Self(value.try_into()?))
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ParseDigestError {
InvalidLength,
InvalidHexCharacter { c: char, index: usize },
}
impl Display for ParseDigestError {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
Self::InvalidLength => write!(f, "{}", ParseHexError::InvalidLength),
Self::InvalidHexCharacter { c, index } => {
let (c, index) = (*c, *index);
write!(f, "{}", ParseHexError::InvalidHexCharacter { c, index })
}
}
}
}
impl From<ParseHexError> for ParseDigestError {
fn from(err: ParseHexError) -> Self {
match err {
ParseHexError::InvalidLength => Self::InvalidLength,
ParseHexError::InvalidHexCharacter { c, index } => {
Self::InvalidHexCharacter { c, index }
}
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseDigestError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn hex_formatting() {
let digest = Digest([0xee; 32]);
assert_eq!(
format!("{digest:?}"),
"Digest(0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee)"
);
assert_eq!(
format!("{digest}"),
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
);
assert_eq!(
format!("{digest:x}"),
"eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
);
assert_eq!(
format!("{digest:#x}"),
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"
);
assert_eq!(
format!("{digest:X}"),
"EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
);
assert_eq!(
format!("{digest:#X}"),
"0xEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"
);
}
}