简体   繁体   中英

CRC32 - wrong checksum using TABLE algorithm and 04C11DB7 polynomial

I am following a painless guide to code correction algorithms. ( https://zlib.net/crc_v3.txt ) I've managed to write a TABLE algorithm, using extra loop for augmented part (I hope so). I am trying to write a most widely used CRC32 version (with 0x04C11DB7 polynomial), but I can not get the right CRC value.

I've achieved the correct table for CRC32 values with mentioned polynomial. My code for generating CRC32 (chapter 9 and 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;
}

The inverse function reverses bits of UINT32 value, and function inverse_byte inverses bits of UINT8 value.

But for the '123456789' string I get the wrong checksum. Could someone help me? Or give some advice?


Input string: '123456789'

Outputted CRC: CRC32: 0x22016B0A

Desired CRC: CRC32: 0xCBF43926

You made your array one word too short. It needs to be:

#define CRC32_BYTE_POSSIBLE_VALUES 256

Though that part probably still worked, because C.

You need to initialize the variable you are reversing into:

    uint32_t toret = 0;

These lines:

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

need to be:

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

and you need to delete these lines:

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

Then you get the right answer.

However reversing every single input byte is a waste of time. You can instead just reverse the calculation and the polynomial. See rcgldr's answer.

Example code using right shifting CRC (0xedb88320 is a reflected version of 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);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM