#![warn(
missing_debug_implementations,
missing_docs,
rust_2018_idioms,
unreachable_pub
)]
#![deny(rustdoc::broken_intra_doc_links)]
#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
pub mod traits;
use std::iter;
use std::marker::PhantomData;
use hybrid_array::{Array, ArraySize};
use traits::{Absorb, Cons, Consume, IOWord, List, Nil, Norm, Normalize, Squeeze, Use};
use typenum::Unsigned;
#[derive(Debug)]
pub enum Error {
ParameterUsageMismatch,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum SpongeOp {
Absorb(u32),
Squeeze(u32),
}
pub trait ToSpongeOp: IOWord {
fn to_sponge_op() -> SpongeOp;
}
impl<U: Unsigned> ToSpongeOp for Absorb<U> {
fn to_sponge_op() -> SpongeOp {
SpongeOp::Absorb(U::to_u32())
}
}
impl<U: Unsigned> ToSpongeOp for Squeeze<U> {
fn to_sponge_op() -> SpongeOp {
SpongeOp::Squeeze(U::to_u32())
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct IOPattern(pub Vec<SpongeOp>);
pub trait ToIOPattern {
fn to_iopattern() -> IOPattern;
}
impl ToIOPattern for Nil {
fn to_iopattern() -> IOPattern {
IOPattern(vec![])
}
}
impl<Item: ToSpongeOp, T: List + ToIOPattern> ToIOPattern for Cons<Item, T> {
fn to_iopattern() -> IOPattern {
IOPattern(
iter::once(<Item as ToSpongeOp>::to_sponge_op())
.chain(T::to_iopattern().0)
.collect(),
)
}
}
pub trait SpongeAPI {
type Acc;
type Value;
fn start(&mut self, p: IOPattern, domain_separator: Option<u32>, acc: &mut Self::Acc);
fn absorb(&mut self, length: u32, elements: &[Self::Value], acc: &mut Self::Acc);
fn squeeze(&mut self, length: u32, elements: &mut [Self::Value], acc: &mut Self::Acc);
fn finish(&mut self) -> Result<(), Error>;
}
#[derive(Debug)]
pub struct ExtraSponge<A: SpongeAPI, I: List> {
api: A,
_current_pattern: PhantomData<I>,
}
impl<A: SpongeAPI, I: List> ExtraSponge<A, I> {
fn new(api: A) -> ExtraSponge<A, I> {
ExtraSponge {
api,
_current_pattern: PhantomData,
}
}
fn repattern<J: List>(self) -> ExtraSponge<A, J> {
let res =
unsafe { std::mem::transmute_copy::<ExtraSponge<A, I>, ExtraSponge<A, J>>(&self) };
std::mem::forget(self);
res
}
}
impl<A: SpongeAPI, I: Normalize> ExtraSponge<A, I>
where
Norm<I>: ToIOPattern, {
pub fn start(
domain_separator: Option<u32>,
api: A,
acc: &mut A::Acc,
) -> ExtraSponge<A, Norm<I>> {
let mut extra_sponge: ExtraSponge<A, Norm<I>> = ExtraSponge::new(api);
extra_sponge
.api
.start(Norm::<I>::to_iopattern(), domain_separator, acc);
extra_sponge
}
}
impl<A: SpongeAPI, I: Normalize> ExtraSponge<A, I> {
pub fn absorb<U>(
mut self,
harray: Array<A::Value, U>,
acc: &mut A::Acc,
) -> ExtraSponge<A, Use<I, Absorb<U>>>
where
U: ArraySize<A::Value>,
I: Consume<Absorb<U>>,
{
self.api.absorb(U::to_u32(), harray.as_slice(), acc);
self.repattern()
}
}
impl<A: SpongeAPI, I: Normalize> ExtraSponge<A, I> {
pub fn squeeze<U>(
mut self,
harray: &mut Array<A::Value, U>,
acc: &mut A::Acc,
) -> ExtraSponge<A, Use<I, Squeeze<U>>>
where
U: ArraySize<A::Value>,
I: Consume<Squeeze<U>>,
{
self.api.squeeze(U::to_u32(), harray.as_mut_slice(), acc);
self.repattern()
}
}
impl<A: SpongeAPI, I: List> Drop for ExtraSponge<A, I> {
fn drop(&mut self) {
if I::is_empty() {
self.api
.finish()
.expect("SpongeAPI invariant violated: finish failed on an empty IO pattern");
} else {
panic!("SpongeAPI invariant violated: forgot to empty IO pattern before dropping it");
}
}
}
#[cfg(test)]
pub mod unit_tests;
#[cfg(test)]
mod tests {
use super::*;
use typenum::{U3, U5};
#[test]
fn test_to_sponge_op() {
assert_eq!(Absorb::<U5>::to_sponge_op(), SpongeOp::Absorb(5));
assert_eq!(Squeeze::<U5>::to_sponge_op(), SpongeOp::Squeeze(5));
}
#[test]
fn test_to_iopattern() {
assert_eq!(Nil::to_iopattern(), IOPattern(Vec::default()));
assert_eq!(
<iopat![Absorb::<U5>, Squeeze<U3>]>::to_iopattern(),
IOPattern(vec![SpongeOp::Absorb(5), SpongeOp::Squeeze(3)])
);
}
}