Click here to Skip to main content
15,886,362 members
Articles / Programming Languages / C#

C# FNV Hash

Rate me:
Please Sign up or sign in to vote.
4.20/5 (7 votes)
18 May 2009CPOL2 min read 62.8K   630   13   9
A C# class for creating a custom sized FNV hash.

Introduction

Recently, I was faced with the problem of how to export a GUID field to a system that required 30 characters long alphanumeric-only IDs. Short GUID didn't work because of the non-alphanumeric characters that base 64 encoding would produce. Hex representation of an MD5 hash came to my mind, but its 128 bit size would produce a 32 character hex string. SHA1 was even worse. I realized I'd need a 120 bit hash for a 30 character hex string, and I started searching for it. Everything I found lower than 128 bits were some 32 and 64 bit solutions, but that wasn't strong enough for me. Then, I tried to find a custom size hash solution, and I discovered FVN hash.

Background

Detailed information and background about FNV hash can be found here. All the examples there are in C or in assembler, and since I didn't find any .NET (VB or C#) implementations on the web, I was forced to write one myself. I'm posting it here in case someone else would have similar or any other kind of need for it. I used the FNV-1a alternate algorithm, which was sufficient for my purposes.

Using the code

The code consists of a single class which you can find in the zipped .cs file above, you can just include it in your project. The code is dependent on a Big Integer solution (also found at CodeProject), so you'll need to download and include that in your project too. By the way, many thanks to the author of that article; without his code, this would have been much harder and complicated to write. Unfortunately, the author didn't implement the Pow (power of) method (needed for the FNV hash implementation), but someone has posted it in the forum of that article. So, after you download and include the BigInteger class in your project, please add the following code to it:

C#
public BigInteger Pow(int exp)
{
    return power(this, exp);
}

private static BigInteger power(BigInteger number, int exponent)
{
    if (exponent == 0)
        return new BigInteger("1", 10);
    if (exponent == 1)
        return number;
    if (exponent % 2 == 0)
        return square(power(number, exponent / 2));
    else
        return number * square(power(number, (exponent - 1) / 2));
}

private static BigInteger square(BigInteger num)
{
    return num * num;
}

And now the FVN hash class:

C#
using System;
using System.Collections.Generic;
using System.Text;

namespace SomeNamespace
{
    public class FnvHash
    {
        #region Constants

        private static readonly BigInteger fnv_prime_32 = 
            new BigInteger("16777619", 10);
        private static readonly BigInteger fnv_prime_64 = 
            new BigInteger("1099511628211", 10);
        private static readonly BigInteger fnv_prime_128 = 
            new BigInteger("309485009821345068724781371", 10);
        private static readonly BigInteger fnv_prime_256 = 
            new BigInteger("374144419156711147060143317175368453031918731002211", 10);
        private static readonly BigInteger fnv_prime_512 = 
            new BigInteger("3583591587484486736891907648909510844994632795575439"
            + "2558399825615420669938882575126094039892345713852759", 10);
        private static readonly BigInteger fnv_prime_1024 = 
            new BigInteger("5016456510113118655434598811035278955030765345404790"
            + "74430301752383111205510814745150915769222029538271616265187852689"
            + "52493852922918165243750837466913718040942718731604847379667202603"
            + "89217684476157468082573", 10);
        private static readonly BigInteger fnv_offset_32 = 
            new BigInteger("2166136261", 10);
        private static readonly BigInteger fnv_offset_64 = 
            new BigInteger("14695981039346656037", 10);
        private static readonly BigInteger fnv_offset_128 = 
            new BigInteger("275519064689413815358837431229664493455", 10);
        private static readonly BigInteger fnv_offset_256 = 
            new BigInteger("10002925795805258090707096862062570483709279601424119"
            + "3945225284501741471925557", 10);
        private static readonly BigInteger fnv_offset_512 = 
            new BigInteger("96593031294966694980094354007163104660904187456726378"
            + "961083743294344626579945829321977164384498130518922065398057844953"
            + "28239340083876191928701583869517785", 10);
        private static readonly BigInteger fnv_offset_1024 = 
            new BigInteger("14197795064947621068722070641403218320880622795441933"
            + "960878474914617582723252296732303717722150864096521202355549365628"
            + "174669108571814760471015076148029755969804077320157692458563003215"
            + "304957150157403644460363550505412711285966361610267868082893823963"
            + "790439336411086884584107735010676915", 10);        

        private static readonly BigInteger fnv_mod_32 = 
            new BigInteger("2", 10).Pow(32);
        private static readonly BigInteger fnv_mod_64 = 
            new BigInteger("2", 10).Pow(64);
        private static readonly BigInteger fnv_mod_128 = 
            new BigInteger("2", 10).Pow(128);
        private static readonly BigInteger fnv_mod_256 = 
            new BigInteger("2", 10).Pow(256);
        private static readonly BigInteger fnv_mod_512 = 
            new BigInteger("2", 10).Pow(512);
        private static readonly BigInteger fnv_mod_1024 = 
            new BigInteger("2", 10).Pow(1024);

        #endregion

        public static BigInteger GetHash(string value, int hashBitSize)
        {
            BigInteger fnv_prime = null;
            BigInteger fnv_offset = null;
            BigInteger fnv_mod = null;
            if (hashBitSize <= 32)
            {
                fnv_prime = fnv_prime_32;
                fnv_offset = fnv_offset_32;
                fnv_mod = fnv_mod_32;
            }
            else if (hashBitSize <= 64)
            {
                fnv_prime = fnv_prime_64;
                fnv_offset = fnv_offset_64;
                fnv_mod = fnv_mod_64;
            }
            else if (hashBitSize <= 128)
            {
                fnv_prime = fnv_prime_128;
                fnv_offset = fnv_offset_128;
                fnv_mod = fnv_mod_128;
            }
            else if (hashBitSize <= 256)
            {
                fnv_prime = fnv_prime_256;
                fnv_offset = fnv_offset_256;
                fnv_mod = fnv_mod_256;
            }
            else if (hashBitSize <= 512)
            {
                fnv_prime = fnv_prime_512;
                fnv_offset = fnv_offset_512;
                fnv_mod = fnv_mod_512;
            }
            else if (hashBitSize <= 1024)
            {
                fnv_prime = fnv_prime_1024;
                fnv_offset = fnv_offset_1024;
                fnv_mod = fnv_mod_1024;
            }
            else
            {
                throw new ArgumentOutOfRangeException("hashBitSize");
            }
            BigInteger hash = fnv_offset;
            for (int i = 0; i < value.Length; i++)
            {
                hash ^= (uint)value[i];
                hash %= fnv_mod;
                hash *= fnv_prime;
            }
            if (!IsPowerOfTwo(hashBitSize))
            {
                BigInteger mask = new BigInteger(
                    new string('f', (hashBitSize / 4) + (hashBitSize % 4 != 0 ? 1 : 0)
                ), 16);
                hash = (hash >> hashBitSize) ^ (mask & hash);
            }
            return hash;
        }

        private static bool IsPowerOfTwo(int number)
        {
            return (number & (number - 1)) == 0;
        }
    }
}

The usage is simple, for example:

C#
string hash = FnvHash.GetHash("SomeStringValue", 120).ToHexString();

Conclusion

There are many different situations where a custom size hash may be helpful, I hope others will find it as useful as I have.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Mono Ltd
Croatia Croatia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionWhy are you using big integer class?? Pin
nethsu2-Nov-10 1:33
nethsu2-Nov-10 1:33 
GeneralInteresting problem Pin
djlove27-May-09 0:22
djlove27-May-09 0:22 
GeneralRe: Interesting problem Pin
Jasmin Muharemovic13-Jul-09 21:56
Jasmin Muharemovic13-Jul-09 21:56 
QuestionUpdate? Pin
torial25-May-09 7:12
torial25-May-09 7:12 
AnswerRe: Update? Pin
Jasmin Muharemovic25-May-09 21:33
Jasmin Muharemovic25-May-09 21:33 
GeneralSo much code... Pin
Axel Rietschin18-May-09 6:30
professionalAxel Rietschin18-May-09 6:30 
GeneralRe: So much code... Pin
Jasmin Muharemovic18-May-09 22:09
Jasmin Muharemovic18-May-09 22:09 
Well, I tried to search a lot about shlwapi HashData and couldn't find any details or explanation of what kind of algorithm it uses, how secure it is and what is the probability of generating duplicates depending on the hash size. This simply leaves me with insufficient information on how good or reliable this is to use in a particular situation. The only bit of info I managed to google was here:

http://www.autohotkey.com/forum/topic8728-90.html[^]

Somewhere near the bottom, it is said:

"The Windows HashData function is not a cryptographic hash, so don't use this in security critical applications."

I don't know anything about the person who wrote that and can't say whether that's true or not, but as I said, having simply no information whatsoever on what this function does and how good or reliable it is, I can't consider it for my hashing purposes. In fact, if you can provide any insight or details about it, I'd be grateful.

Thanks,

.....................................................
Jasmin Muharemovic
Mono Ltd
http://www.mono-software.com
.....................................................

QuestionWhat kind of performance are you seeing? Pin
torial31-Mar-09 11:34
torial31-Mar-09 11:34 
AnswerRe: What kind of performance are you seeing? Pin
Jasmin Muharemovic31-Mar-09 21:45
Jasmin Muharemovic31-Mar-09 21:45 

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.