fcrc 0.1.0

Fast-CRC is a generic CRC implementation using SIMD instructions when available and a fallback when not.
Documentation
#![allow(clippy::unreadable_literal)]
#![allow(non_camel_case_types)]
//! Contains lists of models from
//! [Catalogue of parametrised CRC algorithms](https://reveng.sourceforge.io/crc-catalogue/all.htm).

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
/// The parameters that uniquely define a CRC.
/// Hopefully any CRC can be described with these parameters,
/// and then implemented in a parameterized way.
pub struct Model<T> {
    /// Number of bits needed to store the CRC;
    /// One less than the degree of the polynomial.
    pub width: u32,
    /// The generator polynomial `P(x) = a_i * x^i + a_(i-1) * x^(i-1) ... + a_0*x^0` for degree
    /// `i`, where the LSB (bit 0) represents `a_0` and the MSB (bit `i-1`) represents `a_(i-1)`.
    /// `a_i` is always one, so it is ommited (it wouldn't fit into `width` bits anyways).
    pub poly: T,
    /// The initial value, before reading the first message bit, in the same form as `poly`.
    pub init: T,
    /// If true, the characters of the input message are read bit-by-bit LSB first.
    /// If false, the characters of the input message are read bit-by-bit MSB first.
    pub refin: bool,
    /// If true, the `width` bits of the register are reflected (order reversed) before applying
    /// the final xor.
    pub refout: bool,
    /// The final xor to apply to the calculated crc after the message has been read and bits
    /// optionaly reflected.
    pub xorout: T,
    /// The resulting checksum of the UTF-8 string "123456789".
    pub check: T,
    /// The crc of an error free code word without applyinf the final xor.
    pub residue: T,
    /// The name assigned to this model.
    pub name: Option<&'static str>,
}

#[cfg(not(feature = "std"))]
use core::convert::TryFrom;
#[cfg(feature = "std")]
use std::convert::TryFrom;

// for each combination of two primitives, impl TryFrom
macro_rules! impl_alg_from {
    (u128) => {}; // uint128 isn't crc_catalog::Width
    ($T:ident) => {

        // if configured with the crc-catalog feature,
        // add conversions between crc_catalog::Algorithm and fcrc::Model;

        #[cfg(feature = "crc-catalog")]
        impl From<crc_catalog::Algorithm<$T>> for Model<$T> {
            fn from(value: crc_catalog::Algorithm<$T>) -> Self {
                Model {
                    width: value.width.into(),
                    poly: value.poly,
                    init: value.init,
                    refin: value.refin,
                    refout: value.refout,
                    xorout: value.xorout,
                    check: value.check,
                    residue: value.residue,
                    name: None,
                }
            }
        }
        #[cfg(feature = "crc-catalog")]
        impl From<Model<$T>> for crc_catalog::Algorithm<$T> {
            fn from(value: Model<$T>) -> Self {
                Self {
                    width: u8::try_from(value.width).expect("model bit width does not fit in u8"),
                    poly: value.poly,
                    init: value.init,
                    refin: value.refin,
                    refout: value.refout,
                    xorout: value.xorout,
                    check: value.check,
                    residue: value.residue,
                }
            }
        }

        // if test, then extern crc is available.
        // add conversions between extern crc::Algorithm and fcrc::Model;

        #[cfg(test)]
        impl From<crc::Algorithm<$T>> for Model<$T> {
            fn from(value: crc::Algorithm<$T>) -> Self {
                Model {
                    width: <$T>::BITS,
                    poly: value.poly,
                    init: value.init,
                    refin: value.refin,
                    refout: value.refout,
                    xorout: value.xorout,
                    check: value.check,
                    residue: value.residue,
                    name: None,
                }
            }
        }
        #[cfg(test)]
        impl TryFrom<Model<$T>> for crc::Algorithm<$T> {
            type Error = &'static str;
            fn try_from(value: Model<$T>) -> Result<Self, Self::Error> {
                if <$T>::BITS != value.width {
                    return Err("width is not exactly word size");
                }
                Ok(Self {
                    poly: value.poly,
                    init: value.init,
                    refin: value.refin,
                    refout: value.refout,
                    xorout: value.xorout,
                    check: value.check,
                    residue: value.residue,
                })
            }
        }
    };
    ($T:ident => $U:ident) => {

        // add conversion between Models for pairs of primitives

        impl TryFrom<Model<$T>> for Model<$U> {
            type Error = &'static str;
            fn try_from(value: Model<$T>) -> Result<Self, Self::Error> {
                if value.width > <$U>::BITS {
                    return Err("width does not fit");
                }
                Ok(Model {
                    width: value.width,
                    poly: <$U>::try_from(value.poly).map_err(|_| "poly does not fit")?,
                    init: <$U>::try_from(value.init).map_err(|_| "init does not fit")?,
                    refin: value.refin,
                    refout: value.refout,
                    xorout: <$U>::try_from(value.xorout).map_err(|_| "xorout does not fit")?,
                    check: <$U>::try_from(value.check).map_err(|_| "check does not fit")?,
                    residue: <$U>::try_from(value.residue).map_err(|_| "residue does not fit")?,
                    name: value.name,
                })
            }
        }
    };
    ($A:ident, $($rest:ident),+) => {
        // Take the list of primitives and turn it into pairs.
        // Lists all 2-combinations of the list,
        // and then each individual element.
        $(
            impl_alg_from!($A => $rest);
            impl_alg_from!($rest => $A);
        )*
        impl_alg_from!($A);
        impl_alg_from!($($rest),*);
    };
}

impl_alg_from!(u8, u16, u32, u64, u128);

// include the generated list of models.
// This is generated from build/generate_models.rs

#[path = "generated_models.rs"]
mod generated_models;
pub use generated_models::*;