我很难确定32位变量的Fletcher校验算法的正确实现。维基百科提供了以下优化的实现:
uint32_t fletcher32( uint16_t const *data, size_t words ) {
uint32_t sum1 = 0xffff, sum2 = 0xffff;
size_t tlen;
while (words) {
tlen = words >= 359 ? 359 : words;
words -= tlen;
do {
sum2 += sum1 += *data++;
} while (--tlen);
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
}
/* Second reduction step to reduce sums to 16 bits */
sum1 = (sum1 & 0xffff) + (sum1 >> 16);
sum2 = (sum2 & 0xffff) + (sum2 >> 16);
return sum2 << 16 | sum1;
}
此外,我已经修改了维基百科文章中未经优化的16位示例,以计算32位校验和:
uint32_t naive_fletcher32(uint16_t *data, int words) {
uint32_t sum1 = 0;
uint32_t sum2 = 0;
int index;
for( index = 0; index < words; ++index ) {
sum1 = (sum1 + data[index]) % 0xffff;
sum2 = (sum2 + sum1) % 0xffff;
}
return (sum2 << 16) | sum1;
}
这两种实现都会产生相同的结果,例如对于字符串
abcdef
,结果为0x56502d2a
。为了验证这一点,我尝试找到算法的其他实现:
所有这些实现似乎都同意abcdef
的校验和是0x8180255
,而不是Wikipedia上给出的值。我将这归结于实现所操作的数据缓冲区。以上所有非Wikipedia的实现每次都只处理一个字节,而Wikipedia的实现使用16位字来计算校验和。如果我修改上面“朴素”的Wikipedia实现以按字节操作,则代码如下:uint32_t naive_fletcher32_per_byte(uint8_t *data, int words) {
uint32_t sum1 = 0;
uint32_t sum2 = 0;
int index;
for( index = 0; index < words; ++index ) {
sum1 = (sum1 + data[index]) % 0xffff;
sum2 = (sum2 + sum1) % 0xffff;
}
return (sum2 << 16) | sum1;
}
唯一变化的是签名。因此,这个修改后的天真实现和上述(除了维基百科)的实现都认为
abcdef
的校验和确实是 0x8180255
。我的问题是:哪一个是正确的?
naive_fletcher
中,循环中的% 0xffff
不是必需的。你可以在循环之后执行。 - Paul Ogilvie