From 2640cea1758bdddbc1d9d7e72f9c8f3720a11069 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Tue, 22 Feb 2022 00:20:00 -0800 Subject: [PATCH] [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. --- src/libraries/util/util.module | 1 - src/libraries/util/util/basic_types.h | 13 ++++ src/libraries/util/util/constexpr_hash.h | 42 ------------ src/libraries/util/util/hash.h | 68 +++++++++++++------ src/user/drv.uart/main.cpp | 1 - src/user/test_runner/tests/constexpr_hash.cpp | 39 +++++++++-- 6 files changed, 94 insertions(+), 70 deletions(-) delete mode 100644 src/libraries/util/util/constexpr_hash.h diff --git a/src/libraries/util/util.module b/src/libraries/util/util.module index b8009e6..222d6de 100644 --- a/src/libraries/util/util.module +++ b/src/libraries/util/util.module @@ -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", diff --git a/src/libraries/util/util/basic_types.h b/src/libraries/util/util/basic_types.h index 3e7219d..850f017 100644 --- a/src/libraries/util/util/basic_types.h +++ b/src/libraries/util/util/basic_types.h @@ -2,6 +2,8 @@ /// file basic_types.h /// Type properties that would normally come from +#include + namespace types { template struct enable_if {}; @@ -59,5 +61,16 @@ template struct conjunction : B1 {}; template struct conjunction : conditional, B1>::type {}; +template 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 struct sized_uint { + static constexpr uint64_t mask = ((1<::type; +}; } // namespace types diff --git a/src/libraries/util/util/constexpr_hash.h b/src/libraries/util/util/constexpr_hash.h deleted file mode 100644 index 12f99a7..0000000 --- a/src/libraries/util/util/constexpr_hash.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once -/// \file constexpr_hash.h -/// A complile-time hashing function - -#include -#include - -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(len & 0xff)); -} - diff --git a/src/libraries/util/util/hash.h b/src/libraries/util/util/hash.h index 537401c..dcd40e0 100644 --- a/src/libraries/util/util/hash.h +++ b/src/libraries/util/util/hash.h @@ -4,40 +4,68 @@ #include #include +#include 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(*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(*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(v); uint8_t const *end = p + len; - if (!init) init = fnv1a_64_init; while(p < end) { - init ^= static_cast(*p++); - init *= fnv_64_prime; + value ^= static_cast(*p++); + value *= prime_64; } - return init; + return value; +} + +} // namespace fnv1a + +template +constexpr inline typename types::sized_uint::type hash_fold(typename types::sized_uint::type value) { + return (value >> types::sized_uint::bits) ^ (value & types::sized_uint::mask); +} + +template +constexpr inline typename types::sized_uint::type hash_fold(typename types::sized_uint::type value) { + typename types::sized_uint::type value2 = hash_fold(value); + return (value2 >> types::sized_uint::bits) ^ (value2 & types::sized_uint::mask); } template -inline uint64_t hash(const T &v) { - return hash_buffer(reinterpret_cast(&v), sizeof(T)); -} +inline uint64_t hash(const T &v) { return fnv1a::hash64(reinterpret_cast(&v), sizeof(T)); } template <> inline uint64_t hash(const uint64_t &i) { return i; } -template <> inline uint64_t hash(const char * const &s) { return hash_string(s); } +template <> inline uint64_t hash(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)); } diff --git a/src/user/drv.uart/main.cpp b/src/user/drv.uart/main.cpp index 782431c..3b622b6 100644 --- a/src/user/drv.uart/main.cpp +++ b/src/user/drv.uart/main.cpp @@ -9,7 +9,6 @@ #include #include #include -#include #include "io.h" #include "serial.h" diff --git a/src/user/test_runner/tests/constexpr_hash.cpp b/src/user/test_runner/tests/constexpr_hash.cpp index cd31052..fedec81 100644 --- a/src/user/test_runner/tests/constexpr_hash.cpp +++ b/src/user/test_runner/tests/constexpr_hash.cpp @@ -1,4 +1,4 @@ -#include +#include #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("hash1!"_h); + const auto hash1 = static_cast("hash1!"_id); CHECK( hash1 == 210, "hash gave unexpected value"); - const unsigned hash2 = static_cast("hash1!"_h); + const auto hash2 = static_cast("hash1!"_id); CHECK(hash1 == hash2, "hashes of equal strings should be equal"); - const unsigned hash3 = static_cast("not hash1!"_h); + const auto hash3 = static_cast("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("another thing that's longer"_h); + const auto hash4 = static_cast("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] ); + } + } +}