Search code examples
cmicrocontrollerpiccrcmicrochip

Remake of Fletcher checksum from 32bit to 8


Is this conversion right from the original?

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;
    }

Original:

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 will be 8.

data will have an input of data[] (1 - 8)

Actually I don't know what to do with the line: unsigned tlen = len > 360 ? 360 : len;

Maybe -> int8_t tlen = len > 255 ? 255 : len;


Solution

  • I think you need 0xF masks throughout not 0xFF. The 32 bit uses a 16 bit mask, half of 32, your 8 bit uses an 8 bit mask which is not half of 8, 4 bits is half of 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;
    }
    

    Otherwise you are creating a different checksum, not Fletcher. sum1 for example is performing what I think is called a ones complement checksum. Basically it is a 16 bit in prior and 4 bit in your case, checksum where the carry bits are added back in. used in internet protocols makes it very easy to modify a packet without having to compute the checksum on the whole packet, you can add and subtract only the changes against the existing checksum.

    The additional reduction step is for a corner case, if the result of sum1 += *data = 0x1F using a four bit scheme, then the adding of the carry bit is 0x01 + 0x0F = 0x10, you need to add that carry bit back in as well so outside the loop 0x01 + 0x00 = 0x01. Otherwise that outside the loop sum is adding in zero. Depending on your architecture you might execute faster with something like if(sum1&0x10) sum1=0x01; than the shifty adding thing that may take more instructions.

    Where it becomes something more than just a checksum with the carry bits added in is that last step when the two are combined. And if you for example only use the 32 bit fletcher as a 16 bit checksum, you have wasted your time, the lower 16 bits of the result are just a stock checksum with the carry bit added back in, nothing special. sum2 is the interesting number as it is an accumulation of sum1 checksums (sum1 is an accumulation of the data, sum2 is an accumulation of checksums).