Writing a Byte Array to a Hexadecimal String

I was finishing up work on a hashing library and started testing my hash values against other sources to ensure I was doing everything right. Unfortunately, my hashes were off. Long story short, I was converting the hashed byte array into a string using a Base64 string encoder, but what you are supposed to do is convert it into a Hexadecimal string – that is if you want to conform to standards. I searched around and found out how you are supposed do this from post called How do you convert Byte Array to Hexadecimal String, and vice versa, in C#? on Stack Overflow, as well as a post from MSDN entitled byte[] Array to Hex String. In the first post, two options are presented, but no information about which way is faster or why to do one way over the other. So I decided to find out myself because if I have the option, I’d prefer my code to be faster.

Option 1: Building the hex string using a for loop
In my meanders looking for a solution to the MD5 hash, this is the algorithm that I ran into the most. The method simply runs through each byte in the byte array and outputs the text based version using standard string formatting with a StringBuilder.

public static string CreateHexString(byte[] data)
{
StringBuilder hex = new StringBuilder(data.Length * 2);
foreach (byte b in data)
{
hex.AppendFormat("{0:x2}", b);
}
return hex.ToString();
}

Option 2: Using the bit converter and replacing the delimiters
The second option uses the BitConverter class to create a string – but the string that gets created is delimited by a series of dashes that must subsequently be removed. This is some very succinct code.

Option 3: Dark Magic
This piece of code from PZahra from the MSDN link uses right shifting and dark magic that culminates in a hexadecimal string. I modified it slightly from the original code so it would match the strings produced by the other hexadecimal methods.

public static string CreateHexString(byte[] data)
{
char[] c = new char[data.Length * 2];
byte b;
for (int y = 0, x = 0; y < data.Length; ++y, ++x)
{
b = ((byte)(data[y] >> 4));
c[x] = (char)(b > 9 ? b + 0x37 : b + 0x30);
b = ((byte)(data[y] & 0xF));
c[++x] = (char)(b > 9 ? b + 0x37 : b + 0x30);
}
}

Using my performance timer from a previous post (though it’s been slightly modified since then) I ran both options through a series of tests and determined the following:

Option 1 runs approximately 40,000 ops/sec
Option 2 runs approximately 282,000 ops/sec
Option 3 runs approximately 304,000 ops/sec

So, using the for-loop is the slowest possible way of doing things, which I find interesting because it seems to be widely used for this purpose. Opting for the BitConverter is more than 7 times faster than the for loop (at least on my system) and it only takes a single line of code to write. Perhaps the BitConverter is a bit obscure (pun definitely intended) so nobody uses it. And if you don’t mind losing a lot of readability, then you can eek out even more performance using PZahra’s code.