【问题标题】:How to calculate CRC32 over blocks that are splitted and buffered of a large data?如何在大数据的拆分和缓冲块上计算 CRC32?
【发布时间】:2018-07-09 08:09:12
【问题描述】:

假设我有一个 1024kb 的数据,它被 1kB 缓冲并从发送器到接收器传输了 1024 次。

最后一个缓冲区包含计算出的 CRC32 值作为最后 4 个字节。

但是,由于 RAM 的限制,接收器必须逐个缓冲区计算 CRC32 缓冲区。

我想知道如何应用 CRC32 计算的线性分布式加法来匹配总 CRC32 值。

我查看了 CRC 计算及其分配偏好。计算及其线性度实现起来不是很清楚。

那么,是否有一个数学表达式可以将计算出的 CRC32 与缓冲区相加,以匹配通过总计算得出的 CRC32 结果?

如:

int CRC32Total = 0;
int CRC32[1024];
for(int i = 0; i < 1024; i++){
    CRC32Total = CRC32Total + CRC32[i];
}

亲切的问候

【问题讨论】:

  • 你为什么不继续计算CRC32而不为每个块重置它?这是实现您想要的最简单的方法。要按照您建议的方式进行操作,您将需要为零块预先计算大量 CRC32,以便您可以在最后将它们组合起来......这确实是没有任何收获的大量努力。
  • 先生,我是接收方。我只能有一个缓冲区大小(1kB)的数据。然后我将缓冲区刷新到内部存储器中,等待下一个 TFTP 包到达以恢复缓冲区。因此,我必须在每次恢复缓冲区时收集 CRC32 值(因为由于 RAM,我无法收集缓冲区的所有数据)并将单个 CRC32 值相互组合(添加)直到最后 4 个字节的总 CRC32数据到达,进行匹配。 “在不重置每个块的情况下计算 CRC32”到底是什么意思?
  • 要计算 CRC32,您需要使用预定义的值(通常为 0xFFFFFFFF)加载“种子”,然后将块中的每个字节提供给 CRC32 算法。当块结束时,您将获得该块的 CRC32。如果您对每个块重复此过程,则您正在“重置”CRC32 引擎。现在,如果您使用从一个块计算的 CRC32 作为下一个块的“种子”,那么您将“继续”计算 CRC32。如果您对所有块都遵循此过程,最后您将获得总 CRC32!
  • 先生,我希望您使用数学运算术语(add、mul、shift 等)或固件描述术语(缓冲区、包等)而不是“feed、load 和 seed” “?你能解释更多吗? (例如,使用独占或使用 0xFFFFFFFF 操作第一个缓冲区,使用第一个缓冲区操作第二个缓冲区等等..)感谢您的快速响应和深入了解的答案,但我不能说我和您一样先进,先生,了解您在全栈术语方面的经验。
  • 我说得对吗:您正在刷新未经验证的数据?请告诉我们这不是代码-Flash,而是一些备份-Flash..

标签: embedded crc crc32 distributive


【解决方案1】:

您没有提供任何线索来说明您“查看 CRC 计算”是什么实现,甚至是什么语言。但是,我看到的每个实现都旨在按照您的需要逐个计算 CRC。

对于 zlib 中提供的crc32() 例程,它是这样使用的(在 C 中):

crc = crc32(0, NULL, 0);               // initialize CRC value
crc = crc32(crc, firstchunk, 1024);    // update CRC value with first chunk
crc = crc32(crc, secondchunk, 1024);   // update CRC with second chunk
...
crc = crc32(crc, lastchunk, 1024);     // complete CRC with the last chunk

那么crc 是所有块串联的CRC。您不需要一个函数来组合各个块的 CRC。

如果出于其他原因您确实想要一个函数来组合 CRC,例如如果您需要在多个 CPU 上拆分 CRC 计算,那么 zlib 会为此提供 crc32_combine() 函数。

【讨论】:

    【解决方案2】:

    开始传输时,使用 OnFirstBlock 方法将 CrcChecksum 重置为其初始值。对于收到的每个块,调用 OnBlockReceived 来更新校验和。请注意,必须以正确的顺序处理块。处理完最后一个块后,最终的 CRC 在 CrcChecksum 变量中。

    // In crc32.c
    uint32_t UpdateCrc(uint32_t crc, const void *data, size_t length)
        const uint8_t *current = data;
        while (length--)
            crc = (crc >> 8) ^ Crc32Lookup[(crc & 0xFF) ^ *current++];
    }
    
    // In your block processing application
    static uint32_t CrcChecksum;
    void OnFirstBlock(void) {
        CrcChecksum = 0;
    }
    
    void OnBlockReceived(const void *data, size_t length) {
        CrcChecksum = UpdateCrc(CrcChecksum, data, length);
    }
    

    【讨论】:

    • 先生,非常感谢您的导师明智的解释。我会在几个小时内申请后立即通知您。
    • 将当前的 CRC 传递给函数可能比保留全局数据更好(总是一个坏主意)。此方法一次只允许对一个数据流进行 CRC 校验。
    • 先生,我只是想看看我得到了什么,因为只有一个缓冲区来计算 CRC32,我不知道如何比较我的结果。有谁知道一个可靠的网站,它将二进制文件作为输入并将其内容转换为 CRC32?
    • 请问Crc32Lookup 来自哪里?你可以扩展它(如果它是非优化版本)?
    【解决方案3】:

    为了补充我对您问题的评论,我在此处添加了贯穿整个过程的代码:将数据生成为线性数组,将 CRC32 添加到传输的数据中,注入错误,以及使用计算出的 CRC32 接收“块”和错误检测。您可能只对“接收”部分感兴趣,但我认为有一个完整的示例可以让您更清楚地理解。

    #include <stdio.h>
    #include <stdlib.h>
    #include <stdint.h>
    #include <time.h>
    
    // ---------------------- buildCRC32table ------------------------------
    static const uint32_t CRC32_POLY     = 0xEDB88320;
    static const uint32_t CRC32_XOR_MASK = 0xFFFFFFFF;
    
    static uint32_t CRC32TABLE[256];
    
    void buildCRC32table (void)
    {
        uint32_t crc32;
    
        for (uint16_t byte = 0; byte < 256; byte++)
        {
            crc32 = byte;
    
            // iterate thru all 8 bits
            for (int i = 0; i < 8; i++)
            {
                uint8_t feedback = crc32 & 1;
                crc32 = (crc32 >> 1);
    
                if (feedback)
                {
                    crc32 ^= CRC32_POLY;
                }
            }
    
            CRC32TABLE[byte] = crc32;
        }
    }
    
    // -------------------------- myCRC32 ----------------------------------
    uint32_t myCRC32 (uint32_t previousCRC32, uint8_t *pData, int dataLen)
    {
        uint32_t newCRC32 = previousCRC32 ^ CRC32_XOR_MASK; // remove last XOR mask (or add first)
    
        // add new data to CRC32
        while (dataLen--)
        {
            uint32_t crc32Top24bits = newCRC32 >> 8;
            uint8_t  crc32Low8bits  = newCRC32 & 0x000000FF;
            uint8_t  data           = *pData++;
    
            newCRC32 = crc32Top24bits ^ CRC32TABLE[crc32Low8bits ^ data];
        }
    
        newCRC32 ^= CRC32_XOR_MASK; // put XOR mask back
    
        return newCRC32;
    }
    
    // ------------------------------ main ---------------------------------
    int main()
    {
        // build CRC32 table
        buildCRC32table();
        uint32_t crc32;
    
        // use a union so we can access the same data linearly (TX) or by chunks (RX)
        union
        {
            uint8_t array[1024*1024];
            uint8_t chunk[1024][1024];
        } data;
    
        // use time to seed randomizer so we have different data every run
        srand((unsigned int)time(NULL));
    
        /////////////////////////////////////////////////////////////////////////// Build data to be transmitted
        ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
        // populate array with random data sparing space for the CRC32 at the end
        for (int i = 0; i < (sizeof(data.array) - sizeof(uint32_t)); i++)
        {
            data.array[i] = (uint8_t) (rand() & 0xFF);
        }
    
        // now compute array's CRC32
        crc32 = myCRC32(0, data.array, sizeof(data.array) - sizeof(uint32_t));
        printf ("array CRC32 = 0x%08X\n", crc32);
    
        // to store the CRC32 into the array, we want to remove the XOR mask so we can compute the CRC32
        // of all received data (including the CRC32 itself) and expect the same result all the time,
        // regardless of the data, when no errors are present
        crc32 ^= CRC32_XOR_MASK;
    
        // load CRC32 at the very end of the array
        data.array[sizeof(data.array) - 1] = (uint8_t)((crc32 >> 24) & 0xFF);
        data.array[sizeof(data.array) - 2] = (uint8_t)((crc32 >> 16) & 0xFF);
        data.array[sizeof(data.array) - 3] = (uint8_t)((crc32 >>  8) & 0xFF);
        data.array[sizeof(data.array) - 4] = (uint8_t)((crc32 >>  0) & 0xFF);
    
        /////////////////////////////////////////////// At this point, data is transmitted and errors may happen
        ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
        // to make things interesting, let's add one bit error with 1/8 probability
        if ((rand() % 8) == 0)
        {
            uint32_t index = rand() % sizeof(data.array);
            uint8_t errorBit = 1 << (rand() & 0x7);
    
            // add error
            data.array[index] ^= errorBit;
            printf("Error injected on byte %u, bit mask = 0x%02X\n", index, errorBit);
        }
        else
        {
            printf("No error injected\n");
        }
    
        /////////////////////////////////////////////////////// Once received, the data is processed in 'chunks'
        ////////////////////////////////////////////////////////////////////////////////////////////////////////
    
        // now we access the data and compute its CRC32 one chunk at a time
        crc32 = 0; // initialize CRC32
    
        for (int i = 0; i < 1024; i++)
        {
            crc32 = myCRC32(crc32, data.chunk[i], sizeof data.chunk[i]);
        }
    
        printf ("Final CRC32 = 0x%08X\n", crc32);
    
        // because the CRC32 algorithm applies an XOR mask at the end, when we have no errors, the computed
        // CRC32 will be the mask itself
        if (crc32 == CRC32_XOR_MASK)
        {
            printf ("No errors detected!\n");
        }
        else
        {
            printf ("Errors detected!\n");
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-03-31
      • 1970-01-01
      • 2021-11-29
      • 2020-04-03
      • 2018-01-28
      • 2011-02-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多