零填充缓冲区/文件的CRC32计算

时间:2018-10-27 02:53:27

标签: algorithm crc crc32

如果我想为大量连续的零字节计算CRC32值,给定零位游程的长度,是否可以使用恒定时间公式?例如,如果我知道我的1000个字节全部填充了零,是否有办法避免循环进行1000次迭代(仅举一个例子,为解决这个问题,实际的零个数是无界的)?

3 个答案:

答案 0 :(得分:2)

您可以计算不使用O(1)时间而是使用O(log n )时间应用 n 零的结果。这是在zlib的crc32_combine()中完成的。构建二进制矩阵,该二进制矩阵表示将单个零位应用于CRC的操作。 32x32矩阵将GF(2)上的32位CRC相乘,其中加法被异或(^)取代,乘法被和(&)一点一点地取代。

然后可以对矩阵求平方以得到两个零的算子。求平方得到四个零。第三个平方被平方以得到八个零的运算符。依需要依此类推。

现在,可以基于要计算其CRC的零位数 n 中的一位将这些运算符集应用于CRC。

如果碰巧知道您将经常精确地应用那么多的零,则可以为任意数量的零位预先计算所得的矩阵运算符。那么它只是一个矩阵乘以一个向量,实际上就是O(1)。

您不需要使用此处另一个答案中建议的pclmulqdq指令,但是如果有的话,这样做会更快一些。不会更改操作的O()。

答案 1 :(得分:1)

如果1000是常数,则为32个值的预计算表,每个值表示 可以使用CRC到8000次幂调制的每个位。一组矩阵(CRC的每个字节一组)可用于一次处理一个字节。两种方法都是固定时间(固定循环数)O(1)。

如上所述,如果1000不是常数,则可以使用平方求幂,这将是O(log2(n))时间复杂度,或者是一些恒定数目的零位的预计算表的组合,例如为256,然后可以使用平方求幂,以便最后一步为O(log2(n%256))。


通常进行优化:对于具有零和非零元素的普通数据,在具有pclmulqdq(使用xmm寄存器)的现代X86上,可以实现快速的crc32(或crc16),尽管它接近500行汇编代码。英特尔文档:crc using pclmulqdqgithub fast crc16的示例源代码。对于32位CRC,需要不同的常量集。如果感兴趣的话,我将源代码转换为可与Visual Studio ML64.EXE(64位MASM)一起使用,并为左右移位32位CRC创建了示例,每个示例都为两个最受欢迎的CRC 32位多项式提供了两组常数。 (左移多边形:crc32:0x104C11DB7和crc32c:0x11EDC6F41,右移多边形的位反转)。

答案 2 :(得分:0)

CRC32基于GF(2)[X]中某些乘法的模多项式的乘法。棘手的部分是将非乘法与乘法相分离。

首先定义一个具有以下结构的稀疏文件(在Go中):

type SparseFile struct {
    FileBytes []SparseByte
    Size      uint64
}
type SparseByte struct {
    Position uint64
    Value    byte
}

您的情况应该是SparseFile{[]FileByte{}, 1000}

然后,该函数将是:

func IEEESparse (file SparseFile) uint32 {
    position2Index := map[uint64]int{}
    for i , v := range(file.FileBytes) {
        file.FileBytes[i].Value = bits.Reverse8(v.Value)
        position2Index[v.Position] = i
    }
    for i := 0; i < 4; i++ {
        index, ok := position2Index[uint64(i)]
        if !ok {
            file.FileBytes = append(file.FileBytes, SparseByte{Position: uint64(i), Value: 0xFF})
        } else {
            file.FileBytes[index].Value ^= 0xFF
        }
    }

    // Add padding
    file.Size += 4
    newReminder := bits.Reverse32(reminderIEEESparse(file))

    return newReminder ^ 0xFFFFFFFF
}

请注意:

  1. 以相反的顺序(每个字节)对位进行除法。
  2. 前四个字节与0xFF异或。
  3. 文件填充有4个字节。
  4. 提醒再次撤消。
  5. 提醒已再次异化。

内部函数reminderIEEESparse是真正的提醒,很容易在O(log n)中实现,其中n是文件的大小。

您可以找到完整的实现here