Compile Time Hashing

1, Jul, 2015

Compile time string hashing has been done to death since the AltDevBlogADay article and the Humus post. I couldn’t find an implementation that didn’t make it look far more complicated than it really is, so here’s my take:

#include <stdio.h>
 
typedef unsigned int u32;
 
//
// Template version, evaluated at compile time in optimised builds
//
template <u32 N>
constexpr u32 HashFNVConst(const char(&text)[N])
{
	return (HashFNVConst<N-1>((const char(&)[N - 1])text) ^ static_cast<u32>(text[N - 2])) * 16777619u;
}
 
template<>
constexpr u32 HashFNVConst<1>(const char(&text)[1])
{
	return 2166136261u;
}
 
//
// Loop version, evaluated at runtime 
//
u32 HashFNV(const char* text)
{
	u32 hash = 2166136261u;
	for (const char* c=text ; *c ; ++c)
	{
		hash = hash ^ static_cast<u32>(*c);
		hash *= 16777619u;
	}
	return hash;
}
 
int main()
{
	const char * text = "Hello";
	printf("0x%x (Loop version)\\n", HashFNV(text));
	printf("0x%x (Template version)\\n", HashFNVConst("Hello"));
	return 0;
}

Run this code on IDEOne

This is a FNV1a hash. It uses constexpr so requires a C++11 compliant compiler (VS2015 or recent GCC / Clang). Here’s the obligatory disassembly:

	printf("0x%x (Template version)n", HashFNVConst("Hello"));
0102106F  push        0F55C314Bh  	<--- The hash
01021074  push        1022128h  
01021079  call        printf (01021010h)