C# CRC16 implementation help right shift vs left shift

192 views Asked by At

I am just starting to learn C#, and I created 2 methods based on CRC16 C# implementations on Sanity Free website.

Can someone help me understand when and why to use these two different C# CRC16 implementations, using Right shift or Left shift? I think the second one has a mistake in it.

    static ushort Crc16_SHL(byte[] bytes, ushort poly, ushort initialValue)
        {
             
            ushort[] table = new ushort[256];
            ushort temp, a;
            ushort crc = initialValue;
            for (int i = 0; i < table.Length; ++i)
            {
                temp = 0;
                a = (ushort)(i << 8);
                for (int j = 0; j < 8; ++j)
                {
                    if (((temp ^ a) & 0x8000) != 0)
                        temp = (ushort)((temp << 1) ^ poly);
                    else
                        temp <<= 1;
                    a <<= 1;
                }
                table[i] = temp;
            }
            for (int i = 0; i < bytes.Length; ++i)
            {
                crc = (ushort)((crc << 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
            }
            return crc;
        }

    static ushort Crc16_SHR(byte[] bytes, ushort poly, ushort initialValue)
        {
             
            ushort[] table = new ushort[256];
            ushort temp, a;
            ushort crc = initialValue;
            for (int i = 0; i < table.Length; ++i)
            {
                temp = 0;
                a = (ushort)(i >> 8);
                for (int j = 0; j < 8; ++j)
                {
                    if (((temp ^ a) & 0x0001) != 0)
                        temp = (ushort)((temp >> 1) ^ poly);
                    else
                        temp >>= 1;
                    a >>= 1;
                }
                table[i] = temp;
            }
            for (int i = 0; i < bytes.Length; ++i)
            {
                crc = (ushort)((crc >> 8) ^ table[((crc >> 8) ^ (0xff & bytes[i]))]);
            }
            return crc;
        }
1

There are 1 answers

0
Mark Adler On

If you are choosing your own CRC definition, where you are writing the code on both ends, it doesn't matter, other than a small speed benefit for the right shift.

It is more common however that you need to implement a CRC that has been defined as part of a protocol or format. Then part of that definition is whether the CRC is reflected or not. A reflected CRC uses right shifts, a non-reflected CRC uses left shifts.

You can see many such CRC definitions here. There you will see refin=true refout=true for reflected CRCs and refin=false refout=false for non-reflected CRCs. The other parameters are the width in bits, the polynomial, the initial value, and the final exclusive-or.

(You will find one weird CRC there with refin=false refout=true. That one needs the resulting CRC reflected.)

By the way, there are bugs in your Crc16_SHR(). Your a = (ushort)(i >> 8); makes a always zero! Get rid of a and use if (((temp ^ i) & 0x0001) != 0). A little further down, the table indexing is messed up, where that line throws away the low eight bits of crc entirely! That needs to be: table[0xff & (crc ^ bytes[i])].

Also I'd recommend moving the tables outside of the function and initializing them just once for a given polynomial. There's no point in doing the exact same calculations over and over again every time you call these functions. Especially if your messages are short, in which case generating the table every time makes it slower instead of faster.