What is the inverse of crc32_combine()'s matrix trick?

青春壹個敷衍的年華 提交于 2019-12-03 16:33:56

We will begin by looking at a simple bit-wise implementation of the standard CRC-32 (to be a self-contained definition of the CRC, this routine returns the initial CRC, i.e. the CRC of the empty string, when data is NULL):

#include <stddef.h>
#include <stdint.h>

#define POLY 0xedb88320

uint32_t crc32(uint32_t crc, void const *data, size_t len) {
    if (data == NULL)
        return 0;
    crc = ~crc;
    while (len--) {
        crc ^= *(unsigned char const *)data++;
        for (int k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
    }
    crc = ~crc;
    return crc;
}

We can simplify that for applying n zeros to a CRC:

uint32_t crc32_zeros(uint32_t crc, size_t n) {
    crc = ~crc;
    while (n--)
        for (int k = 0; k < 8; k++)
            crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;
    crc = ~crc;
    return crc;
}

Now let's look carefully at the application of single zero bit to the CRC:

crc = crc & 1 ? (crc >> 1) ^ POLY : crc >> 1;

There are two paths that could have been taken when applying the bit. In the last operation, either the polynomial was exclusive-ored with the CRC, or it wasn't. If we want to reverse this, we would like to know which way it went.

We can tell by looking at the high bit of the result. We can see that if the polynomial was not exclusive-ored, then the high bit must be 0. But what if it was exclusive-ored? In that case, the high bit of the result is the high bit of POLY. We can see that that high bit is 1. So we can tell just by looking at the high bit of the result. In fact, this must be the case for any valid CRC polynomial, since all have the coefficient 1 for the x0 term. (That term is in the high bit for this reflected polynomial.)

By inspection we can easily reverse that operation, where here crc going in is the final CRC after applying a 0 bit, and crc coming out is what that CRC was before applying the 0 bit:

crc = crc & 0x80000000 ? ((crc ^ POLY) << 1) + 1 : crc << 1;

That will take a final CRC and reverse the action of computing the CRC over a single 0 bit. Note that we have to insert the low 1 bit that would have caused the exclusive-or, for that case.

We can factor out POLY to get:

crc = crc & 0x80000000 ? (crc << 1) ^ ((POLY << 1) + 1) : crc << 1;

That is exactly the same as the operation to append a 0 bit to a non-reflected CRC with the polynomial (POLY << 1) + 1, which is just POLY rotated left one bit.

We can then write a function to remove n zero bytes from a standard CRC-32:

#define UNPOLY ((POLY << 1) + 1)

uint32_t crc32_remove_zeros(uint32_t crc, size_t n) {
    crc = ~crc;
    while (n--)
        for (int k = 0; k < 8; k++)
            crc = crc & 0x80000000 ? (crc << 1) ^ UNPOLY : crc << 1;
    crc = ~crc;
    return crc;
}

Now we can use the same approach as used in zlib, but with a non-reflected CRC, to write a function to remove n zeros from a CRC-32 in O(log(n)) time. We do not need to invert any matrices, since we have already inverted the original operation.

The remainder is left as an exercise for the reader.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!