├── Makefile ├── LICENSE ├── constexprhash.h └── meowmeow.cpp /Makefile: -------------------------------------------------------------------------------- 1 | meowmeow: meowmeow.cpp constexprhash.h 2 | ${CXX} -std=c++11 -g -Wall -Wextra meowmeow.cpp -o meowmeow 3 | 4 | clean: 5 | rm -f meowmeow 6 | 7 | all: meowmeow 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2017 Ivan Vashchaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /constexprhash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | constexpr uint32_t djb2a(const char *s, uint32_t h = 5381) { 6 | return !*s ? h : djb2a(s + 1, 33 * h ^ (uint8_t)*s); 7 | } 8 | 9 | constexpr uint32_t fnv1a(const char *s, uint32_t h = 0x811C9DC5) { 10 | return !*s ? h : fnv1a(s + 1, (h ^ (uint8_t)*s) * 0x01000193); 11 | } 12 | 13 | constexpr uint32_t CRC32_TABLE[] = { 14 | 0x00000000, 0x1DB71064, 0x3B6E20C8, 0x26D930AC, 0x76DC4190, 0x6B6B51F4, 0x4DB26158, 0x5005713C, 15 | 0xEDB88320, 0xF00F9344, 0xD6D6A3E8, 0xCB61B38C, 0x9B64C2B0, 0x86D3D2D4, 0xA00AE278, 0xBDBDF21C}; 16 | constexpr uint32_t crc32(const char *s, uint32_t h = ~0) { 17 | #define CRC4(c, h) (CRC32_TABLE[((h) & 0xF) ^ (c)] ^ ((h) >> 4)) 18 | return !*s ? ~h : crc32(s + 1, CRC4((uint8_t)*s >> 4, CRC4((uint8_t)*s & 0xF, h))); 19 | #undef CRC4 20 | } 21 | 22 | namespace MurmurHash3 { 23 | constexpr uint32_t rotl(uint32_t x, int8_t r) { 24 | return (x << r) | (x >> (32 - r)); 25 | } 26 | constexpr uint32_t kmix(uint32_t k) { 27 | return rotl(k * 0xCC9E2D51, 15) * 0x1B873593; 28 | } 29 | constexpr uint32_t hmix(uint32_t h, uint32_t k) { 30 | return rotl(h ^ kmix(k), 13) * 5 + 0xE6546B64; 31 | } 32 | constexpr uint32_t shlxor(uint32_t x, int8_t l) { 33 | return (x >> l) ^ x; 34 | } 35 | constexpr uint32_t fmix(uint32_t h) { 36 | return shlxor(shlxor(shlxor(h, 16) * 0x85EBCA6B, 13) * 0xC2B2AE35, 16); 37 | } 38 | constexpr uint32_t body(const char *s, size_t n, uint32_t h) { 39 | return n < 4 ? h : body(s + 4, n - 4, hmix(h, s[0] | (s[1] << 8) | (s[2] << 16) | (s[3] << 24))); 40 | } 41 | constexpr uint32_t tail(const char *s, size_t n, uint32_t h) { 42 | return h ^ kmix(n == 3 ? s[0] | (s[1] << 8) | (s[2] << 16) : n == 2 ? s[0] | (s[1] << 8) : n == 1 ? s[0] : 0); 43 | } 44 | constexpr uint32_t shash(const char *s, size_t n, uint32_t seed) { 45 | return fmix(tail(s + (n & ~3), n & 3, body(s, n, seed)) ^ n); 46 | } 47 | } // namespace MurmurHash3 48 | 49 | constexpr uint32_t operator"" _H(const char *s, size_t size) { 50 | return MurmurHash3::shash(s, size, 0); 51 | } 52 | -------------------------------------------------------------------------------- /meowmeow.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "constexprhash.h" 8 | 9 | // dummy check that hashes is compile time constants 10 | void check(uint32_t h) { 11 | switch (h) { 12 | case djb2a("123456789"): 13 | case fnv1a("123456789"): 14 | case crc32("123456789"): 15 | case "123456789"_H: 16 | break; 17 | } 18 | } 19 | 20 | // regular version of murmur hash 3 21 | namespace MurmurHash3 { 22 | constexpr uint32_t body(const uint32_t *k, size_t n, uint32_t h) { 23 | return n < 1 ? h : body(k + 1, n - 1, hmix(h, *k)); 24 | } 25 | } // namespace MurmurHash3 26 | 27 | uint32_t murmur3(const void *p, size_t size, uint32_t seed) { 28 | using namespace MurmurHash3; 29 | uint32_t h = seed; 30 | h = body(static_cast(p), size / 4, h); 31 | h = tail(static_cast(p) + (size & ~3), size & 3, h); 32 | return fmix(h ^ size); 33 | } 34 | 35 | // simple hash map with linear lookup 36 | template 37 | struct dict { 38 | uint32_t keys[N] = {}; 39 | T values[N]; 40 | 41 | T &operator[](uint32_t key) { 42 | return setdefault(key, T()); 43 | } 44 | 45 | T &setdefault(uint32_t key, T &&defval) { 46 | auto i = key % N; 47 | for (; keys[i]; i = (i + 1) % N) 48 | if (keys[i] == key) 49 | return values[i]; 50 | keys[i] = key; 51 | return values[i] = std::move(defval); 52 | } 53 | 54 | void remove(uint32_t key) { 55 | auto i = key % N; 56 | for (; keys[i] != key; i = (i + 1) % N) 57 | if (keys[i] == 0) 58 | return; 59 | for (auto j = (i + 1) % N; keys[j]; j = (j + 1) % N) { 60 | auto k = keys[j] % N; 61 | if (i <= j ? (i >= k) || (k > j) : (i >= k) || (k > j)) { 62 | keys[i] = keys[j]; 63 | values[i] = std::move(values[j]); 64 | i = j; 65 | } 66 | } 67 | keys[i] = 0; 68 | } 69 | 70 | const T &lookup(uint32_t key, const T &defval) const { 71 | for (auto i = key % N; keys[i]; i = (i + 1) % N) 72 | if (keys[i] == key) 73 | return values[i]; 74 | return defval; 75 | } 76 | 77 | const T *find(uint32_t key) const { 78 | for (auto i = key % N; keys[i]; i = (i + 1) % N) 79 | if (keys[i] == key) 80 | return values + i; 81 | return nullptr; 82 | } 83 | }; 84 | 85 | int main(int argc, char **argv) { 86 | uint32_t seed = 0; 87 | int benchmark = 0; 88 | int hex = 0; 89 | 90 | int ch; 91 | while ((ch = getopt(argc, argv, "s:b:h")) != -1) { 92 | switch (ch) { 93 | case 's': { 94 | char *endptr; 95 | seed = strtol(optarg, &endptr, 0); 96 | if (*endptr) { 97 | fprintf(stderr, "%s: bad seed value -- %s\n", argv[0], optarg); 98 | exit(EXIT_FAILURE); 99 | } 100 | break; 101 | } 102 | case 'b': 103 | benchmark = strtol(optarg, nullptr, 0); 104 | break; 105 | case 'h': 106 | hex = 1; 107 | break; 108 | case '?': 109 | default: 110 | fprintf(stderr, "usage: %s [-s seed] [-b count] [-h] [strings ...]\n", argv[0]); 111 | exit(EXIT_FAILURE); 112 | } 113 | } 114 | 115 | if (benchmark) { 116 | size_t size = benchmark * (1 << 20); 117 | void *p = malloc(size); 118 | memset(p, rand() % 64, size); 119 | auto start = std::chrono::high_resolution_clock::now(); 120 | auto hash = murmur3(p, size, seed); 121 | auto end = std::chrono::high_resolution_clock::now(); 122 | free(p); 123 | std::chrono::duration elapsed = end - start; 124 | fprintf(stderr, "benchmark hashing: %d MiB (%#.8x)\nelapsed time: %f (%.2f MiB/sec)\n", benchmark, hash, elapsed.count(), size / elapsed.count() / (1 << 20)); 125 | } 126 | 127 | dict map; 128 | for (int arg = optind; arg < argc; ++arg) { 129 | uint32_t h = murmur3(argv[arg], strlen(argv[arg]), seed); 130 | printf(hex ? "%#.8x %s\n" : "%10u %s\n", h, argv[arg]); 131 | if (map.find(h)) { 132 | if (strcmp(map[h], argv[arg])) 133 | printf("! %s %s\n", map[h], argv[arg]); 134 | continue; 135 | } 136 | map[h] = argv[arg]; 137 | } 138 | 139 | return 0; 140 | } 141 | --------------------------------------------------------------------------------