compact_argon2 0.2.0

Thin wrapper around the argon2 crate to improve ergonomics and provide a smaller serialization format
Documentation
use std::fmt::Display;
use std::str::FromStr;

use base64::Engine;

use crate::{BASE64_OUTPUT_SIZE, Hash, OUTPUT_SIZE, ParseError};

pub(crate) const BASE64: base64::engine::GeneralPurpose = base64::prelude::BASE64_URL_SAFE_NO_PAD;

#[derive(Debug, thiserror::Error)]
pub enum Base64ParseError {
    #[error(transparent)]
    Decode(#[from] base64::DecodeSliceError),
    #[error(transparent)]
    Parse(#[from] ParseError),
}

impl Hash {
    pub(crate) fn write_base64<'a>(&self, output: &'a mut [u8]) -> &'a str {
        let bytes = self.to_bytes();
        let encoded = BASE64.encode_slice(&bytes, output).unwrap();
        assert_eq!(encoded, BASE64_OUTPUT_SIZE);
        unsafe { str::from_utf8_unchecked(&output[..BASE64_OUTPUT_SIZE]) }
    }
}

impl FromStr for Hash {
    type Err = Base64ParseError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut bytes = [0u8; OUTPUT_SIZE];
        BASE64.decode_slice(s, &mut bytes)?;
        Ok(Hash::from_bytes(&bytes)?)
    }
}

impl Display for Hash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        let mut output = [0u8; BASE64_OUTPUT_SIZE];
        f.write_str(self.write_base64(&mut output))
    }
}

#[cfg(test)]
mod tests {
    extern crate test;

    use std::hint::black_box;

    use test::Bencher;

    use crate::tests::HASH;
    use crate::*;

    #[test]
    fn round_trip() {
        assert_eq!(HASH.to_string().parse::<Hash>().unwrap(), *HASH);
    }

    #[bench]
    fn to_str(bencher: &mut Bencher) {
        let hash = *HASH;

        bencher.iter(|| {
            let mut out = [0u8; BASE64_OUTPUT_SIZE];
            let hash = black_box(hash).write_base64(black_box(&mut out));
            black_box(hash);
        });
    }

    #[bench]
    fn to_alloc_string(bencher: &mut Bencher) {
        let hash = *HASH;

        bencher.iter(|| black_box(hash).to_string());
    }

    #[bench]
    fn from_string(bencher: &mut Bencher) {
        let string = HASH.to_string();

        bencher.iter(|| black_box(&*string).parse::<Hash>().unwrap());
    }
}