CRC循环冗余校验【实现篇】
在之前的文章当中,我简单介绍了一下有关于CRC的原理,这次呢,我们从开发的角度来看一下CRC的具体实现,因为CRC的种类实在是太多了,如果咱们一个一个实现,那就太费劲了,所以直接来看通用的实现方法。
参数
对于如何确定一个CRC校验,可以通过如下的参数来确定,
-
「多项式(Poly)」: 它是一个二进制数,表示用于生成CRC码的多项式,多项式长度取决于数据位的位数。 -
**初始值(Initial Value)**: 一个二进制数,表示在计算CRC码之前,应该在数据字节流的开头添加的字节,通常情况是全0或者全1。 -
**终止值(Final Xor Value)**:一个二进制数,表示在计算CRC码之后,应该对CRC码进行执行异或操作的字节,通常情况是全0或者全1。 -
**反转输入(Reflect Input)**:对输入的字节是否进行反转。 -
「反转输出(Reflect Input)」: 对输出的字节是否进行反转。
有关于第一个参数的含义,可以参考下我之前写过的文章,https://mp.weixin.qq.com/s/VwT__b-1cjaLcK0o7D-Vjw。
代码实现
再次相应读者们的选择,这次还是用C++来写,比较容易写出来一个比较通用的代码,当然我这代码就仅仅支持到crc64的任意多项式,再多其实也有,目前就先不支持了,实际上用的也不多。
#include <iostream>
#include <vector>
#include <iomanip>
template<typename T>
T reverse_bits(T value, int num_bits) {
T result = 0;
for (int i = 0; i < num_bits; i++) {
result = (result << 1) | (value & 1);
value >>= 1;
}
return result;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshift-count-overflow"
template<typename T>
std::vector<uint8_t> calculate_crc(const std::vector<uint8_t> &data,
T polynomial,
T initial_value,
bool reflect_input,
bool reflect_output,
T final_xor_value) {
std::vector<uint8_t> crc_bytes;
T crc = initial_value;
int num_bits = sizeof(T) * 8;
T top_bit = (T) 1 << (num_bits - 1);
for (uint8_t b : data) {
if (reflect_input) {
b = reverse_bits(b, 8);
crc ^= ((T) b << (num_bits - 8));
for (int i = 0; i < 8; i++) {
T msb = crc & top_bit;
crc <<= 1;
if (msb != 0) {
crc ^= polynomial;
}
}
} else {
crc ^= ((T) b << (num_bits - 8));
for (int i = 0; i < 8; i++) {
T msb = crc & top_bit;
crc <<= 1;
if (msb != 0) {
crc ^= polynomial;
}
}
}
}
if (reflect_output) {
for (int i = 0; i < num_bits / 8; i++) {
if (reflect_input) {
crc_bytes.push_back(static_cast<uint8_t>(reverse_bits((crc >> (8 * i)) & 0xff, 8)));
} else {
crc_bytes.push_back((crc >> (8 * i)) & 0xff);
}
}
} else {
for (int i = (num_bits - 8); i >= 0; i -= 8) {
crc_bytes.push_back((crc >> i) & 0xff);
}
}
for (unsigned char &crc_byte : crc_bytes) {
crc_byte ^= (final_xor_value & 0xff);
final_xor_value >>= 8;
}
return crc_bytes;
}
#pragma clang diagnostic pop
std::vector<uint8_t> crc8(const std::vector<uint8_t> &data,
uint8_t polynomial,
uint8_t initial_value,
bool reflect_input,
bool reflect_output,
uint8_t final_xor_value) {
return calculate_crc<uint8_t>(data, polynomial, initial_value, reflect_input, reflect_output, final_xor_value);
}
std::vector<uint8_t> crc16(const std::vector<uint8_t> &data,
uint16_t polynomial,
uint16_t initial_value,
bool reflect_input,
bool reflect_output,
uint16_t final_xor_value) {
return calculate_crc<uint16_t>(data, polynomial, initial_value, reflect_input, reflect_output, final_xor_value);
}
std::vector<uint8_t> crc32(const std::vector<uint8_t> &data,
uint32_t polynomial,
uint32_t initial_value,
bool reflect_input,
bool reflect_output,
uint32_t final_xor_value) {
return calculate_crc<uint32_t>(data, polynomial, initial_value, reflect_input, reflect_output, final_xor_value);
}
std::vector<uint8_t> crc64(const std::vector<uint8_t> &data,
uint64_t polynomial,
uint64_t initial_value,
bool reflect_input,
bool reflect_output,
uint64_t final_xor_value) {
return calculate_crc<uint64_t>(data, polynomial, initial_value, reflect_input, reflect_output, final_xor_value);
}
void print_vector_as_hex_string(const std::string &tag, const std::vector<uint8_t> &vec) {
std::cout << tag << ":";
for (const uint8_t &value : vec) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int) value << " ";
}
std::cout << std::endl;
}
int main() {
std::vector<uint8_t> data = {0x01, 0x02, 0x03, 0x04, 0x05};
print_vector_as_hex_string("CRC16:(CRC-16/ARC)", crc16(data, 0x8005, 0x0000, true, true, 0x0000));
print_vector_as_hex_string("CRC8:(CRC-8/DARC)", crc8(data, 0x39, 0x00, true, true, 0x00));
return 0;
}
我们具体使用一个例子来看一下这个代码,首先我们选取CRC8
这个来作为例子来看一下,至于为啥选他,看着他简单而已,那下面我们先根据上面我们选取的参数,来看一下CRC8
。
-
「多项式(Poly)」: 0x07 -
**初始值(Initial Value)**: 0x00 -
**终止值(Final Xor Value)**:0x00 -
**反转输入(Reflect Input)**:false -
「反转输出(Reflect Input)」: false
假设我们的输入是0x02
,为啥不选0x01
,因为1是一个常量,乘了和没乘是一样的,所以我们换一个数字。
对应输入的多项式为 ,对应生成多项式为,然后我们可以得到`r=8`,然后计算下,我们可以得到:
也就是最终得到CRC8
的校验值0x0E
,然后我们运行下我们写的程序看一下。
int main() {
std::vector<uint8_t> data = {0x02};
print_vector_as_hex_string("CRC8:(CRC-8)", crc8(data, 0x07, 0x00, false, false, 0x00));
return 0;
}
发现输出结果和我们写的程序是一样的,至于这个值,我这是怎么算出来的,读者可以自己尝试一下手动计算,我这里当然是选择科技与狠活了,如果大家对于这个我怎么算的感兴趣,我可以单独写一篇有关怎么计算多项式的文章,咳咳,不要吐槽我水文章哈。
好了,到这里有关于通用版本的CRC计算方案,到这里就给大家讲完了,然后呢,这里我找了下有关于CRC计算的常见多项式,和一些参数,以下资料来自互联网。
常见CRC方案
里面内容来自于参考资料2,我只是个搬运工。
name | alias | width | poly | init | refin | refout | xorout | check | residue |
---|---|---|---|---|---|---|---|---|---|
CRC-3/GSM | - | 3 | 0x3 | 0x0 | false | false | 0x7 | 0x4 | 0x2 |
CRC-3/ROHC | - | 3 | 0x3 | 0x7 | true | true | 0x0 | 0x6 | 0x0 |
CRC-4/G-704 | CRC-4/ITU | 4 | 0x3 | 0x0 | true | true | 0x0 | 0x7 | 0x0 |
CRC-4/INTERLAKEN | - | 4 | 0x3 | 0xf | false | false | 0xf | 0xb | 0x2 |
CRC-5/EPC-C1G2 | CRC-5/EPC | 5 | 0x09 | 0x09 | false | false | 0x00 | 0x00 | 0x00 |
CRC-5/G-704 | CRC-5/ITU | 5 | 0x15 | 0x00 | true | true | 0x00 | 0x07 | 0x00 |
CRC-6/CDMA2000-A | - | 6 | 0x27 | 0x3f | false | false | 0x00 | 0x0d | 0x00 |
CRC-6/CDMA2000-B | - | 6 | 0x07 | 0x3f | false | false | 0x00 | 0x3b | 0x00 |
CRC-6/DARC | - | 6 | 0x19 | 0x00 | true | true | 0x00 | 0x26 | 0x00 |
CRC-6/G-704 | CRC-6/ITU | 6 | 0x03 | 0x00 | true | true | 0x00 | 0x06 | 0x00 |
CRC-6/GSM | - | 6 | 0x2f | 0x00 | false | false | 0x3f | 0x13 | 0x3a |
CRC-7/MMC | CRC-7 | 7 | 0x09 | 0x00 | false | false | 0x00 | 0x75 | 0x00 |
CRC-7/ROHC | - | 7 | 0x4f | 0x7f | true | true | 0x00 | 0x53 | 0x00 |
CRC-7/UMTS | - | 7 | 0x45 | 0x00 | false | false | 0x00 | 0x61 | 0x00 |
CRC-8/AUTOSAR | - | 8 | 0x2f | 0xff | false | false | 0xff | 0xdf | 0x42 |
CRC-8/CDMA2000 | - | 8 | 0x9b | 0xff | false | false | 0x00 | 0xda | 0x00 |
CRC-8/DARC | - | 8 | 0x39 | 0x00 | true | true | 0x00 | 0x15 | 0x00 |
CRC-8/DVB-S2 | - | 8 | 0xd5 | 0x00 | false | false | 0x00 | 0xbc | 0x00 |
CRC-8/GSM-A | - | 8 | 0x1d | 0x00 | false | false | 0x00 | 0x37 | 0x00 |
CRC-8/GSM-B | - | 8 | 0x49 | 0x00 | false | false | 0xff | 0x94 | 0x53 |
CRC-8/HITAG | - | 8 | 0x1d | 0xff | false | false | 0x00 | 0xb4 | 0x00 |
CRC-8/I-432-1 | CRC-8/ITU | 8 | 0x07 | 0x00 | false | false | 0x55 | 0xa1 | 0xac |
CRC-8/I-CODE | - | 8 | 0x1d | 0xfd | false | false | 0x00 | 0x7e | 0x00 |
CRC-8/LTE | - | 8 | 0x9b | 0x00 | false | false | 0x00 | 0xea | 0x00 |
CRC-8/MAXIM-DOW | CRC-8/MAXIM, DOW-CRC | 8 | 0x31 | 0x00 | true | true | 0x00 | 0xa1 | 0x00 |
CRC-8/MIFARE-MAD | - | 8 | 0x1d | 0xc7 | false | false | 0x00 | 0x99 | 0x00 |
CRC-8/NRSC-5 | - | 8 | 0x31 | 0xff | false | false | 0x00 | 0xf7 | 0x00 |
CRC-8/OPENSAFETY | - | 8 | 0x2f | 0x00 | false | false | 0x00 | 0x3e | 0x00 |
CRC-8/ROHC | - | 8 | 0x07 | 0xff | true | true | 0x00 | 0xd0 | 0x00 |
CRC-8/SAE-J1850 | - | 8 | 0x1d | 0xff | false | false | 0xff | 0x4b | 0xc4 |
CRC-8/SMBUS | CRC-8 | 8 | 0x07 | 0x00 | false | false | 0x00 | 0xf4 | 0x00 |
CRC-8/TECH-3250 | CRC-8/AES, CRC-8/EBU | 8 | 0x1d | 0xff | true | true | 0x00 | 0x97 | 0x00 |
CRC-10/ATM | CRC-10, CRC-10/I-610 | 10 | 0x233 | 0x000 | false | false | 0x000 | 0x199 | 0x000 |
CRC-10/CDMA2000 | - | 10 | 0x3d9 | 0x3ff | false | false | 0x000 | 0x233 | 0x000 |
CRC-10/GSM | - | 10 | 0x175 | 0x000 | false | false | 0x3ff | 0x12a | 0x0c6 |
CRC-11/FLEXRAY | CRC-11 | 11 | 0x385 | 0x01a | false | false | 0x000 | 0x5a3 | 0x000 |
CRC-11/UMTS | - | 11 | 0x307 | 0x000 | false | false | 0x000 | 0x061 | 0x000 |
CRC-12/CDMA2000 | - | 12 | 0xf13 | 0xfff | false | false | 0x000 | 0xd4d | 0x000 |
CRC-12/DECT | X-CRC-12 | 12 | 0x80f | 0x000 | false | false | 0x000 | 0xf5b | 0x000 |
CRC-12/GSM | - | 12 | 0xd31 | 0x000 | false | false | 0xfff | 0xb34 | 0x178 |
CRC-12/UMTS | CRC-12/3GPP | 12 | 0x80f | 0x000 | false | true | 0x000 | 0xdaf | 0x000 |
CRC-13/BBC | - | 13 | 0x1cf5 | 0x0000 | false | false | 0x0000 | 0x04fa | 0x0000 |
CRC-14/DARC | - | 14 | 0x0805 | 0x0000 | true | true | 0x0000 | 0x082d | 0x0000 |
CRC-14/GSM | - | 14 | 0x202d | 0x0000 | false | false | 0x3fff | 0x30ae | 0x031e |
CRC-15/CAN | CRC-15 | 15 | 0x4599 | 0x0000 | false | false | 0x0000 | 0x059e | 0x0000 |
CRC-15/MPT1327 | - | 15 | 0x6815 | 0x0000 | false | false | 0x0001 | 0x2566 | 0x6815 |
CRC-16/ARC | ARC, CRC-16, CRC-16/LHA, CRC-IBM | 16 | 0x8005 | 0x0000 | true | true | 0x0000 | 0xbb3d | 0x0000 |
CRC-16/CDMA2000 | - | 16 | 0xc867 | 0xffff | false | false | 0x0000 | 0x4c06 | 0x0000 |
CRC-16/DDS-110 | - | 16 | 0x8005 | 0x800d | false | false | 0x0000 | 0x9ecf | 0x0000 |
CRC-16/DECT-R | R-CRC-16 | 16 | 0x0589 | 0x0000 | false | false | 0x0001 | 0x007e | 0x0589 |
CRC-16/DECT-X | X-CRC-16 | 16 | 0x0589 | 0x0000 | false | false | 0x0000 | 0x007f | 0x0000 |
CRC-16/DNP | - | 16 | 0x3d65 | 0x0000 | true | true | 0xffff | 0xea82 | 0x66c5 |
CRC-16/EN-13757 | - | 16 | 0x3d65 | 0x0000 | false | false | 0xffff | 0xc2b7 | 0xa366 |
CRC-16/GENIBUS | CRC-16/DARC, CRC-16/EPC, CRC-16/EPC-C1G2, CRC-16/I-CODE | 16 | 0x1021 | 0xffff | false | false | 0xffff | 0xd64e | 0x1d0f |
CRC-16/GSM | - | 16 | 0x1021 | 0x0000 | false | false | 0xffff | 0xce3c | 0x1d0f |
CRC-16/IBM-3740 | CRC-16/AUTOSAR, CRC-16/CCITT-FALSE | 16 | 0x1021 | 0xffff | false | false | 0x0000 | 0x29b1 | 0x0000 |
CRC-16/IBM-SDLC | CRC-16/ISO-HDLC, CRC-16/ISO-IEC-14443-3-B, CRC-16/X-25, CRC-B, X-25 | 16 | 0x1021 | 0xffff | true | true | 0xffff | 0x906e | 0xf0b8 |
CRC-16/ISO-IEC-14443-3-A | CRC-A | 16 | 0x1021 | 0xc6c6 | true | true | 0x0000 | 0xbf05 | 0x0000 |
CRC-16/KERMIT | CRC-16/BLUETOOTH, CRC-16/CCITT, CRC-16/CCITT-TRUE, CRC-16/V-41-LSB, CRC-CCITT, KERMIT | 16 | 0x1021 | 0x0000 | true | true | 0x0000 | 0x2189 | 0x0000 |
CRC-16/M17 | - | 16 | 0x5935 | 0xffff | false | false | 0x0000 | 0x772b | 0x0000 |
CRC-16/MAXIM-DOW | CRC-16/MAXIM | 16 | 0x8005 | 0x0000 | true | true | 0xffff | 0x44c2 | 0xb001 |
CRC-16/MCRF4XX | - | 16 | 0x1021 | 0xffff | true | true | 0x0000 | 0x6f91 | 0x0000 |
CRC-16/MODBUS | MODBUS | 16 | 0x8005 | 0xffff | true | true | 0x0000 | 0x4b37 | 0x0000 |
CRC-16/NRSC-5 | - | 16 | 0x080b | 0xffff | true | true | 0x0000 | 0xa066 | 0x0000 |
CRC-16/OPENSAFETY-A | - | 16 | 0x5935 | 0x0000 | false | false | 0x0000 | 0x5d38 | 0x0000 |
CRC-16/OPENSAFETY-B | - | 16 | 0x755b | 0x0000 | false | false | 0x0000 | 0x20fe | 0x0000 |
CRC-16/PROFIBUS | CRC-16/IEC-61158-2 | 16 | 0x1dcf | 0xffff | false | false | 0xffff | 0xa819 | 0xe394 |
CRC-16/SPI-FUJITSU | CRC-16/AUG-CCITT | 16 | 0x1021 | 0x1d0f | false | false | 0x0000 | 0xe5cc | 0x0000 |
CRC-16/T10-DIF | - | 16 | 0x8bb7 | 0x0000 | false | false | 0x0000 | 0xd0db | 0x0000 |
CRC-16/TELEDISK | - | 16 | 0xa097 | 0x0000 | false | false | 0x0000 | 0x0fb3 | 0x0000 |
CRC-16/TMS37157 | - | 16 | 0x1021 | 0x89ec | true | true | 0x0000 | 0x26b1 | 0x0000 |
CRC-16/UMTS | CRC-16/BUYPASS, CRC-16/VERIFONE | 16 | 0x8005 | 0x0000 | false | false | 0x0000 | 0xfee8 | 0x0000 |
CRC-16/XMODEM | CRC-16/ACORN, CRC-16/LTE, CRC-16/V-41-MSB, XMODEM, ZMODEM | 16 | 0x1021 | 0x0000 | false | false | 0x0000 | 0x31c3 | 0x0000 |
CRC-17/CAN-FD | - | 17 | 0x1685b | 0x00000 | false | false | 0x00000 | 0x04f03 | 0x00000 |
CRC-21/CAN-FD | - | 21 | 0x102899 | 0x000000 | false | false | 0x000000 | 0x0ed841 | 0x000000 |
CRC-24/BLE | - | 24 | 0x00065b | 0x555555 | true | true | 0x000000 | 0xc25a56 | 0x000000 |
CRC-24/FLEXRAY-A | - | 24 | 0x5d6dcb | 0xfedcba | false | false | 0x000000 | 0x7979bd | 0x000000 |
CRC-24/FLEXRAY-B | - | 24 | 0x5d6dcb | 0xabcdef | false | false | 0x000000 | 0x1f23b8 | 0x000000 |
CRC-24/INTERLAKEN | - | 24 | 0x328b63 | 0xffffff | false | false | 0xffffff | 0xb4f3e6 | 0x144e63 |
CRC-24/LTE-A | - | 24 | 0x864cfb | 0x000000 | false | false | 0x000000 | 0xcde703 | 0x000000 |
CRC-24/LTE-B | - | 24 | 0x800063 | 0x000000 | false | false | 0x000000 | 0x23ef52 | 0x000000 |
CRC-24/OPENPGP | CRC-24 | 24 | 0x864cfb | 0xb704ce | false | false | 0x000000 | 0x21cf02 | 0x000000 |
CRC-24/OS-9 | - | 24 | 0x800063 | 0xffffff | false | false | 0xffffff | 0x200fa5 | 0x800fe3 |
CRC-30/CDMA | - | 30 | 0x2030b9c7 | 0x3fffffff | false | false | 0x3fffffff | 0x04c34abf | 0x34efa55a |
CRC-31/PHILIPS | - | 31 | 0x04c11db7 | 0x7fffffff | false | false | 0x7fffffff | 0x0ce9e46c | 0x4eaf26f1 |
CRC-32/AIXM | CRC-32Q | 32 | 0x814141ab | 0x00000000 | false | false | 0x00000000 | 0x3010bf7f | 0x00000000 |
CRC-32/AUTOSAR | - | 32 | 0xf4acfb13 | 0xffffffff | true | true | 0xffffffff | 0x1697d06a | 0x904cddbf |
CRC-32/BASE91-D | CRC-32D | 32 | 0xa833982b | 0xffffffff | true | true | 0xffffffff | 0x87315576 | 0x45270551 |
CRC-32/BZIP2 | CRC-32/AAL5, CRC-32/DECT-B, B-CRC-32 | 32 | 0x04c11db7 | 0xffffffff | false | false | 0xffffffff | 0xfc891918 | 0xc704dd7b |
CRC-32/CD-ROM-EDC | - | 32 | 0x8001801b | 0x00000000 | true | true | 0x00000000 | 0x6ec2edc4 | 0x00000000 |
CRC-32/CKSUM | CKSUM, CRC-32/POSIX | 32 | 0x04c11db7 | 0x00000000 | false | false | 0xffffffff | 0x765e7680 | 0xc704dd7b |
CRC-32/ISCSI | CRC-32/BASE91-C, CRC-32/CASTAGNOLI, CRC-32/INTERLAKEN, CRC-32C | 32 | 0x1edc6f41 | 0xffffffff | true | true | 0xffffffff | 0xe3069283 | 0xb798b438 |
CRC-32/ISO-HDLC | CRC-32, CRC-32/ADCCP, CRC-32/V-42, CRC-32/XZ, PKZIP | 32 | 0x04c11db7 | 0xffffffff | true | true | 0xffffffff | 0xcbf43926 | 0xdebb20e3 |
CRC-32/JAMCRC | JAMCRC | 32 | 0x04c11db7 | 0xffffffff | true | true | 0x00000000 | 0x340bc6d9 | 0x00000000 |
CRC-32/MEF | - | 32 | 0x741b8cd7 | 0xffffffff | true | true | 0x00000000 | 0xd2c22f51 | 0x00000000 |
CRC-32/MPEG-2 | - | 32 | 0x04c11db7 | 0xffffffff | false | false | 0x00000000 | 0x0376e6e7 | 0x00000000 |
CRC-32/XFER | XFER | 32 | 0x000000af | 0x00000000 | false | false | 0x00000000 | 0xbd0be338 | 0x00000000 |
CRC-40/GSM | - | 40 | 0x0004820009 | 0x0000000000 | false | false | 0xffffffffff | 0xd4164fc646 | 0xc4ff8071ff |
CRC-64/ECMA-182 | CRC-64 | 64 | 0x42f0e1eba9ea3693 | 0x0000000000000000 | false | false | 0x0000000000000000 | 0x6c40df5f0b497347 | 0x0000000000000000 |
CRC-64/GO-ISO | - | 64 | 0x000000000000001b | 0xffffffffffffffff | true | true | 0xffffffffffffffff | 0xb90956c775a41001 | 0x5300000000000000 |
CRC-64/MS | - | 64 | 0x259c84cba6426349 | 0xffffffffffffffff | true | true | 0x0000000000000000 | 0x75d4b74f024eceea | 0x0000000000000000 |
CRC-64/REDIS | - | 64 | 0xad93d23594c935a9 | 0x0000000000000000 | true | true | 0x0000000000000000 | 0xe9c6d914c4b8d9ca | 0x0000000000000000 |
CRC-64/WE | - | 64 | 0x42f0e1eba9ea3693 | 0xffffffffffffffff | false | false | 0xffffffffffffffff | 0x62ec59e3f1a4f00a | 0xfcacbebd5931a992 |
CRC-64/XZ | CRC-64/GO-ECMA | 64 | 0x42f0e1eba9ea3693 | 0xffffffffffffffff | true | true | 0xffffffffffffffff | 0x995dc9bbdf1939fa | 0x49958c9abd7d353f |
CRC-82/DARC | - | 82 | 0x0308c0111011401440411 | 0x000000000000000000000 | true | true | 0x000000000000000000000 | 0x09ea83f625023801fd612 | 0x000000000000000000000 |
参考资料
-
https://zlib.net/crc_v3.txt -
https://reveng.sourceforge.io/crc-catalogue/ -
https://crccalc.com/
原文始发于微信公众号(Coder小Q):CRC循环冗余校验【实现篇】
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论