From 384fb68b7af4a06bedbddf2624c715070306a29a Mon Sep 17 00:00:00 2001 From: Bartel Sielski Date: Sun, 17 Oct 2021 18:06:46 +0200 Subject: [PATCH] Add support for CRC algorithms which have a non-power of 2 width --- Cargo.toml.orig | 2 +- README.md | 1 + benches/bench.rs | 20 +++++++- src/crc16.rs | 15 ++++-- src/crc32.rs | 15 ++++-- src/crc64.rs | 15 ++++-- src/crc8.rs | 9 ++-- src/lib.rs | 1 + src/table.rs | 45 ++++++++++++++--- src/util.rs | 100 ++++++++++++++++++++----------------- tests/crc.rs | 125 ++++++++++++++++++++++++++++++++++++++++++----- 11 files changed, 266 insertions(+), 82 deletions(-) diff --git a/Cargo.toml.orig b/Cargo.toml.orig index b787734..3f24e44 100644 --- a/Cargo.toml.orig +++ b/Cargo.toml.orig @@ -15,4 +15,4 @@ categories = ["algorithms", "no-std"] edition = "2018" [dependencies] -crc-catalog = "1.1.1" +crc-catalog = "2.0.1" diff --git a/README.md b/README.md index eefae7b..88bad96 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ assert_eq!(CASTAGNOLI.checksum(b"123456789"), 0xe3069283); // use custom algorithm const CUSTOM_ALG: Algorithm = Algorithm { + width: 16, poly: 0x8005, init: 0xffff, refin: false, diff --git a/benches/bench.rs b/benches/bench.rs index 520e3f7..a5fc4b7 100644 --- a/benches/bench.rs +++ b/benches/bench.rs @@ -5,6 +5,7 @@ use criterion::{Benchmark, Criterion, Throughput}; pub const BLUETOOTH: Crc = Crc::::new(&CRC_8_BLUETOOTH); pub const X25: Crc = Crc::::new(&CRC_16_IBM_SDLC); pub const CASTAGNOLI: Crc = Crc::::new(&CRC_32_ISCSI); +pub const GSM_40: Crc = Crc::::new(&CRC_40_GSM); pub const ECMA: Crc = Crc::::new(&CRC_64_ECMA_182); fn crc8(c: &mut Criterion) { @@ -37,6 +38,16 @@ fn crc32(c: &mut Criterion) { ); } +fn crc40(c: &mut Criterion) { + let mut digest = GSM_40.digest(); + let bytes = vec![0u8; 1_000_000]; + c.bench( + "crc40", + Benchmark::new("crc40", move |b| b.iter(|| digest.update(&bytes))) + .throughput(Throughput::Bytes(1_000_000)), + ); +} + fn crc64(c: &mut Criterion) { let mut digest = ECMA.digest(); let bytes = vec![0u8; 1_000_000]; @@ -50,5 +61,12 @@ fn crc64(c: &mut Criterion) { criterion_group!(crc8_benches, crc8); criterion_group!(crc16_benches, crc16); criterion_group!(crc32_benches, crc32); +criterion_group!(crc40_benches, crc40); criterion_group!(crc64_benches, crc64); -criterion_main!(crc8_benches, crc16_benches, crc32_benches, crc64_benches); +criterion_main!( + crc8_benches, + crc16_benches, + crc32_benches, + crc40_benches, + crc64_benches, +); diff --git a/src/crc16.rs b/src/crc16.rs index d6474da..b88669f 100644 --- a/src/crc16.rs +++ b/src/crc16.rs @@ -3,7 +3,7 @@ use crate::table::crc16_table; impl Crc { pub const fn new(algorithm: &'static Algorithm) -> Self { - let table = crc16_table(algorithm.poly, algorithm.refin); + let table = crc16_table(algorithm.width, algorithm.poly, algorithm.refin); Self { algorithm, table } } @@ -15,9 +15,9 @@ impl Crc { const fn init(&self) -> u16 { if self.algorithm.refin { - self.algorithm.init.reverse_bits() + self.algorithm.init.reverse_bits() >> (u16::BITS as u8 - self.algorithm.width) } else { - self.algorithm.init + self.algorithm.init << (u16::BITS as u8 - self.algorithm.width) } } @@ -29,12 +29,14 @@ impl Crc { let mut i = 0; if self.algorithm.refin { while i < bytes.len() { - crc = self.table_entry(crc ^ bytes[i] as u16) ^ (crc >> 8); + let table_index = crc ^ bytes[i] as u16; + crc = self.table_entry(table_index) ^ (crc >> 8); i += 1; } } else { while i < bytes.len() { - crc = self.table_entry(bytes[i] as u16 ^ (crc >> 8)) ^ (crc << 8); + let table_index = (crc >> (u16::BITS - 8)) ^ bytes[i] as u16; + crc = self.table_entry(table_index) ^ (crc << 8); i += 1; } } @@ -45,6 +47,9 @@ impl Crc { if self.algorithm.refin ^ self.algorithm.refout { crc = crc.reverse_bits(); } + if !self.algorithm.refout { + crc >>= u16::BITS as u8 - self.algorithm.width; + } crc ^ self.algorithm.xorout } diff --git a/src/crc32.rs b/src/crc32.rs index 6ac49cf..d7e7767 100644 --- a/src/crc32.rs +++ b/src/crc32.rs @@ -3,7 +3,7 @@ use crate::table::crc32_table; impl Crc { pub const fn new(algorithm: &'static Algorithm) -> Self { - let table = crc32_table(algorithm.poly, algorithm.refin); + let table = crc32_table(algorithm.width, algorithm.poly, algorithm.refin); Self { algorithm, table } } @@ -15,9 +15,9 @@ impl Crc { const fn init(&self) -> u32 { if self.algorithm.refin { - self.algorithm.init.reverse_bits() + self.algorithm.init.reverse_bits() >> (u32::BITS as u8 - self.algorithm.width) } else { - self.algorithm.init + self.algorithm.init << (u32::BITS as u8 - self.algorithm.width) } } @@ -29,12 +29,14 @@ impl Crc { let mut i = 0; if self.algorithm.refin { while i < bytes.len() { - crc = self.table_entry(crc ^ bytes[i] as u32) ^ (crc >> 8); + let table_index = crc ^ bytes[i] as u32; + crc = self.table_entry(table_index) ^ (crc >> 8); i += 1; } } else { while i < bytes.len() { - crc = self.table_entry(bytes[i] as u32 ^ (crc >> 24)) ^ (crc << 8); + let table_index = (crc >> (u32::BITS - 8)) ^ bytes[i] as u32; + crc = self.table_entry(table_index) ^ (crc << 8); i += 1; } } @@ -45,6 +47,9 @@ impl Crc { if self.algorithm.refin ^ self.algorithm.refout { crc = crc.reverse_bits(); } + if !self.algorithm.refout { + crc >>= u32::BITS as u8 - self.algorithm.width; + } crc ^ self.algorithm.xorout } diff --git a/src/crc64.rs b/src/crc64.rs index b405cd9..52cb0af 100644 --- a/src/crc64.rs +++ b/src/crc64.rs @@ -3,7 +3,7 @@ use crate::table::crc64_table; impl Crc { pub const fn new(algorithm: &'static Algorithm) -> Self { - let table = crc64_table(algorithm.poly, algorithm.refin); + let table = crc64_table(algorithm.width, algorithm.poly, algorithm.refin); Self { algorithm, table } } @@ -15,9 +15,9 @@ impl Crc { const fn init(&self) -> u64 { if self.algorithm.refin { - self.algorithm.init.reverse_bits() + self.algorithm.init.reverse_bits() >> (u64::BITS as u8 - self.algorithm.width) } else { - self.algorithm.init + self.algorithm.init << (u64::BITS as u8 - self.algorithm.width) } } @@ -29,12 +29,14 @@ impl Crc { let mut i = 0; if self.algorithm.refin { while i < bytes.len() { - crc = self.table_entry(crc ^ bytes[i] as u64) ^ (crc >> 8); + let table_index = crc ^ bytes[i] as u64; + crc = self.table_entry(table_index) ^ (crc >> 8); i += 1; } } else { while i < bytes.len() { - crc = self.table_entry(bytes[i] as u64 ^ (crc >> 56)) ^ (crc << 8); + let table_index = (crc >> (u64::BITS - 8)) ^ bytes[i] as u64; + crc = self.table_entry(table_index) ^ (crc << 8); i += 1; } } @@ -45,6 +47,9 @@ impl Crc { if self.algorithm.refin ^ self.algorithm.refout { crc = crc.reverse_bits(); } + if !self.algorithm.refout { + crc >>= u64::BITS as u8 - self.algorithm.width; + } crc ^ self.algorithm.xorout } diff --git a/src/crc8.rs b/src/crc8.rs index f37935d..0cf45c1 100644 --- a/src/crc8.rs +++ b/src/crc8.rs @@ -3,7 +3,7 @@ use crate::table::crc8_table; impl Crc { pub const fn new(algorithm: &'static Algorithm) -> Self { - let table = crc8_table(algorithm.poly, algorithm.refin); + let table = crc8_table(algorithm.width, algorithm.poly, algorithm.refin); Self { algorithm, table } } @@ -15,9 +15,9 @@ impl Crc { const fn init(&self) -> u8 { if self.algorithm.refin { - self.algorithm.init.reverse_bits() + self.algorithm.init.reverse_bits() >> (u8::BITS as u8 - self.algorithm.width) } else { - self.algorithm.init + self.algorithm.init << (u8::BITS as u8 - self.algorithm.width) } } @@ -40,6 +40,9 @@ impl Crc { if self.algorithm.refin ^ self.algorithm.refout { crc = crc.reverse_bits(); } + if !self.algorithm.refout { + crc >>= u8::BITS as u8 - self.algorithm.width; + } crc ^ self.algorithm.xorout } diff --git a/src/lib.rs b/src/lib.rs index 7944e19..99a30f6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -14,6 +14,7 @@ //! //! // use custom algorithm //! const CUSTOM_ALG: Algorithm = Algorithm { +//! width: 16, //! poly: 0x8005, //! init: 0xffff, //! refin: false, diff --git a/src/table.rs b/src/table.rs index 1e7ad75..a921ec2 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1,6 +1,13 @@ use crate::util::*; -pub(crate) const fn crc8_table(poly: u8, reflect: bool) -> [u8; 256] { +pub(crate) const fn crc8_table(width: u8, poly: u8, reflect: bool) -> [u8; 256] { + let poly = if reflect { + let poly = poly.reverse_bits(); + poly >> (u8::BITS as u8 - width) + } else { + poly << (u8::BITS as u8 - width) + }; + let mut table = [0u8; 256]; let mut i = 0; while i < table.len() { @@ -9,29 +16,53 @@ pub(crate) const fn crc8_table(poly: u8, reflect: bool) -> [u8; 256] { } table } -pub(crate) const fn crc16_table(poly: u16, reflect: bool) -> [u16; 256] { + +pub(crate) const fn crc16_table(width: u8, poly: u16, reflect: bool) -> [u16; 256] { + let poly = if reflect { + let poly = poly.reverse_bits(); + poly >> (u16::BITS as u8 - width) + } else { + poly << (u16::BITS as u8 - width) + }; + let mut table = [0u16; 256]; let mut i = 0; while i < table.len() { - table[i] = crc16(poly, reflect, i as u8); + table[i] = crc16(poly, reflect, i as u16); i += 1; } table } -pub(crate) const fn crc32_table(poly: u32, reflect: bool) -> [u32; 256] { + +pub(crate) const fn crc32_table(width: u8, poly: u32, reflect: bool) -> [u32; 256] { + let poly = if reflect { + let poly = poly.reverse_bits(); + poly >> (u32::BITS as u8 - width) + } else { + poly << (u32::BITS as u8 - width) + }; + let mut table = [0u32; 256]; let mut i = 0; while i < table.len() { - table[i] = crc32(poly, reflect, i as u8); + table[i] = crc32(poly, reflect, i as u32); i += 1; } table } -pub(crate) const fn crc64_table(poly: u64, reflect: bool) -> [u64; 256] { + +pub(crate) const fn crc64_table(width: u8, poly: u64, reflect: bool) -> [u64; 256] { + let poly = if reflect { + let poly = poly.reverse_bits(); + poly >> (u64::BITS as u8 - width) + } else { + poly << (u64::BITS as u8 - width) + }; + let mut table = [0u64; 256]; let mut i = 0; while i < table.len() { - table[i] = crc64(poly, reflect, i as u8); + table[i] = crc64(poly, reflect, i as u64); i += 1; } table diff --git a/src/util.rs b/src/util.rs index c378c23..b69f89e 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,63 +1,75 @@ -pub(crate) const fn crc8(poly: u8, reflect: bool, mut byte: u8) -> u8 { +pub(crate) const fn crc8(poly: u8, reflect: bool, mut value: u8) -> u8 { if reflect { - byte = byte.reverse_bits() - }; - let mut value = byte; - let mut i = 0; - while i < 8 { - value = (value << 1) ^ ((value >> 7) * poly); - i += 1; - } - if reflect { - value = value.reverse_bits() + let mut i = 0; + while i < 8 { + value = (value >> 1) ^ ((value & 1) * poly); + i += 1; + } + } else { + value <<= u8::BITS - 8; + + let mut i = 0; + while i < 8 { + value = (value << 1) ^ (((value >> (u8::BITS - 1)) & 1) * poly); + i += 1; + } } value } -pub(crate) const fn crc16(poly: u16, reflect: bool, mut byte: u8) -> u16 { +pub(crate) const fn crc16(poly: u16, reflect: bool, mut value: u16) -> u16 { if reflect { - byte = byte.reverse_bits() - }; - let mut value = (byte as u16) << 8; - let mut i = 0; - while i < 8 { - value = (value << 1) ^ ((value >> 15) * poly); - i += 1; - } - if reflect { - value = value.reverse_bits() + let mut i = 0; + while i < 8 { + value = (value >> 1) ^ ((value & 1) * poly); + i += 1; + } + } else { + value <<= u16::BITS - 8; + + let mut i = 0; + while i < 8 { + value = (value << 1) ^ (((value >> (u16::BITS - 1)) & 1) * poly); + i += 1; + } } value } -pub(crate) const fn crc32(poly: u32, reflect: bool, mut byte: u8) -> u32 { +pub(crate) const fn crc32(poly: u32, reflect: bool, mut value: u32) -> u32 { if reflect { - byte = byte.reverse_bits() - }; - let mut value = (byte as u32) << 24; - let mut i = 0; - while i < 8 { - value = (value << 1) ^ ((value >> 31) * poly); - i += 1; - } - if reflect { - value = value.reverse_bits() + let mut i = 0; + while i < 8 { + value = (value >> 1) ^ ((value & 1) * poly); + i += 1; + } + } else { + value <<= u32::BITS - 8; + + let mut i = 0; + while i < 8 { + value = (value << 1) ^ (((value >> (u32::BITS - 1)) & 1) * poly); + i += 1; + } } value } -pub(crate) const fn crc64(poly: u64, reflect: bool, mut byte: u8) -> u64 { +pub(crate) const fn crc64(poly: u64, reflect: bool, mut value: u64) -> u64 { if reflect { - byte = byte.reverse_bits() - }; - let mut value = (byte as u64) << 56; - let mut i = 0; - while i < 8 { - value = (value << 1) ^ ((value >> 63) * poly); - i += 1; - } - if reflect { - value = value.reverse_bits() + let mut i = 0; + while i < 8 { + value = (value >> 1) ^ ((value & 1) * poly); + i += 1; + } + } else { + value <<= u64::BITS - 8; + + let mut i = 0; + while i < 8 { + value = (value << 1) ^ (((value >> (u64::BITS - 1)) & 1) * poly); + i += 1; + } } value } diff --git a/tests/crc.rs b/tests/crc.rs index 7ee3238..dc89eb5 100644 --- a/tests/crc.rs +++ b/tests/crc.rs @@ -2,14 +2,53 @@ use crc::*; const INIT: &[u8] = b"123456789"; +const INIT_PART1: &[u8] = b"1234"; +const INIT_PART2: &[u8] = b"56789"; + #[test] fn crc_8() { - let algs = &[CRC_8_AUTOSAR, CRC_8_BLUETOOTH, CRC_8_SMBUS, CRC_8_DARC]; - for alg in algs { + let algs = &[ + CRC_3_GSM, + CRC_3_ROHC, + CRC_4_G_704, + CRC_4_INTERLAKEN, + CRC_5_EPC_C1G2, + CRC_5_G_704, + CRC_5_USB, + CRC_6_CDMA2000_A, + CRC_6_CDMA2000_B, + CRC_6_DARC, + CRC_6_G_704, + CRC_6_GSM, + CRC_7_MMC, + CRC_7_ROHC, + CRC_7_UMTS, + CRC_8_AUTOSAR, + CRC_8_BLUETOOTH, + CRC_8_CDMA2000, + CRC_8_DARC, + CRC_8_DVB_S2, + CRC_8_GSM_A, + CRC_8_GSM_B, + CRC_8_I_432_1, + CRC_8_I_CODE, + CRC_8_LTE, + CRC_8_MAXIM_DOW, + CRC_8_MIFARE_MAD, + CRC_8_NRSC_5, + CRC_8_OPENSAFETY, + CRC_8_ROHC, + CRC_8_SAE_J1850, + CRC_8_SMBUS, + CRC_8_TECH_3250, + CRC_8_WCDMA, + ]; + for alg in algs.iter() { let crc = Crc::::new(alg); assert_eq!(alg.check, crc.checksum(INIT)); let mut digest = crc.digest(); - digest.update(INIT); + digest.update(INIT_PART1); + digest.update(INIT_PART2); assert_eq!(alg.check, digest.finalize()); } } @@ -17,19 +56,57 @@ fn crc_8() { #[test] fn crc_16() { let algs = &[ - CRC_16_IBM_SDLC, - CRC_16_USB, + CRC_10_ATM, + CRC_10_CDMA2000, + CRC_10_GSM, + CRC_11_FLEXRAY, + CRC_11_UMTS, + CRC_12_CDMA2000, + CRC_12_DECT, + CRC_12_GSM, + CRC_12_UMTS, + CRC_13_BBC, + CRC_14_DARC, + CRC_14_GSM, + CRC_15_CAN, + CRC_15_MPT1327, CRC_16_ARC, CRC_16_CDMA2000, + CRC_16_CMS, + CRC_16_DDS_110, + CRC_16_DECT_R, + CRC_16_DECT_X, + CRC_16_DNP, + CRC_16_EN_13757, + CRC_16_GENIBUS, + CRC_16_GSM, CRC_16_IBM_3740, CRC_16_IBM_SDLC, + CRC_16_ISO_IEC_14443_3_A, CRC_16_KERMIT, + CRC_16_LJ1200, + CRC_16_MAXIM_DOW, + CRC_16_MCRF4XX, + CRC_16_MODBUS, + CRC_16_NRSC_5, + CRC_16_OPENSAFETY_A, + CRC_16_OPENSAFETY_B, + CRC_16_PROFIBUS, + CRC_16_RIELLO, + CRC_16_SPI_FUJITSU, + CRC_16_T10_DIF, + CRC_16_TELEDISK, + CRC_16_TMS37157, + CRC_16_UMTS, + CRC_16_USB, + CRC_16_XMODEM, ]; - for alg in algs { + for alg in algs.iter() { let crc = Crc::::new(alg); assert_eq!(alg.check, crc.checksum(INIT)); let mut digest = crc.digest(); - digest.update(INIT); + digest.update(INIT_PART1); + digest.update(INIT_PART2); assert_eq!(alg.check, digest.finalize()); } } @@ -37,29 +114,55 @@ fn crc_16() { #[test] fn crc_32() { let algs = &[ - CRC_32_ISCSI, + CRC_17_CAN_FD, + CRC_21_CAN_FD, + CRC_24_BLE, + CRC_24_FLEXRAY_A, + CRC_24_FLEXRAY_B, + CRC_24_INTERLAKEN, + CRC_24_LTE_A, + CRC_24_LTE_B, + CRC_24_OPENPGP, + CRC_24_OS_9, + CRC_30_CDMA, + CRC_31_PHILIPS, + CRC_32_AIXM, CRC_32_AUTOSAR, + CRC_32_BASE91_D, CRC_32_BZIP2, + CRC_32_CD_ROM_EDC, + CRC_32_CKSUM, CRC_32_ISCSI, CRC_32_ISO_HDLC, + CRC_32_JAMCRC, + CRC_32_MPEG_2, + CRC_32_XFER, ]; for alg in algs { let crc = Crc::::new(alg); assert_eq!(alg.check, crc.checksum(INIT)); let mut digest = crc.digest(); - digest.update(INIT); + digest.update(INIT_PART1); + digest.update(INIT_PART2); assert_eq!(alg.check, digest.finalize()); } } #[test] fn crc_64() { - let algs = &[CRC_64_ECMA_182, CRC_64_GO_ISO, CRC_64_WE, CRC_64_XZ]; + let algs = &[ + CRC_40_GSM, + CRC_64_ECMA_182, + CRC_64_GO_ISO, + CRC_64_WE, + CRC_64_XZ, + ]; for alg in algs { let crc = Crc::::new(alg); assert_eq!(alg.check, crc.checksum(INIT)); let mut digest = crc.digest(); - digest.update(INIT); + digest.update(INIT_PART1); + digest.update(INIT_PART2); assert_eq!(alg.check, digest.finalize()); } }