mod error;
mod md5;
mod sha1;
mod sha256;
use core::fmt;
use std::io;
pub use error::ChecksumMismatchError;
pub use md5::Md5;
pub use sha1::Sha1;
pub use sha256::Sha256;
use zerocopy::LE;
use crate::read::ReadBytesExt;
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Checksum {
Adler32(u32),
Crc32(u32),
MD5(Md5),
Sha1(Sha1),
Sha256(Sha256),
Check([u8; 4]),
}
impl Checksum {
#[must_use]
#[inline]
pub const fn new_adler32(adler32: u32) -> Self {
Self::Adler32(adler32)
}
#[must_use]
#[inline]
pub const fn new_crc32(crc32: u32) -> Self {
Self::Crc32(crc32)
}
#[must_use]
#[inline]
pub const fn new_md5(md5: [u8; 16]) -> Self {
Self::MD5(Md5::new(md5))
}
#[must_use]
#[inline]
pub const fn new_sha1(sha1: [u8; 20]) -> Self {
Self::Sha1(Sha1::new(sha1))
}
#[must_use]
#[inline]
pub const fn new_sha256(sha256: [u8; 32]) -> Self {
Self::Sha256(Sha256::new(sha256))
}
pub fn read_adler32<R: io::Read>(mut reader: R) -> io::Result<Self> {
reader.read_u32::<LE>().map(Self::Adler32)
}
pub fn read_crc32<R: io::Read>(mut reader: R) -> io::Result<Self> {
reader.read_u32::<LE>().map(Self::Crc32)
}
pub fn read_md5<R: io::Read>(mut reader: R) -> io::Result<Self> {
reader.read_t::<Md5>().map(Self::MD5)
}
pub fn read_sha1<R: io::Read>(mut reader: R) -> io::Result<Self> {
reader.read_t::<Sha1>().map(Self::Sha1)
}
pub fn read_sha256<R: io::Read>(mut reader: R) -> io::Result<Self> {
reader.read_t::<Sha256>().map(Self::Sha256)
}
#[must_use]
#[inline]
pub const fn is_adler32(&self) -> bool {
matches!(self, Self::Adler32(_))
}
#[must_use]
#[inline]
pub const fn is_crc32(&self) -> bool {
matches!(self, Self::Crc32(_))
}
#[must_use]
#[inline]
pub const fn is_md5(&self) -> bool {
matches!(self, Self::MD5(_))
}
#[must_use]
#[inline]
pub const fn is_sha1(&self) -> bool {
matches!(self, Self::Sha1(_))
}
#[must_use]
#[inline]
pub const fn is_sha256(&self) -> bool {
matches!(self, Self::Sha256(_))
}
}
impl fmt::Debug for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Adler32(adler32) => f.debug_tuple("Adler32").field(adler32).finish(),
Self::Crc32(crc32) => f.debug_tuple("Crc32").field(crc32).finish(),
Self::MD5(md5) => f.debug_tuple("MD5").field(md5).finish(),
Self::Sha1(sha1) => f.debug_tuple("SHA1").field(sha1).finish(),
Self::Sha256(sha256) => f.debug_tuple("SHA256").field(sha256).finish(),
Self::Check(check) => f.debug_tuple("Check").field(check).finish(),
}
}
}
impl fmt::Display for Checksum {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Adler32(adler32) => write!(f, "{adler32}"),
Self::Crc32(crc32) => write!(f, "{crc32}"),
Self::MD5(md5) => write!(f, "{md5}"),
Self::Sha1(sha1) => write!(f, "{sha1}"),
Self::Sha256(sha256) => write!(f, "{sha256}"),
Self::Check(check) => write!(f, "{:?}", u32::from_le_bytes(*check)),
}
}
}
impl Checksum {
#[cfg(feature = "extract")]
pub fn validate(&self, data: &[u8]) -> Result<(), ChecksumMismatchError> {
match self {
Self::Adler32(expected) => {
let actual = simd_adler32::adler32(&data);
if *expected != actual {
return Err(ChecksumMismatchError::new_adler32(*expected, actual));
}
}
Self::Crc32(expected) => {
let actual = crc32fast::hash(data);
if *expected != actual {
return Err(ChecksumMismatchError::new_crc32(*expected, actual));
}
}
Self::MD5(expected) => {
use ::md5::{Digest, Md5};
let actual = Md5::digest(data).0;
if *expected != actual {
return Err(ChecksumMismatchError::new_md5(
expected.into_inner(),
actual,
));
}
}
Self::Sha1(expected) => {
use ::sha1::{Digest, Sha1};
let actual = Sha1::digest(data).0;
if *expected != actual {
return Err(ChecksumMismatchError::new_sha1(
expected.into_inner(),
actual,
));
}
}
Self::Sha256(expected) => {
use sha2::{Digest, Sha256};
let actual = Sha256::digest(data).0;
if *expected != actual {
return Err(ChecksumMismatchError::new_sha256(
expected.into_inner(),
actual,
));
}
}
Self::Check(_) => {
}
}
Ok(())
}
}
impl Default for Checksum {
fn default() -> Self {
Self::Adler32(0)
}
}