crcany-core 0.0.2

Core implementation of crcany library.
Documentation
use crate::util::reverse;

mod parse;

#[derive(Clone)]
pub struct Spec {
    /// number of bits in the CRC (the degree of the polynomial)
    pub width: u16,
    /// polynomial representation (sans x^width)
    pub poly: u128,
    /// register initial value before first message bit
    pub init: u128,
    /// if true, reflect input (read bits LSB first)
    pub refin: bool,
    /// if true, reflect output
    pub refout: bool,
    /// final CRC is exclusive-or'ed with this
    pub xorout: u128,
    /// CRC of the nine ASCII bytes "123456789"
    pub check: u128,
    /// residue of the CRC
    pub residue: u128,
    /// text description of this CRC
    pub name: String,
}

impl Spec {
    pub fn reflect(&self) -> bool {
        self.refin
    }

    pub fn reverse(&self) -> bool {
        self.refin ^ self.refout
    }

    pub fn init(&self) -> u128 {
        let maybe_reversed = if self.refout {
            reverse(self.init, self.width)
        } else {
            self.init
        };

        maybe_reversed ^ self.xorout
    }

    pub fn poly(&self) -> u128 {
        if self.refin {
            reverse(self.poly, self.width)
        } else {
            self.poly
        }
    }
}

impl core::str::FromStr for Spec {
    type Err = parse::SpecParseError;

    fn from_str(s: &str) -> Result<Spec, parse::SpecParseError> {
        parse::read_spec(s)
    }
}

impl core::fmt::Display for Spec {
    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
        if f.alternate() {
            write!(
                f,
                "w={width} p={poly}{init} r={refin}{refout}{xorout} c={check}{residue} n={name}",
                width = self.width,
                poly = self.poly,
                init = if self.init == 0 {
                    "".into()
                } else {
                    format!("i={}", self.init)
                },
                refin = if self.refin { "t" } else { "f" },
                refout = if self.refout == self.refin {
                    "".into()
                } else {
                    format!("refo={}", if self.refout { "t" } else { "f" })
                },
                xorout = if self.xorout == 0 {
                    "".into()
                } else {
                    format!("x={}", self.xorout)
                },
                check = self.check,
                residue = if self.residue == 0 {
                    "".into()
                } else {
                    format!("res={}", self.residue)
                },
                name = self.name,
            )
        } else {
            write!(
                f,
                "width={width} poly={poly:#06x} init={init:#06x} refin={refin} refout={refout} xorout={xorout:#06x} check={check:#06x} residue={residue:#06x} name={name:?}",
                width = self.width,
                poly = self.poly,
                init = self.init,
                refin = self.refin,
                refout = self.refout,
                xorout = self.xorout,
                check = self.check,
                residue = self.residue,
                name = self.name,
            )
        }
    }
}

#[cfg(test)]
mod test {
    use super::*;

    const KERMIT_FULL: &str = "width=16 poly=0x1021 init=0x0000 refin=true refout=true xorout=0x0000 check=0x2189 residue=0x0000 name=\"KERMIT\"";
    const KERMIT_ABBR: &str = "w=16 p=4129 r=t c=8585 n=KERMIT";

    #[test]
    fn kermit_full() {
        let model = KERMIT_FULL.parse::<Spec>().unwrap();

        assert_eq!(KERMIT_FULL, format!("{}", model));
        assert_eq!(KERMIT_ABBR, format!("{:#}", model));
        assert_eq!(16, model.width);
        assert_eq!(0x1021, model.poly);
        assert_eq!(0, model.init);
        assert!(model.refin);
        assert!(model.refout);
        assert_eq!(0, model.xorout);
        assert_eq!(0x2189, model.check);
        assert_eq!(0, model.residue);
        assert_eq!("KERMIT", model.name);
    }

    #[test]
    fn kermit_abbr() {
        let model = KERMIT_ABBR.parse::<Spec>().unwrap();

        assert_eq!(KERMIT_FULL, format!("{}", model));
        assert_eq!(KERMIT_ABBR, format!("{:#}", model));
        assert_eq!(16, model.width);
        assert_eq!(0x1021, model.poly);
        assert_eq!(0, model.init);
        assert!(model.refin);
        assert!(model.refout);
        assert_eq!(0, model.xorout);
        assert_eq!(0x2189, model.check);
        assert_eq!(0, model.residue);
        assert_eq!("KERMIT", model.name);
    }
}