use core::marker::PhantomData;
use hybrid_array::{
Array, ArraySize,
typenum::{IsLessOrEqual, True},
};
use crate::digest::{Digest, DigestPrimitive};
pub struct Fingerprint<T, N: ArraySize> {
bytes: Array<u8, N>,
_marker: PhantomData<fn() -> T>,
}
impl<T, N: ArraySize> Clone for Fingerprint<T, N>
where
Array<u8, N>: Clone,
{
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
_marker: PhantomData,
}
}
}
impl<T, N: ArraySize> Copy for Fingerprint<T, N> where Array<u8, N>: Copy {}
impl<T, N: ArraySize> PartialEq for Fingerprint<T, N>
where
Array<u8, N>: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.bytes == other.bytes
}
}
impl<T, N: ArraySize> Eq for Fingerprint<T, N> where Array<u8, N>: Eq {}
impl<T, N: ArraySize> PartialOrd for Fingerprint<T, N>
where
Array<u8, N>: PartialOrd,
{
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T, N: ArraySize> Ord for Fingerprint<T, N>
where
Array<u8, N>: Ord,
{
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
self.bytes.cmp(&other.bytes)
}
}
impl<T, N: ArraySize> core::hash::Hash for Fingerprint<T, N>
where
Array<u8, N>: core::hash::Hash,
{
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.bytes.hash(state);
}
}
impl<T, N: ArraySize> core::fmt::Debug for Fingerprint<T, N> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Fingerprint({:02x?})", self.bytes.as_slice())
}
}
impl<T, N: ArraySize> Fingerprint<T, N> {
#[must_use]
pub(crate) const fn new(bytes: Array<u8, N>) -> Self {
Self {
bytes,
_marker: PhantomData,
}
}
#[must_use]
#[allow(clippy::expect_used, clippy::indexing_slicing)] pub fn from_digest<P: DigestPrimitive, C>(digest: &Digest<T, P, C>) -> Self
where
N: IsLessOrEqual<P::Size, Output = True>,
{
let bytes = Array::try_from(&digest.as_bytes()[..N::USIZE])
.expect("N <= P::Size is enforced by type bound");
Self::new(bytes)
}
#[must_use]
pub const fn as_array(&self) -> &Array<u8, N> {
&self.bytes
}
#[must_use]
pub const fn as_bytes(&self) -> &[u8] {
self.bytes.as_slice()
}
#[must_use]
pub fn into_array(self) -> Array<u8, N> {
self.bytes
}
}
pub trait FingerprintUnchecked<T, N: ArraySize> {
#[must_use]
fn from_unchecked_array(bytes: Array<u8, N>) -> Self;
}
impl<T, N: ArraySize> FingerprintUnchecked<T, N> for Fingerprint<T, N> {
fn from_unchecked_array(bytes: Array<u8, N>) -> Self {
Self::new(bytes)
}
}
#[cfg(feature = "serde")]
impl<T, N: ArraySize> serde::Serialize for Fingerprint<T, N>
where
Array<u8, N>: 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, N: ArraySize> serde::Deserialize<'de> for Fingerprint<T, N>
where
Array<u8, N>: 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, N: ArraySize> arbitrary::Arbitrary<'a> for Fingerprint<T, N>
where
Array<u8, N>: 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, N: ArraySize + 'static> bolero_generator::TypeGenerator for Fingerprint<T, N>
where
Array<u8, N>: 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, N: ArraySize + 'static> proptest::arbitrary::Arbitrary for Fingerprint<T, N>
where
Array<u8, N>: core::fmt::Debug,
{
type Parameters = ();
type Strategy = proptest::strategy::BoxedStrategy<Self>;
#[allow(clippy::expect_used)] fn arbitrary_with((): Self::Parameters) -> Self::Strategy {
use proptest::prelude::*;
proptest::collection::vec(any::<u8>(), N::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, ArraySize, Fingerprint};
use alloc::vec::Vec;
use rkyv::{Archive, Deserialize, Serialize, rancor::Fallible};
#[derive(Debug, Archive, Serialize, Deserialize)]
#[rkyv(derive(Debug))]
pub struct FingerprintBytes {
bytes: Vec<u8>,
}
impl ArchivedFingerprintBytes {
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
&self.bytes
}
}
impl<T, N: ArraySize> Archive for Fingerprint<T, N> {
type Archived = ArchivedFingerprintBytes;
type Resolver = <FingerprintBytes as Archive>::Resolver;
fn resolve(&self, resolver: Self::Resolver, out: rkyv::Place<Self::Archived>) {
let helper = FingerprintBytes {
bytes: self.bytes.as_slice().to_vec(),
};
helper.resolve(resolver, out);
}
}
impl<T, N: ArraySize, S> Serialize<S> for Fingerprint<T, N>
where
S: Fallible + rkyv::ser::Allocator + rkyv::ser::Writer + ?Sized,
{
fn serialize(&self, serializer: &mut S) -> Result<Self::Resolver, S::Error> {
let helper = FingerprintBytes {
bytes: self.bytes.as_slice().to_vec(),
};
helper.serialize(serializer)
}
}
impl<T, N: ArraySize, D> Deserialize<Fingerprint<T, N>, D> for ArchivedFingerprintBytes
where
D: Fallible + ?Sized,
D::Error: rkyv::rancor::Source,
{
#[allow(clippy::expect_used)] fn deserialize(&self, deserializer: &mut D) -> Result<Fingerprint<T, N>, D::Error> {
let helper: FingerprintBytes = <ArchivedFingerprintBytes as Deserialize<
FingerprintBytes,
D,
>>::deserialize(self, deserializer)?;
let bytes =
Array::try_from(helper.bytes.as_slice()).expect("invalid fingerprint length");
Ok(Fingerprint::new(bytes))
}
}
}