Click here to Skip to main content
12,447,786 members (61,128 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

146.4K views
67 bookmarked
Posted

How to calculate a CRC in C#

, 13 Apr 2009 Ms-PL
Rate this:
Please Sign up or sign in to vote.
How to calculate CRC in C#?

CRC – Cyclic Redundancy Check is an algorithm which is widely used in different communication protocols, packing and packaging algorithms for assuring robustness of data. The idea behind it is simple – calculate a unique checksum (frame check sequence) for each data frame, based on its content and stick it at the end of each meaningful message. Once data is received, it’s possible to perform the same calculation and compare results – if results are similar, message is ok.

There are two kinds of CRC – 16 and 32 bit. There are also less used checksums for 8 and 64 bits. All this is about appending a string of zeros to the frame equal in number of frames and modulo two device by using generator polynomial containing one or more bits, then checksum to be generated. This is very similar to performing a bit-wise XOR operation in the frame, while the remainder is actually our CRC.

In many industries, the first polynomial is in use to create CRC tables and then apply it for performance purposes. The default polynomial, defined by IEEE 802.3 is 0xA001 for 16 bit and 0×04C11DB7 for 32 bit. We're in C#, thus we should use its inversed version which is 0×8408 for 16 bit and 0xEDB88320 for 32 bit. We're going to use those polynomials also in our sample.

So let’s start. Because CRC is HashAlgorithm after all, we can derive our classes from System.Security.Cryptography.HashAlgorithm class.

public class CRC16 : HashAlgorithm {
public class CRC32 : HashAlgorithm {

Then, upon first creation we'll generate hashtables with CRC values to enhance future performance. It’s all about values table for bytes from 0 to 255 , so we should calculate it only once and then we can use it statically.

[CLSCompliant(false)]
public CRC16(ushort polynomial) 
{
  HashSizeValue = 16;
  _crc16Table = (ushort[])_crc16TablesCache[polynomial];

  if (_crc16Table == null) {
    _crc16Table = CRC16._buildCRC16Table(polynomial);
    _crc16TablesCache.Add(polynomial, _crc16Table);
  }

  Initialize();
}

[CLSCompliant(false)]
public CRC32(uint polynomial) 
{
  HashSizeValue = 32;
  _crc32Table = (uint[])_crc32TablesCache[polynomial];

  if (_crc32Table == null) {
    _crc32Table = CRC32._buildCRC32Table(polynomial);
    _crc32TablesCache.Add(polynomial, _crc32Table);
  }

  Initialize();
}

Then let’s calculate it:

private static ushort[] _buildCRC16Table(ushort polynomial) 
{
  // 256 values representing ASCII character codes.
  ushort[] table = new ushort[256];
  for (ushort i = 0; i < table.Length; i++) 
  {
    ushort value = 0;
    ushort temp = i;

    for (byte j = 0; j < 8; j++) {
      if (((value ^ temp) & 0&times;0001) != 0) {
        value = (ushort)((value >> 1) ^ polynomial);
      } else {
        value >>= 1;
      }

      temp >>= 1;
    }

    table[i] = value;
  }

  return table;
}

private static uint[] _buildCRC32Table(uint polynomial) 
{
  uint crc;
  uint[] table = new uint[256];
  // 256 values representing ASCII character codes.
  for (int i = 0; i < 256; i++) 
  {
    crc = (uint)i;
    for (int j = 8; j > 0; j&ndash;) 
    {
      if ((crc & 1) == 1)
        crc = (crc >> 1) ^ polynomial;
      else
        crc >>= 1;
    }

    table[i] = crc;
  }

  return table;
}

The result will look like this for 32 bits:

0x00, 0x31, 0x62, 0x53, 0xC4, 0xF5, 0xA6, 0x97,
0xB9, 0x88, 0xDB, 0xEA, 0x7D, 0x4C, 0x1F, 0x2E,
0x43, 0x72, 0x21, 0x10, 0x87, 0xB6, 0xE5, 0xD4,
0xFA, 0xCB, 0x98, 0xA9, 0x3E, 0x0F, 0x5C, 0x6D,
0x86, 0xB7, 0xE4, 0xD5, 0x42, 0x73, 0x20, 0x11,
0x3F, 0x0E, 0x5D, 0x6C, 0xFB, 0xCA, 0x99, 0xA8,
0xC5, 0xF4, 0xA7, 0x96, 0x01, 0x30, 0x63, 0x52,
0x7C, 0x4D, 0x1E, 0x2F, 0xB8, 0x89, 0xDA, 0xEB,
0x3D, 0x0C, 0x5F, 0x6E, 0xF9, 0xC8, 0x9B, 0xAA,
0x84, 0xB5, 0xE6, 0xD7, 0x40, 0x71, 0x22, 0x13,
0x7E, 0x4F, 0x1C, 0x2D, 0xBA, 0x8B, 0xD8, 0xE9,
0xC7, 0xF6, 0xA5, 0x94, 0x03, 0x32, 0x61, 0x50,
0xBB, 0x8A, 0xD9, 0xE8, 0x7F, 0x4E, 0x1D, 0x2C,
0x02, 0x33, 0x60, 0x51, 0xC6, 0xF7, 0xA4, 0x95,
0xF8, 0xC9, 0x9A, 0xAB, 0x3C, 0x0D, 0x5E, 0x6F,
0x41, 0x70, 0x23, 0x12, 0x85, 0xB4, 0xE7, 0xD6,
0x7A, 0x4B, 0x18, 0x29, 0xBE, 0x8F, 0xDC, 0xED,
0xC3, 0xF2, 0xA1, 0x90, 0x07, 0x36, 0x65, 0x54,
0x39, 0x08, 0x5B, 0x6A, 0xFD, 0xCC, 0x9F, 0xAE,
0x80, 0xB1, 0xE2, 0xD3, 0x44, 0x75, 0x26, 0x17,
0xFC, 0xCD, 0x9E, 0xAF, 0x38, 0x09, 0x5A, 0x6B,
0x45, 0x74, 0x27, 0x16, 0x81, 0xB0, 0xE3, 0xD2,
0xBF, 0x8E, 0xDD, 0xEC, 0x7B, 0x4A, 0x19, 0x28,
0x06, 0x37, 0x64, 0x55, 0xC2, 0xF3, 0xA0, 0x91,
0x47, 0x76, 0x25, 0x14, 0x83, 0xB2, 0xE1, 0xD0,
0xFE, 0xCF, 0x9C, 0xAD, 0x3A, 0x0B, 0x58, 0x69,
0x04, 0x35, 0x66, 0x57, 0xC0, 0xF1, 0xA2, 0x93,
0xBD, 0x8C, 0xDF, 0xEE, 0x79, 0x48, 0x1B, 0x2A,
0xC1, 0xF0, 0xA3, 0x92, 0x05, 0x34, 0x67, 0x56,
0x78, 0x49, 0x1A, 0x2B, 0xBC, 0x8D, 0xDE, 0xEF,
0x82, 0xB3, 0xE0, 0xD1, 0x46, 0x77, 0x24, 0x15,
0x3B, 0x0A, 0x59, 0x68, 0xFF, 0xCE, 0x9D, 0xAC

Now, all we have to do upon request is to lookup into this hash table for related value and XOR it:

protected override void HashCore(byte[] buffer, int offset, int count) 
{
  for (int i = offset; i < count; i++) 
  {
    ulong ptr = (_crc & 0xFF) ^ buffer[i];
    _crc >>= 8;
    _crc ^= _crc32Table[ptr];
  }
}

new public byte[] ComputeHash(Stream inputStream) 
{
  byte[] buffer = new byte[4096];
  int bytesRead;

  while ((bytesRead = inputStream.Read(buffer, 0, 4096)) > 0) 
  {
    HashCore(buffer, 0, bytesRead);
  }

  return HashFinal();
}

protected override byte[] HashFinal() 
{
  byte[] finalHash = new byte[4];
  ulong finalCRC = _crc ^ _allOnes;
  
  finalHash[0] = (byte)((finalCRC >> 0) & 0xFF);
  finalHash[1] = (byte)((finalCRC >> 8) & 0xFF);
  finalHash[2] = (byte)((finalCRC >> 16) & 0xFF);
  finalHash[3] = (byte)((finalCRC >> 24) & 0xFF);

  return finalHash;
}

We're done. Have a good time and be good people. Also, I want to thank Boris for helping me with this article. He promised to write here some day…

License

This article, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)

Share

About the Author

Tamir Khason
Architect Better Place
Israel Israel
Hello! My name is Tamir Khason, and I am software architect, project manager, system analyst and [of course] programmer. In addition to writing big amount of documentation, I also write code, a lot of code. I used to work as a freelance architect, project manager, trainer, and consultant here, in Israel, but recently join the company with extremely persuasive idea - to make a world better place. I have very pretty wife and 3 charming kids, but unfortunately almost no time for them.

To be updated within articles, I publishing, visit my blog or subscribe RSS feed. Also you can follow me on Twitter to be up to date about my everyday life.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
Question[My vote of 2] Doesn't work Pin
jefffhaynes29-Apr-16 11:25
memberjefffhaynes29-Apr-16 11:25 
GeneralMy vote of 1 Pin
zzfima23-Apr-16 22:15
memberzzfima23-Apr-16 22:15 
QuestionHow many characters of result CRC32Hash Pin
BuiLuongTruong2-Aug-15 21:25
memberBuiLuongTruong2-Aug-15 21:25 
Generalfff Pin
melbin mp6-Jul-15 6:25
membermelbin mp6-Jul-15 6:25 
QuestionIs it unique always. Pin
Member 103097022-Oct-14 11:07
professionalMember 103097022-Oct-14 11:07 
AnswerRe: Is it unique always. Pin
Tim Arheit15-Nov-14 15:47
memberTim Arheit15-Nov-14 15:47 
Questioncrc16 bug? Pin
Alberto Molero17-Sep-13 22:24
memberAlberto Molero17-Sep-13 22:24 
AnswerRe: crc16 bug? Pin
Alberto Molero17-Sep-13 22:26
memberAlberto Molero17-Sep-13 22:26 
GeneralMy vote of 1 Pin
Member 92037495-Sep-13 3:50
memberMember 92037495-Sep-13 3:50 
QuestionSmall bug when calculating crc over part of byte array Pin
Andrew Pearson29-Jul-13 14:31
memberAndrew Pearson29-Jul-13 14:31 
QuestionMessage Removed Pin
jack asshole12-Dec-12 17:01
memberjack asshole12-Dec-12 17:01 
AnswerRe: code is functional but is trash Pin
Andrew Pearson29-Jul-13 14:28
memberAndrew Pearson29-Jul-13 14:28 
QuestionI dont get it to work for a message with crc16..... is the message coded by another algorithm? Pin
ConnyWesth17-Jun-12 21:01
memberConnyWesth17-Jun-12 21:01 
I have done some research with the CRC, and i dont think the crc in my specific problem (AMBrc)is compliant to the CRC16-CCITT standard.

I downloaded the crc code to carlculate CRC16-CCITT and CRC32-CCITT but none of them works for calculating the AMBrc messages. I'm convinced it is a 16-bit CRC algorithm but dont know wich, there is so many different algorithms.

I added som test-code like this:

//////////////////////////////////////////////////////////////////////////////////
// This is file: Program.cs, it is the main startingpoint for the testproject.
//////////////////////////////////////////////////////////////////////////////////
// Author: Conny Westh, 2012
// E-Mail: conny.westh@verimentor.se
//////////////////////////////////////////////////////////////////////////////////
// To compile, you only need to include the file CRC16.cs in the project from
// http://www.codeproject.com/Articles/35134/How-to-calculate-CRC-in-C?msg=4283673#xx4283673xx
//////////////////////////////////////////////////////////////////////////////////
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
 
namespace crctest
{
	class Program
	{
		static void Main(string[] args)
		{
			//testCRC16();
			testRigg();
			Console.WriteLine("Press [ENTER] to continue...");
			Console.ReadLine();
 
		}
 

		public static void testRigg()
		{
			//byte[] bytes = new byte[10] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
			//byte[] bytes1 = new byte[10] { 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 };
			byte[] bytes13a = new byte[13] { 0x8e, 0x00, 0x00, 0x00, 0x74, 0xb8, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13b = new byte[13-8] { 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13c = new byte[13-4] { 0x8e, 0x00, 0x00, 0x00, 0x24, 0x00, 0x01, 0x00, 0x8f };
			byte[] bytes13d = new byte[13 - 7] { 0x00, 0x00, 0x24, 0x00, 0x01, 0x00 };
			byte[] bytes13e = new byte[13 - 9] { 0x24, 0x00, 0x01, 0x00};
 

			byte[] bytes13ar = new byte[bytes13a.Length];
			byte[] bytes13br = new byte[bytes13b.Length];
			byte[] bytes13cr = new byte[bytes13c.Length];
			byte[] bytes13dr = new byte[bytes13d.Length];
			byte[] bytes13er = new byte[bytes13b.Length];
 
			for (byte i = 0; i < bytes13a.Length; i++)
			{
				bytes13ar[bytes13a.Length - i - 1] = bytes13a[i];
			}
 
			for (byte i = 0; i < bytes13b.Length; i++)
			{
				bytes13br[bytes13b.Length - i - 1] = bytes13b[i];
			}
 
			for (byte i = 0; i < bytes13c.Length; i++)
			{
				bytes13cr[bytes13c.Length - i - 1] = bytes13c[i];
			}
 
			for (byte i = 0; i < bytes13d.Length; i++)
			{
				bytes13dr[bytes13d.Length - i - 1] = bytes13d[i];
			}
 
			for (byte i = 0; i < bytes13e.Length; i++)
			{
				bytes13er[bytes13e.Length - i - 1] = bytes13e[i];
			}
 

 
			try
			{
 
				Console.WriteLine("");
				Console.WriteLine("Test of transponder message 13 characters!");
				test("74b80000", "13a", bytes13a);
				test("74b80000", "13b", bytes13b);
				test("74b80000", "13c", bytes13c);
				test("74b80000", "13d", bytes13d);
				test("74b80000", "13e", bytes13e);
 
				Console.WriteLine("");
				Console.WriteLine("Test of transponder message 13 characters, reverse order!");
				test("74b80000", "13ar", bytes13ar);
				test("74b80000", "13br", bytes13br);
				test("74b80000", "13cr", bytes13cr);
				test("74b80000", "13dr", bytes13dr);
				test("74b80000", "13er", bytes13er);
 
			}
			catch
			{ }
		}
 
		public static void test(string expectedCRC, string arrayName, byte[] bytes)
		{
			Console.WriteLine("CRC-16 ({0}) for bytearray {1} is {2:x4}", expectedCRC, arrayName, getCRC16(bytes));
			//Console.WriteLine("Sum-16 ({0}) for bytearray {1} is {2:x4}", expectedCRC, arrayName, getSum16(bytes));
			//Console.WriteLine("CRC-32 ({0}) for bytearray {1} is {2:x8}", expectedCRC, arrayName, getCRC32(bytes));
			//Console.WriteLine("Sum-32 ({0}) for bytearray {1} is {2:x8}", expectedCRC, arrayName, getSum32(bytes));

		}
 

		public static UInt16 getCRC16(byte[] bytes)
		{
			CRC16 crc16 = new CRC16();
			byte[] newBytes = new byte[bytes.Length];
			byte[] retArr = new byte[2];
			for (int i=0; i<bytes.Length; i++) newBytes[i]=bytes[i];
			if (BitConverter.IsLittleEndian)
			{
				Array.Reverse(newBytes);
			}
			retArr=crc16.ComputeHash(newBytes);
			Console.Write("[{0:x2}{1:x2}]", retArr[0], retArr[1]);
			return BitConverter.ToUInt16(retArr,0);
		}
	}
}

This message is an initial message that the decoder sends to the RCM software and that "opens" the RCM software for recieving data from the decoder.

The message appears twice that is 13 bytes followed by another 13 bytes message in the same TCP/IP package.

I strongly believe that the array bytes13b (or maybe bytes13e) is the right one to calculate the CRC-value for.

In this message the CRC-value is 74 b8 00 00 that is in reverse order 47220 (0xb874), and the array is { 0x24, 0x00, 0x01, 0x00, 0x8f }, how will that happen?
/Conny Westh
Verimentor AB


modified 18-Jun-12 11:33am.

GeneralMy vote of 5 Pin
ShaneMcDonald27-Jan-11 11:55
memberShaneMcDonald27-Jan-11 11:55 
GeneralMy vote of 3 Pin
Reuben200529-Aug-10 0:03
memberReuben200529-Aug-10 0:03 
Questionhow you inveresed 0xA001 Pin
laithadhary25-May-10 22:57
memberlaithadhary25-May-10 22:57 
GeneralMy vote of 1 Pin
Soft_banuma5-Feb-10 22:17
memberSoft_banuma5-Feb-10 22:17 
GeneralFormat/Indentation Pin
Dust Signs24-Jun-09 21:26
memberDust Signs24-Jun-09 21:26 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.160811.3 | Last Updated 13 Apr 2009
Article Copyright 2009 by Tamir Khason
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid