fcrc 0.1.0

Fast-CRC is a generic CRC implementation using SIMD instructions when available and a fallback when not.
Documentation
//! The reference implementation, to which other implementations are checked.

use crate::Model;

/// A reference CRC implementation with a focus on correctness and readability over speed.
/// Calculates the CRC bit-by-bit, and should be easy to follow.
#[derive(Clone, Debug)]
pub struct Crc<W>(Model<W>);

macro_rules! impl_reference_crc {
    (@impl $T:ident : qc $alg:ident ) => {
        #[cfg(test)]
        mod $T {
            use super::*;
            #[test]
            fn check() {
                for alg in crate::models::$alg {
                    let check = alg.check;
                    let residue = alg.residue;
                    let crc = Crc::<$T>::new(alg);
                    assert_eq!(check, crc.check());
                    if alg.width % 8 == 0 {
                        assert_eq!(residue, crc.residue(), "{:?}", alg.name);
                    }
                    assert_eq!(
                        crc.checksum(b"123456789"),
                        crc.finalize(crc.update(crc.undo_finalize(crc.checksum(b"123456")), b"789"))
                    );
                }
            }
            use std::convert::TryFrom;
            lazy_static::lazy_static! {
                static ref ALG_PAIRS: Vec<(Model<$T>, crc::Algorithm<$T>)> = 
                    crate::models::$alg
                        .iter()
                        .filter(|alg| alg.width == <$T>::BITS)
                        .map(|a| (*a, crc::Algorithm::<$T>::try_from(*a).unwrap()))
                        .collect();
            }

            quickcheck::quickcheck! {
                fn compare_against_extern_crc(data: Vec<u8>) -> bool {
                    for (alg, other_alg) in ALG_PAIRS.iter() {
                        if alg.width != <$T>::BITS { continue; }
                        let crc = Crc::<$T>::new(*alg);
                        let other = crc::Crc::<$T>::new(other_alg);
                        let mut d = other.digest();
                        d.update(&data);
                        let other_crc = d.finalize();
                        assert_eq!(crc.checksum(&data), other_crc);
                    }
                    true
                }
            }
        }
    };
    (@impl $T:ident : noqc $alg:ident ) => {
        #[cfg(test)]
        mod $T {
            use super::*;
            #[test]
            fn check() {
                for alg in crate::models::$alg {
                    let check = alg.check;
                    let residue = alg.residue;
                    let crc = Crc::<$T>::new(alg);
                    assert_eq!(check, crc.check());
                    if alg.width % 8 == 0 {
                        assert_eq!(residue, crc.residue(), "{:?}", alg.name);
                    }
                    assert_eq!(
                        crc.checksum(b"123456789"),
                        crc.finalize(crc.update(crc.undo_finalize(crc.checksum(b"123456")), b"789"))
                    );
                }
            }
        }
    };
    ($($T:ident : $qc:ident $alg:ident ),+) => {
        $(
            impl_reference_crc!($T);
            impl_reference_crc!(@impl $T : $qc $alg);
        )*
    };
    ($T:ty) => {
        #[doc = concat!("An implemenation of CRC for ", stringify!($T))]
        impl Crc<$T> {
            /// Create a new CRC calculator (reference implementation)
            #[doc = concat!("that uses the primitive ", stringify!($T))]
            #[must_use]
            pub const fn new(model: Model<$T>) -> Self {
                Self(model)
            }

            /// Get the model for this CRC computation
            #[must_use]
            pub const fn model(&self) -> &Model<$T> {
                &self.0
            }

            /// Calculate the checksum of all of this data.
            #[must_use]
            pub const fn checksum(&self, data: &[u8]) -> $T {
                // initialize, update, and finalize the state.
                self.finalize(self.update(self.init(), data))
            }

            /// Get the initial state.
            #[must_use]
            pub const fn init(&self) -> $T {
                self.0.init
            }

            /// Update the state with new data.
            #[must_use]
            pub const fn update(&self, mut crc: $T, data: &[u8]) -> $T {
                // we use the most significant bits for this calculation,
                // so map the poly and current crc to the msb of the data type.
                // We can do this because '[R(x) mod P(x)] == [R(x) mod (P(x) << n)] >> n'.
                //
                // This is only necessary for widths that don't fit evenly into a primitive type.
                let poly = self.0.poly << (<$T>::BITS - self.0.width);
                crc <<= <$T>::BITS - self.0.width;

                // for each byte of input ...
                let mut i = 0;
                while i < data.len() {
                    // if the model says reflect input bits,
                    // then do it
                    let byte = if self.0.refin {
                        data[i].reverse_bits()
                    } else {
                        data[i]
                    };

                    // XOR the byte into the high byte of the CRC.
                    crc ^= (byte as $T) << (<$T>::BITS - 8);

                    // for each of the 8 newly added bits...
                    let mut j = 0;
                    while j < 8 {
                        // if the highest bit is set, then crc is divisible by poly,
                        // so subtract in the polynomial to continue towards the remainder.
                        // This is a bit-by-bit remainder of two polynomials in GF(2).
                        if 0 == (crc & (1 << (<$T>::BITS - 1))) {
                            crc <<= 1;
                        } else {
                            crc = (crc << 1) ^ poly;
                        }
                        // A simple branchless version:
                        // crc = (crc << 1) ^ ((crc >> (<$T>::BITS - 1)) * self.0.poly);

                        j += 1;
                    }

                    i += 1;
                }
                // map the top bits back to the bottom
                crc >> (<$T>::BITS - self.0.width)
            }

            /// Finalize the state.
            #[must_use]
            pub const fn finalize(&self, mut crc: $T) -> $T {
                // if the model says to reflect output bits,
                // then do it
                if self.0.refout {
                    crc = crc.reverse_bits() >> (<$T>::BITS - self.0.width);
                }
                // apply the final xor
                crc ^ self.0.xorout
            }

            /// Undo the finalization of a CRC, so that it can be further updated.
            #[must_use]
            pub const fn undo_finalize(&self, mut crc: $T) -> $T {
                // do the reverse of the operations above
                crc ^= self.0.xorout;
                if self.0.refout {
                    crc = crc.reverse_bits() >> (<$T>::BITS - self.0.width);
                }
                crc
            }

            /// Calculate a CRC of the check data b"123456789",
            /// to be used to verify the model against it's known check.
            #[cfg(test)]
            #[must_use]
            const fn check(&self) -> $T {
                self.checksum(b"123456789")
            }

            /// Calculate the residue of this model,
            /// to be used to verify the model against it's known residue.
            #[cfg(test)]
            #[must_use]
            fn residue(&self) -> $T {
                let zeros = vec![0u8; self.0.width as usize / 8];
                let mut init = self.0.xorout;
                if self.0.refout {
                    init = init.reverse_bits();
                }
                let mut out = self.update(init, &zeros);
                if self.0.refin {
                    out = out.reverse_bits();
                }
                out
            }
        }
        impl crate::generic::Crc<$T> for Crc<$T> {
            #[inline]
            fn model(&self) -> &Model<$T> {
                Self::model(self)
            }
            #[inline]
            fn checksum(&self, data: &[u8]) -> $T {
                Self::finalize(self, Self::update(self, Self::init(self), data))
            }
            #[inline]
            fn init(&self) -> $T {
                Self::init(self)
            }
            #[inline]
            fn update(&self, crc: $T, data: &[u8]) -> $T {
                Self::update(self, crc, data)
            }
            #[inline]
            fn finalize(&self, crc: $T) -> $T {
                Self::finalize(self, crc)
            }
            #[inline]
            fn undo_finalize(&self, crc: $T) -> $T {
                Self::undo_finalize(self, crc)
            }
        }
    }
}

impl_reference_crc!(u8: qc ALL_CRC_8, u16: qc ALL_CRC_16, u32: qc ALL_CRC_32, u64: qc ALL_CRC_64, u128: noqc ALL_CRC_128);