【问题标题】:CRC32 - wrong checksum using TABLE algorithm and 04C11DB7 polynomialCRC32 - 使用 TABLE 算法和 04C11DB7 多项式的错误校验和
【发布时间】:2021-12-25 10:58:17
【问题描述】:

我正在遵循代码校正算法的无痛指南。 (https://zlib.net/crc_v3.txt) 我已经设法编写了一个 TABLE 算法,对增强部分使用额外的循环(我希望如此)。我正在尝试编写一个使用最广泛的 CRC32 版本(带有 0x04C11DB7 多项式),但我无法获得正确的 CRC 值。

我已经用提到的多项式获得了正确的 CRC32 值表。 我生成 CRC32 的代码(第 9 章和第 10 章):

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>

#define CRC32_BYTE_POSSIBLE_VALUES 255
#define CRC32_LAST_BIT_MASK 0x80000000

#define CRC32_POLYNOMIAL 0x04C11DB7

uint32_t __crc32_table[CRC32_BYTE_POSSIBLE_VALUES] = { 0 };

void __crc32_fill_crc_table() {
    uint32_t reg;
    uint8_t byte = 0;

    for (;;) {
        reg = (byte << 24);

        for (uint8_t byte_size = 0; byte_size < 8; byte_size++) {
            if (reg & CRC32_LAST_BIT_MASK) {
                reg <<= 1;
                reg ^= CRC32_POLYNOMIAL;
            } else {
                reg <<= 1;
            }
        }

        __crc32_table[byte] = reg;
        if (byte == 255)
            break;
        else
            byte++;
    }

}

void __crc32_print_table(uint32_t *arr) {
    printf(" 0x%08X ", arr[0]);

    for (uint32_t i = 1; i < 256; i++) {
        if (!(i % 8))
            printf("\n");

        printf(" 0x%08X ", arr[i]);

    }

    printf("\n");
}

uint8_t inverse_byte(uint8_t byte) {
    uint8_t reflected_byte = 0;

    for (uint8_t i = 0; i < 8; i++) {
        if (byte & (1 << i))
            reflected_byte |= (1 << (7 - i));
    }

    return reflected_byte;
}

uint32_t inverse(uint32_t src) {

    uint32_t toret;

    for (uint8_t i = 0; i < 32; i++) {
        if (src & (1 << i))
            toret |= (1 << (31 - i));
    }

    return toret;
}


uint32_t __crc32_table_approach( unsigned char *data, size_t size) {
    uint32_t reg = -1;
    uint8_t top_byte;

    for (size_t i = 0; i < size; i++) {
        top_byte = (uint8_t)(reg >> 24);
        reg = (reg << 8) | inverse_byte(data[i]);
        reg ^= __crc32_table[top_byte];
    }

    for (size_t i = 0; i < 4; i++) {
        top_byte = (uint8_t) (reg >> 24);
        reg = (reg << 8) ;
        reg ^= __crc32_table[top_byte];
    }

    return inverse(reg) ^ -1;
}

uint32_t calc_crc32(unsigned char *data, size_t size) {
    if (!__crc32_table[1])
        __crc32_fill_crc_table();

    __crc32_print_table(__crc32_table);

    return __crc32_table_approach(data, size);
}


int main( int argc, char** argv )
{
    
    unsigned char* test = "123456789";
    size_t test_len = strlen(test);

    uint32_t crc = calc_crc32(test, test_len);
    printf("CRC32: 0x%08X", crc);
    return 0;
}

inverse函数反转UINT32值的位,函数inverse_byte反转UINT8值的位。

但是对于“123456789”字符串,我得到了错误的校验和。 有人可以帮助我吗?或者给点建议?


输入字符串:'123456789'

输出的 CRC:CRC32:0x22016B0A

所需的 CRC:CRC32:0xCBF43926

【问题讨论】:

  • 编辑问题以提供minimal reproducible example,包括完整的可运行程序、重现问题的示例输入、观察到的输出和所需的输出。
  • 添加了带有示例的存储库。
  • 看起来指南说用0初始化寄存器,而你用-1初始化它。你是故意的吗?
  • 据我所知,最广为人知的 CRC32 使用寄存器的初始值 0xFFFFFFFF,并且还通过最终的 XOR 反映输入/输出数据。本文档稍后会注明这些特征(REFIN/REFOUT 等)。
  • 编辑问题以完全在问题内提供minimal reproducible example。外部链接可用于补充信息,但重现问题所需的所有信息(包括代码)都应包含在帖子本身内。 Stack Overflow 不是个人调试服务。它的存在是为了提供一个持久的问题和答案存储库,供其他人在未来使用。外部链接上的文件会随着时间的推移而改变或消失,因此不适合记录问题。

标签: c crc crc32


【解决方案1】:

您的数组太短了一个字,因此覆盖了分配的内存。它必须是:

#define CRC32_BYTE_POSSIBLE_VALUES 256

虽然那部分可能仍然有效,因为 C.

您需要初始化要反转的变量:

    uint32_t toret = 0;

这些行:

    top_byte = (uint8_t)(reg >> 24);
    reg = (reg << 8) | inverse_byte(data[i]);

需要:

    top_byte = (uint8_t)(reg >> 24) ^ inverse_byte(data[i]);
    reg <<= 8;

你需要删除这些行: 罢工>

    for (size_t i = 0; i < 4; i++) {
        top_byte = (uint8_t) (reg >> 24);
        reg = (reg << 8) ;
        reg ^= __crc32_table[top_byte];
    }

那么你就会得到正确的答案。

如果您想在第 9 章中描述的增强消息上实现表格方法(在代码的末尾需要另外四次 CRC 迭代),那么您需要首先阅读文档中的这些重要说明:

注意:此算法的初始寄存器值必须是 前一个算法的寄存器初始值 表四次。注意:该表是这样的,如果前一个 算法使用0,新算法也会。

要获得与0xffffffff 的初始值相同的效果(特别是不是零)和非增强消息版本,这是标准 CRC 的定义方式,那么您需要找到一个初始值,以便使用 CRC 对其应用 32 个零位给你0xffffffff。该值为0x46af6449,通过反转CRC逐位算法获得:

    uint32_t x = -1;
    for (unsigned i = 0; i < 32; i++)
        x = x & 1 ? ((x ^ 0x4c11db7) >> 1) | 0x80000000 : x >> 1;

如果您修复数组大小和toret 初始化错误,那么您的代码将起作用,并且只需替换:

    uint32_t reg = -1;

与:

    uint32_t reg = 0x46af6449;

无论是否增强,在你做的时候反转每个输入字节都是浪费时间。您可以而且应该只反转计算和多项式。请参阅 rcgldr 的回答。

【讨论】:

  • 您需要打开编译器警告。编译器会发现您的代码存在一些问题,例如缺少初始化。
  • 谢谢 :) 我只是想看看事情是如何工作的,并没有真正做一些优化,所以我只是坚持使用多项式和倒数。所以我猜第 9 章在无痛指南中是错误的?因为没有异或,只有或:r=0;而 (len--) { 字节 t = (r >> 24) & 0xFF; r = (r
  • ... 以及那些零字节的东西: for (i=0; i> 24 ) & 0xFF];
  • 不,无痛指南是正确的。我是该文件的仔细审阅者之一。 (25 年前。)
  • Ow :) 我将非常感谢,如果您能指出错误在哪里,我的意思是,如果伪代码是正确的,那么我的代码中的错误在哪里?而 (len--) { 字节 t = (r >> 24) & 0xFF; r = (r > 24) & 0xFF];从那时起,我将尝试实现最佳形式(您建议的形式)。
【解决方案2】:

使用右移 CRC 的示例代码(0xedb88320 是 0x04C11DB7 的反映版本):

#include <iostream>
#include <iomanip>

typedef unsigned char uint8_t;
typedef unsigned int uint32_t;

uint32_t crctbl[256];

void gentbl(void)
{
uint32_t crc;
uint32_t c;
uint32_t i;
    for(c = 0; c < 0x100; c++){
        crc = c;
        for(i = 0; i < 8; i++){
            crc = (crc & 1) ? (crc >> 1) ^ 0xedb88320 : (crc >> 1);
        }
        crctbl[c] = crc;
    }
}

uint32_t crc32(uint8_t * bfr, size_t size)
{
uint32_t crc = 0xfffffffful;
    while(size--)
        crc = (crc >> 8) ^ crctbl[(crc & 0xff) ^ *bfr++];
    return(crc ^ 0xfffffffful);
}

int main(int argc, char**argv)
{
uint32_t crc;
uint8_t msg[10] = "123456789";

    gentbl();
    crc = crc32(msg, 9);
    std::cout << "crc " << std::hex << std::setw(8) << std::setfill('0') << crc << std::endl;
    return(0);
}

【讨论】:

  • 感谢您的回答:)。我更感兴趣的是从更简单的层面而不是从完全优化的角度来看算法是如何工作的。
  • @foralost - 它使用与问题代码相同的表格方法。反射 CRC 通常在软件中实现为右移 CRC。我没有检查所有问题的代码,但一个问题是数据应该在循环 CRC 寄存器之前异或到 CRC 寄存器。另一个是 CRC 表需要有 256 个条目的大小,而不是 255 个。如果以后有时间,我会尝试包含问题代码的固定版本。
  • 我看到的代码使用了优化,我们不需要为寄存器提供额外的零字节(仅第 10 章)。我的代码没有使用它,我通过向寄存器提供 4 个零输入字节(第 9 章,仅移动 8 位)来模拟它。此外,SIMPLE 算法不适用于 CRC32,但出于某种原因,它适用于 CRC8。是的,我忘了更改 255 -> 256,并且在逆函数中。我没有设置初始值。感谢您的努力!
  • @foralost - 此答案的代码不会为寄存器提供额外的零字节。它只循环“大小”数据字节,在本例中为 9 个字节。额外的零字节是隐含的,但代码只需要处理“大小”字节,而无需进一步循环 CRC 寄存器。
猜你喜欢
  • 2011-02-04
  • 1970-01-01
  • 1970-01-01
  • 2015-04-29
  • 2015-04-14
  • 1970-01-01
  • 1970-01-01
  • 2015-01-30
  • 2017-12-01
相关资源
最近更新 更多