将Fletcher校验和从32位重新编码为8位

这次转换是否正确?

uint8_t fletcher8( uint8_t *data, uint8_t len ) { uint8_t sum1 = 0xff, sum2 = 0xff; while (len) { unsigned tlen = len > 360 ? 360 : len; len -= tlen; do { sum1 += *data++; sum2 += sum1; tlen -= sizeof( uint8_t ); } while (tlen); sum1 = (sum1 & 0xff) + (sum1 >> 4); sum2 = (sum2 & 0xff) + (sum2 >> 4); } /* Second reduction step to reduce sums to 4 bits */ sum1 = (sum1 & 0xff) + (sum1 >> 4); sum2 = (sum2 & 0xff) + (sum2 >> 4); return sum2 << 4 | sum1; } 

原版的:

 uint32_t fletcher32( uint16_t *data, size_t len ) { uint32_t sum1 = 0xffff, sum2 = 0xffff; while (len) { unsigned tlen = len > 360 ? 360 : len; len -= tlen; do { sum1 += *data++; sum2 += sum1; tlen -= sizeof( uint16_t ); } 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; } 

len将是8。

数据将输入数据[](1 – 8)

其实我不知道怎么处理这条线:unsigned tlen = len> 360? 360:len;

也许 – > int8_t tlen = len> 255? 255:len;

我认为你需要0xF掩码而不是0xFF。 32位使用16位掩码,32位的一半,8位使用8位掩码,不是8的一半,4位是8的一半。

 uint8_t fletcher8( uint8_t *data, uint8_t len ) { uint8_t sum1 = 0xf, sum2 = 0xf; while (len) { unsigned tlen = len > 360 ? 360 : len; len -= tlen; do { sum1 += *data++; sum2 += sum1; tlen -= sizeof( uint8_t ); } while (tlen); sum1 = (sum1 & 0xf) + (sum1 >> 4); sum2 = (sum2 & 0xf) + (sum2 >> 4); } /* Second reduction step to reduce sums to 4 bits */ sum1 = (sum1 & 0xf) + (sum1 >> 4); sum2 = (sum2 & 0xf) + (sum2 >> 4); return sum2 << 4 | sum1; } 

否则,您将创建一个不同的校验和,而不是Fletcher。 例如sum1正在执行我认为称为补码校验和的东西。 基本上它是先前的16位和你的情况下的4位,校验和,其中进位位被重新加入。用于互联网协议使得很容易修改数据包而不必计算整个数据包的校验和,你可以仅根据现有校验和添加和减去更改。

额外的减少步骤是针对极端情况,如果sum1 + = * data = 0x1F的结果使用四位方案,则进位的加法为0x01 + 0x0F = 0x10,需要将该进位添加回因此在循环外0x01 + 0x00 = 0x01。 否则,循环和之外的加法为零。 根据您的体系结构,您可以使用if(sum1和0x10)sum1 = 0x01等更快的执行速度; 而不是可能需要更多指示的狡猾的添加物。

当它们变成了一个不仅仅是一个带有进位的校验和的东西,就是两者组合时的最后一步。 例如,如果你只使用32位fletcher作为16位校验和,你浪费了你的时间,结果的低16位只是一个库存校验和,进位位重新加入,没什么特别的。 sum2是有趣的数字,因为它是sum1校验和的累积(sum1是数据的累积,sum2是校验和的累积)。

如何计算此tlen

其实我不知道怎么处理这条线:unsigned tlen = len> 360? 360:len;

该行似乎来自此Wikipedia部分 的旧版本 。 它现在已经改为359,在谈话页面上解释了理由。 该数字仅适用于求和16位实体,因为它是满足的最大数n

nn +5)/ 2×(2 16 -1)<2 32

换句话说,这是您可以在不执行模数减少的情况下添加块的大量次数,并且仍然可以避免溢出uint32_t 。 对于4位数据字和8位累加器,相应的值将为4,使用计算

nn +5)/ 2×(2 4 -1)<2 8

因此,如果更改数据大小,则必须修改该行。 您还可以更改代码以使用更大的数据类型来保持其总和,从而在减少之前汇总更多块。 但在这种情况下,您可能需要在循环内部执行多个缩减步骤。

例如,如果你使用uint32_t作为sum1sum2 ,那么你可以在溢出危险之前总计23927个半字节,但在此之后你需要最多7个减少formssum1 = (sum1 & 0xf) + (sum1 >> 4)将其煮沸到10x1e的范围,就像你原来的算法那样。 将其写为(sum1 - 1)%0xf + 1可能更有效。 在这种情况下,您甚至可以将范围从1到15更改为0到14,将总和初始化为0并将减少值写为sum1 %= 0xf 。 除非您要求与使用其他范围的实现兼容。

在原始版本sum1中,sum2是32位。 这就是之后比特转移的原因。 在你的情况下,你声明sum1,sum2为8位,所以位移是没有意义的。