fcrc 0.1.0

Fast-CRC is a generic CRC implementation using SIMD instructions when available and a fallback when not.
Documentation
//! Generic CRC trait

use crate::Model;

/// A Crc trait valid for trait objects.
pub trait Crc<T>: core::fmt::Debug {
    /// Get the Model for this CRC.
    #[must_use]
    fn model(&self) -> &Model<T>;

    /// Calculate the CRC of the data
    #[must_use]
    fn checksum(&self, data: &[u8]) -> T {
        self.finalize(self.update(self.init(), data))
    }

    /// Get the initial value for the CRC
    #[must_use]
    fn init(&self) -> T;

    /// Update the CRC with new data
    #[must_use]
    fn update(&self, crc: T, data: &[u8]) -> T;

    /// Finalize the CRC
    #[must_use]
    fn finalize(&self, crc: T) -> T;

    /// Undo the finalize operation, so the crc can be updated again.
    #[must_use]
    fn undo_finalize(&self, crc: T) -> T;
}


/// A Crc value of any size.
#[cfg(feature = "std")]
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum CrcType {
    /// CRC type of size u8
    U8(u8),
    /// CRC type of size u16
    U16(u16),
    /// CRC type of size u32
    U32(u32),
    /// CRC type of size u64
    U64(u64),
    /// CRC type of size u128
    U128(u128),
}

macro_rules! fwd_fmt {
    ($($trait:path),*) => {
        $(
            #[cfg(feature = "std")]
            impl $trait for CrcType {
                fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
                    use CrcType::*;
                    match self {
                        U8(x) => <u8 as $trait>::fmt(x, fmt),
                        U16(x) => <u16 as $trait>::fmt(x, fmt),
                        U32(x) => <u32 as $trait>::fmt(x, fmt),
                        U64(x) => <u64 as $trait>::fmt(x, fmt),
                        U128(x) => <u128 as $trait>::fmt(x, fmt),
                    }
                }
            }
        )*
    }
}
use core::fmt::{Debug, Display, Octal, Binary, LowerHex, UpperHex};
fwd_fmt!(Debug, Display, Octal, Binary, LowerHex, UpperHex);


/// A CRC model of some any size.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GenericModel {
    /// CRC model of size u8
    U8(Model<u8>),
    /// CRC model of size u16
    U16(Model<u16>),
    /// CRC model of size u32
    U32(Model<u32>),
    /// CRC model of size u64
    U64(Model<u64>),
    /// CRC model of size u128
    U128(Model<u128>),
}

impl core::fmt::Display for GenericModel {
    fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
        use GenericModel::*;
        match self {
            U8(model) => {
                write!(
                    fmt,
                    "{}:\twidth={}\tpoly={:#X}\tinit={:#X}\trefin={:?}\trefout={:?}\txorout={:#X}\tcheck={:#X}\tresidue={:#X}",
                    model.name.unwrap_or("<unnamed>"),
                    model.width,
                    model.poly,
                    model.init,
                    model.refin,
                    model.refout,
                    model.xorout,
                    model.check,
                    model.residue
                )
            },
            U16(model) => {
                write!(
                    fmt,
                    "{}:\twidth={}\tpoly={:#X}\tinit={:#X}\trefin={:?}\trefout={:?}\txorout={:#X}\tcheck={:#X}\tresidue={:#X}",
                    model.name.unwrap_or("<unnamed>"),
                    model.width,
                    model.poly,
                    model.init,
                    model.refin,
                    model.refout,
                    model.xorout,
                    model.check,
                    model.residue
                )
            },
            U32(model) => {
                write!(
                    fmt,
                    "{}:\twidth={}\tpoly={:#X}\tinit={:#X}\trefin={:?}\trefout={:?}\txorout={:#X}\tcheck={:#X}\tresidue={:#X}",
                    model.name.unwrap_or("<unnamed>"),
                    model.width,
                    model.poly,
                    model.init,
                    model.refin,
                    model.refout,
                    model.xorout,
                    model.check,
                    model.residue
                )
            },
            U64(model) => {
                write!(
                    fmt,
                    "{}:\twidth={}\tpoly={:#X}\tinit={:#X}\trefin={:?}\trefout={:?}\txorout={:#X}\tcheck={:#X}\tresidue={:#X}",
                    model.name.unwrap_or("<unnamed>"),
                    model.width,
                    model.poly,
                    model.init,
                    model.refin,
                    model.refout,
                    model.xorout,
                    model.check,
                    model.residue
                )
            },
            U128(model) => {
                write!(
                    fmt,
                    "{}:\twidth={}\tpoly={:#X}\tinit={:#X}\trefin={:?}\trefout={:?}\txorout={:#X}\tcheck={:#X}\tresidue={:#X}",
                    model.name.unwrap_or("<unnamed>"),
                    model.width,
                    model.poly,
                    model.init,
                    model.refin,
                    model.refout,
                    model.xorout,
                    model.check,
                    model.residue
                )
            },
        }
    }
}

#[cfg(feature = "std")]
impl GenericModel {
    /// Create a new generic Slice by N.
    #[must_use]
    #[inline]
    pub fn slice_by<const N: usize>(&self) -> GenericCrc {
        match self {
            GenericModel::U8(m) => GenericCrc::U8(Box::new(crate::slice_by::Crc::<u8, N>::new(*m))),
            GenericModel::U16(m) => GenericCrc::U16(Box::new(crate::slice_by::Crc::<u16, N>::new(*m))),
            GenericModel::U32(m) => GenericCrc::U32(Box::new(crate::slice_by::Crc::<u32, N>::new(*m))),
            GenericModel::U64(m) => GenericCrc::U64(Box::new(crate::slice_by::Crc::<u64, N>::new(*m))),
            GenericModel::U128(m) => GenericCrc::U128(Box::new(crate::slice_by::Crc::<u128, N>::new(*m))),
        }
    }

    /// Get the width of this model
    #[must_use]
    #[inline]
    pub fn width(&self) -> u32 {
        match self {
            GenericModel::U8(m) => m.width,
            GenericModel::U16(m) => m.width,
            GenericModel::U32(m) => m.width,
            GenericModel::U64(m) => m.width,
            GenericModel::U128(m) => m.width,
        }
    }
}

/// A CRC algorithm of some any size.
#[cfg(feature = "std")]
#[derive(Debug)]
pub enum GenericCrc {
    /// CRC algorithm of size u8
    U8(Box<dyn Crc<u8>>),
    /// CRC algorithm of size u16
    U16(Box<dyn Crc<u16>>),
    /// CRC algorithm of size u32
    U32(Box<dyn Crc<u32>>),
    /// CRC algorithm of size u64
    U64(Box<dyn Crc<u64>>),
    /// CRC algorithm of size u128
    U128(Box<dyn Crc<u128>>),
}


#[cfg(feature = "std")]
impl GenericCrc {

    /// Get the Model for this CRC.
    #[must_use]
    #[inline]
    pub fn model(&self) -> GenericModel {
        match self {
            GenericCrc::U8(m)   => GenericModel::U8(m.model().clone()),
            GenericCrc::U16(m)  => GenericModel::U16(m.model().clone()),
            GenericCrc::U32(m)  => GenericModel::U32(m.model().clone()),
            GenericCrc::U64(m)  => GenericModel::U64(m.model().clone()),
            GenericCrc::U128(m) => GenericModel::U128(m.model().clone()),
        }
    }

    /// Calculate the CRC of the data
    #[must_use]
    #[inline]
    pub fn checksum(&self, data: &[u8]) -> CrcType {
        match self {
            GenericCrc::U8(m)   => CrcType::U8(m.checksum(data)),
            GenericCrc::U16(m)  => CrcType::U16(m.checksum(data)),
            GenericCrc::U32(m)  => CrcType::U32(m.checksum(data)),
            GenericCrc::U64(m)  => CrcType::U64(m.checksum(data)),
            GenericCrc::U128(m) => CrcType::U128(m.checksum(data)),
        }
    }

    /// Get the initial value for the CRC
    #[must_use]
    #[inline]
    pub fn init(&self) -> CrcType {
        match self {
            GenericCrc::U8(m)   => CrcType::U8(m.init()),
            GenericCrc::U16(m)  => CrcType::U16(m.init()),
            GenericCrc::U32(m)  => CrcType::U32(m.init()),
            GenericCrc::U64(m)  => CrcType::U64(m.init()),
            GenericCrc::U128(m) => CrcType::U128(m.init()),
        }
    }

    /// Update the CRC with new data
    #[must_use]
    #[inline]
    pub fn update(&self, crc: CrcType, data: &[u8]) -> CrcType {
        match (self, crc) {
            (GenericCrc::U8(s), CrcType::U8(c))     => CrcType::U8(s.update(c, data)),
            (GenericCrc::U16(s), CrcType::U16(c))   => CrcType::U16(s.update(c, data)),
            (GenericCrc::U32(s), CrcType::U32(c))   => CrcType::U32(s.update(c, data)),
            (GenericCrc::U64(s), CrcType::U64(c))   => CrcType::U64(s.update(c, data)),
            (GenericCrc::U128(s), CrcType::U128(c)) => CrcType::U128(s.update(c, data)),
            _ => panic!("crc type mismatch"),
        }
    }

    /// Finalize the CRC
    #[must_use]
    #[inline]
    pub fn finalize(&self, crc: CrcType) -> CrcType {
        match (self, crc) {
            (GenericCrc::U8(s), CrcType::U8(c))     => CrcType::U8(s.finalize(c)),
            (GenericCrc::U16(s), CrcType::U16(c))   => CrcType::U16(s.finalize(c)),
            (GenericCrc::U32(s), CrcType::U32(c))   => CrcType::U32(s.finalize(c)),
            (GenericCrc::U64(s), CrcType::U64(c))   => CrcType::U64(s.finalize(c)),
            (GenericCrc::U128(s), CrcType::U128(c)) => CrcType::U128(s.finalize(c)),
            _ => panic!("crc type mismatch"),
        }
    }
}