use crate::memory::Work;
use core::cmp::Ordering;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Resolution<T> {
Supported(T),
Unsupported,
Malformed,
}
pub trait Digest<'a> {
type Identity;
type State;
type Output: AsRef<[u8]>;
type Error;
fn width(&self, identity: &Self::Identity) -> Option<usize>;
fn compare<W: Work>(
&self,
left: &Self::Identity,
right: &Self::Identity,
execution: &W,
) -> Result<Ordering, Self::Error>;
fn start<W: Work>(
&'a self,
identity: &Self::Identity,
execution: &W,
) -> Result<Resolution<Self::State>, Self::Error>;
fn update<W: Work>(
&self,
state: &mut Self::State,
bytes: &[u8],
execution: &W,
) -> Result<(), Self::Error>;
fn finish<W: Work>(
&self,
state: Self::State,
execution: &W,
) -> Result<Self::Output, Self::Error>;
fn matches<W: Work>(
&self,
identity: &Self::Identity,
output: &Self::Output,
expected: &[u8],
execution: &W,
) -> Result<bool, Self::Error>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OnceError<E> {
Provider(E),
OutputWidth {
expected: usize,
actual: usize,
},
}
pub fn matches_once<'a, D: Digest<'a> + ?Sized, W: Work>(
digest: &'a D,
identity: &D::Identity,
input: &[u8],
expected: &[u8],
execution: &W,
) -> Result<Resolution<bool>, OnceError<D::Error>> {
let mut state = match digest
.start(identity, execution)
.map_err(OnceError::Provider)?
{
Resolution::Supported(state) => state,
Resolution::Unsupported => return Ok(Resolution::Unsupported),
Resolution::Malformed => return Ok(Resolution::Malformed),
};
digest
.update(&mut state, input, execution)
.map_err(OnceError::Provider)?;
let output = digest
.finish(state, execution)
.map_err(OnceError::Provider)?;
if let Some(expected) = digest.width(identity) {
let actual = output.as_ref().len();
if actual != expected {
return Err(OnceError::OutputWidth { expected, actual });
}
}
digest
.matches(identity, &output, expected, execution)
.map(Resolution::Supported)
.map_err(OnceError::Provider)
}