[util] Update constexpr hash to be FNV-1a
The constexpr_hash.h header has fallen out of use. As constexpr hashing will be used for IDs with the service locator protocol, update these hashes to be 32 and 64 bit FNV-1a, and replace the _h user-defined literal with _id (a 64-bit hash), and _id8 (a 32-bit hash folded down to 8 bits). These are now in the util/hash.h header along with the runtime hash functions.
This commit is contained in:
@@ -9,7 +9,6 @@ module("util",
|
||||
public_headers = [
|
||||
"util/basic_types.h",
|
||||
"util/bip_buffer.h",
|
||||
"util/constexpr_hash.h",
|
||||
"util/counted.h",
|
||||
"util/deque.h",
|
||||
"util/enum_bitfields.h",
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
/// file basic_types.h
|
||||
/// Type properties that would normally come from <type_traits>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace types {
|
||||
|
||||
template <bool B, typename T = void> struct enable_if {};
|
||||
@@ -59,5 +61,16 @@ template<typename B1> struct conjunction<B1> : B1 {};
|
||||
template<typename B1, typename... Bn>
|
||||
struct conjunction<B1, Bn...> : conditional<bool(B1::value), conjunction<Bn...>, B1>::type {};
|
||||
|
||||
template <unsigned N> struct sized_uint_type {};
|
||||
template <> struct sized_uint_type<8> { using type = uint8_t; };
|
||||
template <> struct sized_uint_type<16> { using type = uint16_t; };
|
||||
template <> struct sized_uint_type<32> { using type = uint32_t; };
|
||||
template <> struct sized_uint_type<64> { using type = uint64_t; };
|
||||
|
||||
template <unsigned N> struct sized_uint {
|
||||
static constexpr uint64_t mask = ((1<<N)-1);
|
||||
static constexpr unsigned bits = N;
|
||||
using type = typename sized_uint_type<N>::type;
|
||||
};
|
||||
|
||||
} // namespace types
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#pragma once
|
||||
/// \file constexpr_hash.h
|
||||
/// A complile-time hashing function
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
namespace util {
|
||||
|
||||
constexpr static const uint8_t pearson_hash_table[256] = {
|
||||
0x76,0x07,0xbe,0x47,0xcf,0x41,0x0a,0xe8,0x01,0x5c,0x9f,0xc5,0x24,0x63,0x9a,0x85,
|
||||
0x39,0x2c,0xe2,0x34,0xb9,0xf2,0xae,0x40,0x10,0x90,0x94,0xd1,0x98,0x2d,0x16,0xfd,
|
||||
0xc6,0x48,0x0d,0xce,0x74,0x43,0x28,0xf9,0x61,0x12,0xd0,0xcd,0xd8,0xd7,0xa8,0x78,
|
||||
0x73,0x70,0xcc,0x1e,0x17,0xa7,0x87,0x38,0x68,0x91,0xc1,0x04,0x3f,0xf5,0xde,0xa3,
|
||||
0x8a,0xe5,0x9b,0xec,0x97,0xd5,0x71,0x4a,0x20,0xca,0xc8,0xc4,0x83,0x53,0xe7,0x7b,
|
||||
0x64,0x31,0x06,0xe0,0x7a,0xb6,0x52,0x8c,0xba,0x58,0xcb,0xb5,0x37,0x51,0x59,0xa1,
|
||||
0x11,0xe3,0x5a,0xdb,0xe1,0x6d,0x46,0x62,0xaf,0xbd,0x57,0xb8,0x0e,0xf4,0xdd,0xa6,
|
||||
0x45,0xf8,0x35,0x42,0x56,0xdf,0xad,0x80,0xb2,0x0b,0x5b,0xd4,0x86,0xb3,0xf0,0xc9,
|
||||
0x3c,0xa5,0xc0,0x8e,0x55,0x77,0xeb,0x36,0x79,0xab,0x4c,0x25,0xed,0xa9,0x75,0x8f,
|
||||
0xee,0xc2,0x72,0x8b,0x60,0x2a,0xfa,0x32,0xe9,0xda,0x03,0x1b,0x27,0x69,0x18,0x9e,
|
||||
0x88,0x96,0x54,0x81,0x30,0x22,0x7c,0x4f,0xc7,0xef,0x5d,0xa4,0x67,0x44,0xc3,0x99,
|
||||
0xbb,0xd3,0x8d,0x65,0xb1,0x82,0x09,0x1a,0x13,0xd9,0x9c,0x4d,0xb0,0xfc,0xac,0xbc,
|
||||
0x6a,0x29,0x95,0x19,0x92,0xaa,0x49,0x7d,0x3b,0xfb,0x50,0xb7,0xf3,0x5e,0x3e,0x6b,
|
||||
0x3a,0x14,0x2b,0xb4,0xfe,0xe6,0x93,0x23,0xd6,0x1f,0xd2,0x0c,0x1d,0x9d,0x6c,0x66,
|
||||
0x1c,0x89,0xbf,0xf6,0xff,0x6f,0x84,0x6e,0x2e,0xea,0x21,0xf7,0x7f,0x33,0xf1,0xe4,
|
||||
0x3d,0x0f,0x05,0x08,0x4e,0xa2,0xa0,0x2f,0xdc,0x00,0x5f,0x15,0x7e,0x02,0x4b,0x26
|
||||
};
|
||||
|
||||
constexpr inline uint8_t pearson_hash_8(const char *s, uint8_t inv) {
|
||||
return (*s) ? pearson_hash_8(s + 1, pearson_hash_table[inv ^ *s]) : inv;
|
||||
}
|
||||
|
||||
constexpr inline uint32_t djb_hash_32(const char *s, int off = 0) {
|
||||
return !s[off] ? 5381 : (djb_hash_32(s, off+1)*33) ^ s[off];
|
||||
}
|
||||
|
||||
} // namespace util
|
||||
|
||||
constexpr inline uint8_t operator "" _h (const char *s, size_t len) {
|
||||
return util::pearson_hash_8(s, static_cast<uint8_t>(len & 0xff));
|
||||
}
|
||||
|
||||
@@ -4,40 +4,68 @@
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <util/basic_types.h>
|
||||
|
||||
namespace util {
|
||||
namespace fnv1a {
|
||||
|
||||
constexpr uint64_t fnv_64_prime = 0x100000001b3ull;
|
||||
constexpr uint64_t fnv1a_64_init = 0xcbf29ce484222325ull;
|
||||
constexpr uint32_t val_32 = 0x811c9dc5;
|
||||
constexpr uint32_t prime_32 = 0x1000193;
|
||||
constexpr uint64_t val_64 = 0xcbf29ce484222325;
|
||||
constexpr uint64_t prime_64 = 0x100000001b3;
|
||||
|
||||
/// Return the FNV-1a hash of the given 0-terminated string.
|
||||
inline uint64_t hash_string(char const *s, uint64_t init = 0) {
|
||||
if (!init) init = fnv1a_64_init;
|
||||
while(s && *s) {
|
||||
init ^= static_cast<uint64_t>(*s++);
|
||||
init *= fnv_64_prime;
|
||||
}
|
||||
return init;
|
||||
/// Evaluate the 32-bit constexpr FNV-1a hash of the given 0-terminated string.
|
||||
inline constexpr uint32_t hash32_const(const char* const str, uint32_t value = val_32) {
|
||||
return (str[0] == '\0') ? value :
|
||||
hash32_const(&str[1], (value ^ uint32_t(str[0])) * prime_32);
|
||||
}
|
||||
|
||||
/// Return the FNV-1a hash of the given buffer.
|
||||
inline uint64_t hash_buffer(const void *v, size_t len, uint64_t init = 0) {
|
||||
/// Evaluate the 64-bit constexpr FNV-1a hash of the given 0-terminated string.
|
||||
inline constexpr uint64_t hash64_const(const char* const str, uint64_t value = val_64) {
|
||||
return (str[0] == '\0') ? value :
|
||||
hash64_const(&str[1], (value ^ uint64_t(str[0])) * prime_64);
|
||||
}
|
||||
|
||||
/// Return the 64-bit FNV-1a hash of the given 0-terminated string.
|
||||
inline uint64_t hash64_string(char const *s, uint64_t value = val_64) {
|
||||
while(s && *s) {
|
||||
value ^= static_cast<uint64_t>(*s++);
|
||||
value *= prime_64;
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/// Return the 64-bit FNV-1a hash of the given buffer.
|
||||
inline uint64_t hash64(void const * const v, size_t len, uint64_t value = val_64) {
|
||||
uint8_t const *p = reinterpret_cast<uint8_t const*>(v);
|
||||
uint8_t const *end = p + len;
|
||||
if (!init) init = fnv1a_64_init;
|
||||
while(p < end) {
|
||||
init ^= static_cast<uint64_t>(*p++);
|
||||
init *= fnv_64_prime;
|
||||
value ^= static_cast<uint64_t>(*p++);
|
||||
value *= prime_64;
|
||||
}
|
||||
return init;
|
||||
return value;
|
||||
}
|
||||
|
||||
} // namespace fnv1a
|
||||
|
||||
template <unsigned N>
|
||||
constexpr inline typename types::sized_uint<N>::type hash_fold(typename types::sized_uint<N*2>::type value) {
|
||||
return (value >> types::sized_uint<N>::bits) ^ (value & types::sized_uint<N>::mask);
|
||||
}
|
||||
|
||||
template <unsigned N>
|
||||
constexpr inline typename types::sized_uint<N>::type hash_fold(typename types::sized_uint<N*4>::type value) {
|
||||
typename types::sized_uint<N*2>::type value2 = hash_fold<N*2>(value);
|
||||
return (value2 >> types::sized_uint<N>::bits) ^ (value2 & types::sized_uint<N>::mask);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline uint64_t hash(const T &v) {
|
||||
return hash_buffer(reinterpret_cast<const void*>(&v), sizeof(T));
|
||||
}
|
||||
inline uint64_t hash(const T &v) { return fnv1a::hash64(reinterpret_cast<const void*>(&v), sizeof(T)); }
|
||||
|
||||
template <> inline uint64_t hash<uint64_t>(const uint64_t &i) { return i; }
|
||||
template <> inline uint64_t hash<const char *>(const char * const &s) { return hash_string(s); }
|
||||
template <> inline uint64_t hash<const char *>(const char * const &s) { return fnv1a::hash64_string(s); }
|
||||
|
||||
} // namespace util
|
||||
|
||||
constexpr inline uint8_t operator "" _id (const char *s, size_t len) { return util::fnv1a::hash64_const(s); }
|
||||
constexpr inline uint8_t operator "" _id8 (const char *s, size_t len) { return util::hash_fold<8>(util::fnv1a::hash32_const(s)); }
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
#include <j6/syscalls.h>
|
||||
#include <j6/sysconf.h>
|
||||
#include <j6/types.h>
|
||||
#include <util/constexpr_hash.h>
|
||||
|
||||
#include "io.h"
|
||||
#include "serial.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <util/constexpr_hash.h>
|
||||
#include <util/hash.h>
|
||||
#include "test_case.h"
|
||||
|
||||
class hash_tests :
|
||||
@@ -6,19 +6,46 @@ class hash_tests :
|
||||
{
|
||||
};
|
||||
|
||||
TEST_CASE( hash_tests, equality_test )
|
||||
TEST_CASE( hash_tests, equality_test64 )
|
||||
{
|
||||
const unsigned hash1 = static_cast<unsigned>("hash1!"_h);
|
||||
const auto hash1 = static_cast<unsigned>("hash1!"_id);
|
||||
CHECK( hash1 == 210, "hash gave unexpected value");
|
||||
|
||||
const unsigned hash2 = static_cast<unsigned>("hash1!"_h);
|
||||
const auto hash2 = static_cast<unsigned>("hash1!"_id);
|
||||
CHECK(hash1 == hash2, "hashes of equal strings should be equal");
|
||||
|
||||
const unsigned hash3 = static_cast<unsigned>("not hash1!"_h);
|
||||
const auto hash3 = static_cast<unsigned>("not hash1!"_id);
|
||||
CHECK(hash1 != hash3, "hashes of different strings should not be equal");
|
||||
CHECK(hash3 == 37, "hash gave unexpected value");
|
||||
|
||||
const unsigned hash4 = static_cast<unsigned>("another thing that's longer"_h);
|
||||
const auto hash4 = static_cast<unsigned>("another thing that's longer"_id);
|
||||
CHECK(hash1 != hash4, "hashes of different strings should not be equal");
|
||||
CHECK(hash4 == 212, "hash gave unexpected value");
|
||||
}
|
||||
|
||||
TEST_CASE( hash_tests, equality_test8 )
|
||||
{
|
||||
const uint8_t type_ids[] = {
|
||||
"system"_id8,
|
||||
|
||||
"event"_id8,
|
||||
"channel"_id8,
|
||||
"endpoint"_id8,
|
||||
"mailbox"_id8,
|
||||
|
||||
"vma"_id8,
|
||||
|
||||
"process"_id8,
|
||||
"thread"_id8
|
||||
};
|
||||
constexpr unsigned id_count = sizeof(type_ids) / sizeof(type_ids[0]);
|
||||
|
||||
const uint8_t system = "system"_id8;
|
||||
|
||||
CHECK_BARE( type_ids[0] == system );
|
||||
for (unsigned i = 0; i < id_count; ++i) {
|
||||
for (unsigned j = i + 1; j < id_count; ++j) {
|
||||
CHECK_BARE( type_ids[i] != type_ids[j] );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user