use core::marker::PhantomData;
use hybrid_array::{Array, ArraySize};
use crate::codec::Encode;
#[cfg(feature = "blake3")]
pub mod blake3;
#[cfg(feature = "sha2")]
pub mod sha2;
#[cfg(feature = "sha3")]
pub mod sha3;
pub struct Digest<T, P: DigestPrimitive, C> {
bytes: Array<u8, P::Size>,
_marker: PhantomData<fn() -> (T, C)>,
}
impl<T, P: DigestPrimitive, C> Clone for Digest<T, P, C>
where
Array<u8, P::Size>: Clone,
{
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
_marker: PhantomData,
}
}
}
impl<T, P: DigestPrimitive, C> PartialEq for Digest<T, P, C>
where
Array<u8, P::Size>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes
}
}
impl<T, P: DigestPrimitive, C> Eq for Digest<T, P, C> where Array<u8, P::Size>: Eq {}
impl<T, P: DigestPrimitive, C> core::hash::Hash for Digest<T, P, C>
where
Array<u8, P::Size>: core::hash::Hash,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}
impl<T, P: DigestPrimitive, C> Digest<T, P, C> {
#[allow(clippy::expect_used)] pub fn hash(value: &T) -> Self
where
C: Encode<T>,
{
let encoded = C::encode(value).expect("encoding failed");
let bytes = P::hash(&encoded);
Self::new(bytes)
}
#[must_use]
pub(crate) const fn new(bytes: Array<u8, P::Size>) -> Self {
Self {
bytes,
_marker: PhantomData,
}
}
#[must_use]
pub const fn as_array(&self) -> &Array<u8, P::Size> {
&self.bytes
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
#[must_use]
pub fn into_array(self) -> Array<u8, P::Size> {
self.bytes
}
}
pub trait DigestUnchecked<T, P: DigestPrimitive, C> {
#[must_use]
fn from_unchecked_array(bytes: Array<u8, P::Size>) -> Self;
}
impl<T, P: DigestPrimitive, C> DigestUnchecked<T, P, C> for Digest<T, P, C> {
fn from_unchecked_array(bytes: Array<u8, P::Size>) -> Self {
Self::new(bytes)
}
}
impl<T, P: DigestPrimitive, C> Copy for Digest<T, P, C> where Array<u8, P::Size>: Copy {}
impl<T, P: DigestPrimitive, C> core::fmt::Debug for Digest<T, P, C> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Digest({:02x?})", self.bytes.as_slice())
}
}
pub trait DigestPrimitive {
type Size: ArraySize;
fn hash(data: &[u8]) -> Array<u8, Self::Size>;
}
#[cfg(feature = "serde")]
impl<T, P: DigestPrimitive, C> serde::Serialize for Digest<T, P, C>
where
Array<u8, P::Size>: serde::Serialize,
{
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.bytes.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, T, P: DigestPrimitive, C> serde::Deserialize<'de> for Digest<T, P, C>
where
Array<u8, P::Size>: serde::Deserialize<'de>,
{
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let bytes = Array::deserialize(deserializer)?;
Ok(Self::new(bytes))
}
}
#[cfg(feature = "arbitrary")]
impl<'a, T, P: DigestPrimitive, C> arbitrary::Arbitrary<'a> for Digest<T, P, C>
where
Array<u8, P::Size>: arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
let bytes = Array::arbitrary(u)?;
Ok(Self::new(bytes))
}
}
#[cfg(feature = "bolero")]
impl<T: 'static, P: DigestPrimitive + 'static, C: 'static> bolero_generator::TypeGenerator
for Digest<T, P, C>
where
Array<u8, P::Size>: bolero_generator::TypeGenerator,
{
fn generate<D: bolero_generator::Driver>(driver: &mut D) -> Option<Self> {
let bytes = Array::generate(driver)?;
Some(Self::new(bytes))
}
}
#[cfg(feature = "proptest")]
impl<T: 'static, P: DigestPrimitive + 'static, C: 'static> proptest::arbitrary::Arbitrary
for Digest<T, P, C>
where
Array<u8, P::Size>: core::fmt::Debug,
{
type Parameters = ();
type Strategy = proptest::strategy::BoxedStrategy<Self>;
#[allow(clippy::expect_used)] fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
use hybrid_array::typenum::Unsigned;
use proptest::prelude::*;
proptest::collection::vec(any::<u8>(), P::Size::USIZE)
.prop_map(|v| Self::new(Array::try_from(v.as_slice()).expect("correct length")))
.boxed()
}
}
#[cfg(feature = "rkyv")]
pub mod archive {
use super::{Array, Digest, DigestPrimitive};
use alloc::vec::Vec;
use rkyv::{Archive, Deserialize, Serialize, rancor::Fallible};
#[derive(Debug, Archive, Serialize, Deserialize)]
#[rkyv(derive(Debug))]
pub struct DigestBytes {
bytes: Vec<u8>,
}
impl ArchivedDigestBytes {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl<T, P: DigestPrimitive, C> Archive for Digest<T, P, C> {
type Archived = ArchivedDigestBytes;
type Resolver = <DigestBytes as Archive>::Resolver;
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
let helper = DigestBytes {
bytes: self.bytes.as_slice().to_vec(),
};
helper.resolve(resolver, out);
}
}
impl<T, P: DigestPrimitive, C, S> Serialize<S> for Digest<T, P, C>
where
S: Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized,
{
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
let helper = DigestBytes {
bytes: self.bytes.as_slice().to_vec(),
};
helper.serialize(serializer)
}
}
impl<T, P: DigestPrimitive, C, D> Deserialize<Digest<T, P, C>, D> for ArchivedDigestBytes
where
D: Fallible + ?Sized,
D::Error: rkyv::rancor::Source,
{
#[allow(clippy::expect_used)] fn deserialize(&self, deserializer: &mut D) -> Result<Digest<T, P, C>, D::Error> {
let helper: DigestBytes =
<ArchivedDigestBytes as Deserialize<DigestBytes, D>>::deserialize(
self,
deserializer,
)?;
let bytes = Array::try_from(helper.bytes.as_slice()).expect("invalid digest length");
Ok(Digest::new(bytes))
}
}
}