├── LICENSE ├── README.md ├── blake2-impl.h ├── blake2.h ├── blake2b-ref.c ├── city.c ├── city.h ├── elastic_cuckoo_table.c ├── elastic_cuckoo_table.h └── makefile /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Dimitrios Skarlatos 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Elastic Cuckoo Hashing 2 | This repository contains a simple reference implementation of (elastic) cuckoo hashing. 3 | 4 | Please check our paper for details: 5 | 6 | ``` 7 | @inproceedings{Skarlatos:ElasticCuckooPageTables:ASPLOS20, 8 | author = {Skarlatos, Dimitrios and Kokolis, Apostolos and Xu, Tianyin and Torrellas, Josep}, 9 | title = {Elastic Cuckoo Page Tables: Rethinking Virtual Memory Translation for Parallelism}, 10 | year = {2020}, 11 | publisher = {Association for Computing Machinery}, 12 | booktitle = {Proceedings of the Twenty-Fifth International Conference on Architectural Support for Programming Languages and Operating Systems}, 13 | location = {Lausanne, Switzerland}, 14 | series = {ASPLOS ’20} 15 | } 16 | ``` 17 | 18 | [You can find the Elastic Cuckoo Page Tables paper here!](http://skarlat2.web.engr.illinois.edu/publications/cuckoo_asplos20.pdf) 19 | 20 | ## How to run 21 | 22 | 1) make 23 | 2) ./elastic_cuckoo ${#ways} ${size} ${hash_func} ${type} ${scheme} ${occupancy_thr} ${scale_factor} ${swaps} ${priority} 24 | 25 | Parameters: 26 | 1) ways: number of ways/nests >2 27 | 2) size: size of each way 28 | 3) hash_func: string of the hash function name to be used {city,blake2} 29 | 4) type: type of cuckoo hashtable {elastic, regular} 30 | 5) scheme: resizing method for the elastic cuckoo hashtable {oneshot, dynamic} 31 | 6) occupancy_thr: threshold to trigger resizing 32 | 7) swaps: number of elements to be rehashed 33 | 8) priority: bias a hashtable during insertion 34 | 35 | ## Hash functions 36 | 37 | 1) Blake2 from https://github.com/BLAKE2/BLAKE2 38 | 2) CityHash from https://github.com/google/cityhash 39 | 40 | ## Notes 41 | The purpose of this code is to demonstrate some of the characteristics of (elastic) cuckoo hashing and it has not been optimized for performance. 42 | -------------------------------------------------------------------------------- /blake2-impl.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | #ifndef BLAKE2_IMPL_H 16 | #define BLAKE2_IMPL_H 17 | 18 | #include 19 | #include 20 | 21 | #if !defined(__cplusplus) && \ 22 | (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) 23 | #if defined(_MSC_VER) 24 | #define BLAKE2_INLINE __inline 25 | #elif defined(__GNUC__) 26 | #define BLAKE2_INLINE __inline__ 27 | #else 28 | #define BLAKE2_INLINE 29 | #endif 30 | #else 31 | #define BLAKE2_INLINE inline 32 | #endif 33 | 34 | static BLAKE2_INLINE uint32_t load32(const void *src) { 35 | #if defined(NATIVE_LITTLE_ENDIAN) 36 | uint32_t w; 37 | memcpy(&w, src, sizeof w); 38 | return w; 39 | #else 40 | const uint8_t *p = (const uint8_t *)src; 41 | return ((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8) | 42 | ((uint32_t)(p[2]) << 16) | ((uint32_t)(p[3]) << 24); 43 | #endif 44 | } 45 | 46 | static BLAKE2_INLINE uint64_t load64(const void *src) { 47 | #if defined(NATIVE_LITTLE_ENDIAN) 48 | uint64_t w; 49 | memcpy(&w, src, sizeof w); 50 | return w; 51 | #else 52 | const uint8_t *p = (const uint8_t *)src; 53 | return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | 54 | ((uint64_t)(p[2]) << 16) | ((uint64_t)(p[3]) << 24) | 55 | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40) | 56 | ((uint64_t)(p[6]) << 48) | ((uint64_t)(p[7]) << 56); 57 | #endif 58 | } 59 | 60 | static BLAKE2_INLINE uint16_t load16(const void *src) { 61 | #if defined(NATIVE_LITTLE_ENDIAN) 62 | uint16_t w; 63 | memcpy(&w, src, sizeof w); 64 | return w; 65 | #else 66 | const uint8_t *p = (const uint8_t *)src; 67 | return (uint16_t)(((uint32_t)(p[0]) << 0) | ((uint32_t)(p[1]) << 8)); 68 | #endif 69 | } 70 | 71 | static BLAKE2_INLINE void store16(void *dst, uint16_t w) { 72 | #if defined(NATIVE_LITTLE_ENDIAN) 73 | memcpy(dst, &w, sizeof w); 74 | #else 75 | uint8_t *p = (uint8_t *)dst; 76 | *p++ = (uint8_t)w; 77 | w >>= 8; 78 | *p++ = (uint8_t)w; 79 | #endif 80 | } 81 | 82 | static BLAKE2_INLINE void store32(void *dst, uint32_t w) { 83 | #if defined(NATIVE_LITTLE_ENDIAN) 84 | memcpy(dst, &w, sizeof w); 85 | #else 86 | uint8_t *p = (uint8_t *)dst; 87 | p[0] = (uint8_t)(w >> 0); 88 | p[1] = (uint8_t)(w >> 8); 89 | p[2] = (uint8_t)(w >> 16); 90 | p[3] = (uint8_t)(w >> 24); 91 | #endif 92 | } 93 | 94 | static BLAKE2_INLINE void store64(void *dst, uint64_t w) { 95 | #if defined(NATIVE_LITTLE_ENDIAN) 96 | memcpy(dst, &w, sizeof w); 97 | #else 98 | uint8_t *p = (uint8_t *)dst; 99 | p[0] = (uint8_t)(w >> 0); 100 | p[1] = (uint8_t)(w >> 8); 101 | p[2] = (uint8_t)(w >> 16); 102 | p[3] = (uint8_t)(w >> 24); 103 | p[4] = (uint8_t)(w >> 32); 104 | p[5] = (uint8_t)(w >> 40); 105 | p[6] = (uint8_t)(w >> 48); 106 | p[7] = (uint8_t)(w >> 56); 107 | #endif 108 | } 109 | 110 | static BLAKE2_INLINE uint64_t load48(const void *src) { 111 | const uint8_t *p = (const uint8_t *)src; 112 | return ((uint64_t)(p[0]) << 0) | ((uint64_t)(p[1]) << 8) | 113 | ((uint64_t)(p[2]) << 16) | ((uint64_t)(p[3]) << 24) | 114 | ((uint64_t)(p[4]) << 32) | ((uint64_t)(p[5]) << 40); 115 | } 116 | 117 | static BLAKE2_INLINE void store48(void *dst, uint64_t w) { 118 | uint8_t *p = (uint8_t *)dst; 119 | p[0] = (uint8_t)(w >> 0); 120 | p[1] = (uint8_t)(w >> 8); 121 | p[2] = (uint8_t)(w >> 16); 122 | p[3] = (uint8_t)(w >> 24); 123 | p[4] = (uint8_t)(w >> 32); 124 | p[5] = (uint8_t)(w >> 40); 125 | } 126 | 127 | static BLAKE2_INLINE uint32_t rotr32(const uint32_t w, const unsigned c) { 128 | return (w >> c) | (w << (32 - c)); 129 | } 130 | 131 | static BLAKE2_INLINE uint64_t rotr64(const uint64_t w, const unsigned c) { 132 | return (w >> c) | (w << (64 - c)); 133 | } 134 | 135 | /* prevents compiler optimizing out memset() */ 136 | static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) { 137 | static void *(*const volatile memset_v)(void *, int, size_t) = &memset; 138 | memset_v(v, 0, n); 139 | } 140 | 141 | #endif 142 | -------------------------------------------------------------------------------- /blake2.h: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | #ifndef BLAKE2_H 16 | #define BLAKE2_H 17 | 18 | #include 19 | #include 20 | 21 | #if defined(_MSC_VER) 22 | #define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) 23 | #else 24 | #define BLAKE2_PACKED(x) x __attribute__((packed)) 25 | #endif 26 | 27 | #if defined(__cplusplus) 28 | extern "C" { 29 | #endif 30 | 31 | enum blake2s_constant { 32 | BLAKE2S_BLOCKBYTES = 64, 33 | BLAKE2S_OUTBYTES = 32, 34 | BLAKE2S_KEYBYTES = 32, 35 | BLAKE2S_SALTBYTES = 8, 36 | BLAKE2S_PERSONALBYTES = 8 37 | }; 38 | 39 | enum blake2b_constant { 40 | BLAKE2B_BLOCKBYTES = 128, 41 | BLAKE2B_OUTBYTES = 64, 42 | BLAKE2B_KEYBYTES = 64, 43 | BLAKE2B_SALTBYTES = 16, 44 | BLAKE2B_PERSONALBYTES = 16 45 | }; 46 | 47 | typedef struct blake2s_state__ { 48 | uint32_t h[8]; 49 | uint32_t t[2]; 50 | uint32_t f[2]; 51 | uint8_t buf[BLAKE2S_BLOCKBYTES]; 52 | size_t buflen; 53 | size_t outlen; 54 | uint8_t last_node; 55 | } blake2s_state; 56 | 57 | typedef struct blake2b_state__ { 58 | uint64_t h[8]; 59 | uint64_t t[2]; 60 | uint64_t f[2]; 61 | uint8_t buf[BLAKE2B_BLOCKBYTES]; 62 | size_t buflen; 63 | size_t outlen; 64 | uint8_t last_node; 65 | } blake2b_state; 66 | 67 | typedef struct blake2sp_state__ { 68 | blake2s_state S[8][1]; 69 | blake2s_state R[1]; 70 | uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; 71 | size_t buflen; 72 | size_t outlen; 73 | } blake2sp_state; 74 | 75 | typedef struct blake2bp_state__ { 76 | blake2b_state S[4][1]; 77 | blake2b_state R[1]; 78 | uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; 79 | size_t buflen; 80 | size_t outlen; 81 | } blake2bp_state; 82 | 83 | BLAKE2_PACKED(struct blake2s_param__ { 84 | uint8_t digest_length; /* 1 */ 85 | uint8_t key_length; /* 2 */ 86 | uint8_t fanout; /* 3 */ 87 | uint8_t depth; /* 4 */ 88 | uint32_t leaf_length; /* 8 */ 89 | uint32_t node_offset; /* 12 */ 90 | uint16_t xof_length; /* 14 */ 91 | uint8_t node_depth; /* 15 */ 92 | uint8_t inner_length; /* 16 */ 93 | /* uint8_t reserved[0]; */ 94 | uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ 95 | uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ 96 | }); 97 | 98 | typedef struct blake2s_param__ blake2s_param; 99 | 100 | BLAKE2_PACKED(struct blake2b_param__ { 101 | uint8_t digest_length; /* 1 */ 102 | uint8_t key_length; /* 2 */ 103 | uint8_t fanout; /* 3 */ 104 | uint8_t depth; /* 4 */ 105 | uint32_t leaf_length; /* 8 */ 106 | uint32_t node_offset; /* 12 */ 107 | uint32_t xof_length; /* 16 */ 108 | uint8_t node_depth; /* 17 */ 109 | uint8_t inner_length; /* 18 */ 110 | uint8_t reserved[14]; /* 32 */ 111 | uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ 112 | uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ 113 | }); 114 | 115 | typedef struct blake2b_param__ blake2b_param; 116 | 117 | typedef struct blake2xs_state__ { 118 | blake2s_state S[1]; 119 | blake2s_param P[1]; 120 | } blake2xs_state; 121 | 122 | typedef struct blake2xb_state__ { 123 | blake2b_state S[1]; 124 | blake2b_param P[1]; 125 | } blake2xb_state; 126 | 127 | /* Padded structs result in a compile-time error */ 128 | enum { 129 | BLAKE2_DUMMY_1 = 1 / (sizeof(blake2s_param) == BLAKE2S_OUTBYTES), 130 | BLAKE2_DUMMY_2 = 1 / (sizeof(blake2b_param) == BLAKE2B_OUTBYTES) 131 | }; 132 | 133 | /* Streaming API */ 134 | int blake2s_init(blake2s_state *S, size_t outlen); 135 | int blake2s_init_key(blake2s_state *S, size_t outlen, const void *key, 136 | size_t keylen); 137 | int blake2s_init_param(blake2s_state *S, const blake2s_param *P); 138 | int blake2s_update(blake2s_state *S, const void *in, size_t inlen); 139 | int blake2s_final(blake2s_state *S, void *out, size_t outlen); 140 | 141 | int blake2b_init(blake2b_state *S, size_t outlen); 142 | int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, 143 | size_t keylen); 144 | int blake2b_init_param(blake2b_state *S, const blake2b_param *P); 145 | int blake2b_update(blake2b_state *S, const void *in, size_t inlen); 146 | int blake2b_final(blake2b_state *S, void *out, size_t outlen); 147 | 148 | int blake2sp_init(blake2sp_state *S, size_t outlen); 149 | int blake2sp_init_key(blake2sp_state *S, size_t outlen, const void *key, 150 | size_t keylen); 151 | int blake2sp_update(blake2sp_state *S, const void *in, size_t inlen); 152 | int blake2sp_final(blake2sp_state *S, void *out, size_t outlen); 153 | 154 | int blake2bp_init(blake2bp_state *S, size_t outlen); 155 | int blake2bp_init_key(blake2bp_state *S, size_t outlen, const void *key, 156 | size_t keylen); 157 | int blake2bp_update(blake2bp_state *S, const void *in, size_t inlen); 158 | int blake2bp_final(blake2bp_state *S, void *out, size_t outlen); 159 | 160 | /* Variable output length API */ 161 | int blake2xs_init(blake2xs_state *S, const size_t outlen); 162 | int blake2xs_init_key(blake2xs_state *S, const size_t outlen, const void *key, 163 | size_t keylen); 164 | int blake2xs_update(blake2xs_state *S, const void *in, size_t inlen); 165 | int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); 166 | 167 | int blake2xb_init(blake2xb_state *S, const size_t outlen); 168 | int blake2xb_init_key(blake2xb_state *S, const size_t outlen, const void *key, 169 | size_t keylen); 170 | int blake2xb_update(blake2xb_state *S, const void *in, size_t inlen); 171 | int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); 172 | 173 | /* Simple API */ 174 | int blake2s(void *out, size_t outlen, const void *in, size_t inlen, 175 | const void *key, size_t keylen); 176 | int blake2b(void *out, size_t outlen, const void *in, size_t inlen, 177 | const void *key, size_t keylen); 178 | 179 | int blake2sp(void *out, size_t outlen, const void *in, size_t inlen, 180 | const void *key, size_t keylen); 181 | int blake2bp(void *out, size_t outlen, const void *in, size_t inlen, 182 | const void *key, size_t keylen); 183 | 184 | int blake2xs(void *out, size_t outlen, const void *in, size_t inlen, 185 | const void *key, size_t keylen); 186 | int blake2xb(void *out, size_t outlen, const void *in, size_t inlen, 187 | const void *key, size_t keylen); 188 | 189 | /* This is simply an alias for blake2b */ 190 | int blake2(void *out, size_t outlen, const void *in, size_t inlen, 191 | const void *key, size_t keylen); 192 | 193 | #if defined(__cplusplus) 194 | } 195 | #endif 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /blake2b-ref.c: -------------------------------------------------------------------------------- 1 | /* 2 | BLAKE2 reference source code package - reference C implementations 3 | 4 | Copyright 2012, Samuel Neves . You may use this under the 5 | terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at 6 | your option. The terms of these licenses can be found at: 7 | 8 | - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 9 | - OpenSSL license : https://www.openssl.org/source/license.html 10 | - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | More information about the BLAKE2 hash function can be found at 13 | https://blake2.net. 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | #include "blake2-impl.h" 21 | #include "blake2.h" 22 | 23 | static const uint64_t blake2b_IV[8] = { 24 | 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, 0x3c6ef372fe94f82bULL, 25 | 0xa54ff53a5f1d36f1ULL, 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, 26 | 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL}; 27 | 28 | static const uint8_t blake2b_sigma[12][16] = { 29 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 30 | {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}, 31 | {11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4}, 32 | {7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8}, 33 | {9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13}, 34 | {2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9}, 35 | {12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11}, 36 | {13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10}, 37 | {6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5}, 38 | {10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0}, 39 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, 40 | {14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3}}; 41 | 42 | static void blake2b_set_lastnode(blake2b_state *S) { S->f[1] = (uint64_t)-1; } 43 | 44 | /* Some helper functions, not necessarily useful */ 45 | static int blake2b_is_lastblock(const blake2b_state *S) { return S->f[0] != 0; } 46 | 47 | static void blake2b_set_lastblock(blake2b_state *S) { 48 | if (S->last_node) 49 | blake2b_set_lastnode(S); 50 | 51 | S->f[0] = (uint64_t)-1; 52 | } 53 | 54 | static void blake2b_increment_counter(blake2b_state *S, const uint64_t inc) { 55 | S->t[0] += inc; 56 | S->t[1] += (S->t[0] < inc); 57 | } 58 | 59 | static void blake2b_init0(blake2b_state *S) { 60 | size_t i; 61 | memset(S, 0, sizeof(blake2b_state)); 62 | 63 | for (i = 0; i < 8; ++i) 64 | S->h[i] = blake2b_IV[i]; 65 | } 66 | 67 | /* init xors IV with input parameter block */ 68 | int blake2b_init_param(blake2b_state *S, const blake2b_param *P) { 69 | const uint8_t *p = (const uint8_t *)(P); 70 | size_t i; 71 | 72 | blake2b_init0(S); 73 | 74 | /* IV XOR ParamBlock */ 75 | for (i = 0; i < 8; ++i) 76 | S->h[i] ^= load64(p + sizeof(S->h[i]) * i); 77 | 78 | S->outlen = P->digest_length; 79 | return 0; 80 | } 81 | 82 | int blake2b_init(blake2b_state *S, size_t outlen) { 83 | blake2b_param P[1]; 84 | 85 | if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) 86 | return -1; 87 | 88 | P->digest_length = (uint8_t)outlen; 89 | P->key_length = 0; 90 | P->fanout = 1; 91 | P->depth = 1; 92 | store32(&P->leaf_length, 0); 93 | store32(&P->node_offset, 0); 94 | store32(&P->xof_length, 0); 95 | P->node_depth = 0; 96 | P->inner_length = 0; 97 | memset(P->reserved, 0, sizeof(P->reserved)); 98 | memset(P->salt, 0, sizeof(P->salt)); 99 | memset(P->personal, 0, sizeof(P->personal)); 100 | return blake2b_init_param(S, P); 101 | } 102 | 103 | int blake2b_init_key(blake2b_state *S, size_t outlen, const void *key, 104 | size_t keylen) { 105 | blake2b_param P[1]; 106 | 107 | if ((!outlen) || (outlen > BLAKE2B_OUTBYTES)) 108 | return -1; 109 | 110 | if (!key || !keylen || keylen > BLAKE2B_KEYBYTES) 111 | return -1; 112 | 113 | P->digest_length = (uint8_t)outlen; 114 | P->key_length = (uint8_t)keylen; 115 | P->fanout = 1; 116 | P->depth = 1; 117 | store32(&P->leaf_length, 0); 118 | store32(&P->node_offset, 0); 119 | store32(&P->xof_length, 0); 120 | P->node_depth = 0; 121 | P->inner_length = 0; 122 | memset(P->reserved, 0, sizeof(P->reserved)); 123 | memset(P->salt, 0, sizeof(P->salt)); 124 | memset(P->personal, 0, sizeof(P->personal)); 125 | 126 | if (blake2b_init_param(S, P) < 0) 127 | return -1; 128 | 129 | { 130 | uint8_t block[BLAKE2B_BLOCKBYTES]; 131 | memset(block, 0, BLAKE2B_BLOCKBYTES); 132 | memcpy(block, key, keylen); 133 | blake2b_update(S, block, BLAKE2B_BLOCKBYTES); 134 | secure_zero_memory(block, BLAKE2B_BLOCKBYTES); /* Burn the key from stack */ 135 | } 136 | return 0; 137 | } 138 | 139 | #define G(r, i, a, b, c, d) \ 140 | do { \ 141 | a = a + b + m[blake2b_sigma[r][2 * i + 0]]; \ 142 | d = rotr64(d ^ a, 32); \ 143 | c = c + d; \ 144 | b = rotr64(b ^ c, 24); \ 145 | a = a + b + m[blake2b_sigma[r][2 * i + 1]]; \ 146 | d = rotr64(d ^ a, 16); \ 147 | c = c + d; \ 148 | b = rotr64(b ^ c, 63); \ 149 | } while (0) 150 | 151 | #define ROUND(r) \ 152 | do { \ 153 | G(r, 0, v[0], v[4], v[8], v[12]); \ 154 | G(r, 1, v[1], v[5], v[9], v[13]); \ 155 | G(r, 2, v[2], v[6], v[10], v[14]); \ 156 | G(r, 3, v[3], v[7], v[11], v[15]); \ 157 | G(r, 4, v[0], v[5], v[10], v[15]); \ 158 | G(r, 5, v[1], v[6], v[11], v[12]); \ 159 | G(r, 6, v[2], v[7], v[8], v[13]); \ 160 | G(r, 7, v[3], v[4], v[9], v[14]); \ 161 | } while (0) 162 | 163 | static void blake2b_compress(blake2b_state *S, 164 | const uint8_t block[BLAKE2B_BLOCKBYTES]) { 165 | uint64_t m[16]; 166 | uint64_t v[16]; 167 | size_t i; 168 | 169 | for (i = 0; i < 16; ++i) { 170 | m[i] = load64(block + i * sizeof(m[i])); 171 | } 172 | 173 | for (i = 0; i < 8; ++i) { 174 | v[i] = S->h[i]; 175 | } 176 | 177 | v[8] = blake2b_IV[0]; 178 | v[9] = blake2b_IV[1]; 179 | v[10] = blake2b_IV[2]; 180 | v[11] = blake2b_IV[3]; 181 | v[12] = blake2b_IV[4] ^ S->t[0]; 182 | v[13] = blake2b_IV[5] ^ S->t[1]; 183 | v[14] = blake2b_IV[6] ^ S->f[0]; 184 | v[15] = blake2b_IV[7] ^ S->f[1]; 185 | 186 | ROUND(0); 187 | ROUND(1); 188 | ROUND(2); 189 | ROUND(3); 190 | ROUND(4); 191 | ROUND(5); 192 | ROUND(6); 193 | ROUND(7); 194 | ROUND(8); 195 | ROUND(9); 196 | ROUND(10); 197 | ROUND(11); 198 | 199 | for (i = 0; i < 8; ++i) { 200 | S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; 201 | } 202 | } 203 | 204 | #undef G 205 | #undef ROUND 206 | 207 | int blake2b_update(blake2b_state *S, const void *pin, size_t inlen) { 208 | const unsigned char *in = (const unsigned char *)pin; 209 | if (inlen > 0) { 210 | size_t left = S->buflen; 211 | size_t fill = BLAKE2B_BLOCKBYTES - left; 212 | if (inlen > fill) { 213 | S->buflen = 0; 214 | memcpy(S->buf + left, in, fill); /* Fill buffer */ 215 | blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); 216 | blake2b_compress(S, S->buf); /* Compress */ 217 | in += fill; 218 | inlen -= fill; 219 | while (inlen > BLAKE2B_BLOCKBYTES) { 220 | blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); 221 | blake2b_compress(S, in); 222 | in += BLAKE2B_BLOCKBYTES; 223 | inlen -= BLAKE2B_BLOCKBYTES; 224 | } 225 | } 226 | memcpy(S->buf + S->buflen, in, inlen); 227 | S->buflen += inlen; 228 | } 229 | return 0; 230 | } 231 | 232 | int blake2b_final(blake2b_state *S, void *out, size_t outlen) { 233 | uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; 234 | size_t i; 235 | 236 | if (out == NULL || outlen < S->outlen) 237 | return -1; 238 | 239 | if (blake2b_is_lastblock(S)) 240 | return -1; 241 | 242 | blake2b_increment_counter(S, S->buflen); 243 | blake2b_set_lastblock(S); 244 | memset(S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen); /* Padding */ 245 | blake2b_compress(S, S->buf); 246 | 247 | for (i = 0; i < 8; ++i) /* Output full hash to temp buffer */ 248 | store64(buffer + sizeof(S->h[i]) * i, S->h[i]); 249 | 250 | memcpy(out, buffer, S->outlen); 251 | secure_zero_memory(buffer, sizeof(buffer)); 252 | return 0; 253 | } 254 | 255 | /* inlen, at least, should be uint64_t. Others can be size_t. */ 256 | int blake2b(void *out, size_t outlen, const void *in, size_t inlen, 257 | const void *key, size_t keylen) { 258 | blake2b_state S[1]; 259 | 260 | /* Verify parameters */ 261 | if (NULL == in && inlen > 0) 262 | return -1; 263 | 264 | if (NULL == out) 265 | return -1; 266 | 267 | if (NULL == key && keylen > 0) 268 | return -1; 269 | 270 | if (!outlen || outlen > BLAKE2B_OUTBYTES) 271 | return -1; 272 | 273 | if (keylen > BLAKE2B_KEYBYTES) 274 | return -1; 275 | 276 | if (keylen > 0) { 277 | if (blake2b_init_key(S, outlen, key, keylen) < 0) 278 | return -1; 279 | } else { 280 | if (blake2b_init(S, outlen) < 0) 281 | return -1; 282 | } 283 | 284 | blake2b_update(S, (const uint8_t *)in, inlen); 285 | blake2b_final(S, out, outlen); 286 | return 0; 287 | } 288 | 289 | int blake2(void *out, size_t outlen, const void *in, size_t inlen, 290 | const void *key, size_t keylen) { 291 | return blake2b(out, outlen, in, inlen, key, keylen); 292 | } 293 | 294 | #if defined(SUPERCOP) 295 | int crypto_hash(unsigned char *out, unsigned char *in, 296 | unsigned long long inlen) { 297 | return blake2b(out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0); 298 | } 299 | #endif 300 | 301 | #if defined(BLAKE2B_SELFTEST) 302 | #include "blake2-kat.h" 303 | #include 304 | int main(void) { 305 | uint8_t key[BLAKE2B_KEYBYTES]; 306 | uint8_t buf[BLAKE2_KAT_LENGTH]; 307 | size_t i, step; 308 | 309 | for (i = 0; i < BLAKE2B_KEYBYTES; ++i) 310 | key[i] = (uint8_t)i; 311 | 312 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) 313 | buf[i] = (uint8_t)i; 314 | 315 | /* Test simple API */ 316 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 317 | uint8_t hash[BLAKE2B_OUTBYTES]; 318 | blake2b(hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES); 319 | 320 | if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { 321 | goto fail; 322 | } 323 | } 324 | 325 | /* Test streaming API */ 326 | for (step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { 327 | for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { 328 | uint8_t hash[BLAKE2B_OUTBYTES]; 329 | blake2b_state S; 330 | uint8_t *p = buf; 331 | size_t mlen = i; 332 | int err = 0; 333 | 334 | if ((err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, 335 | BLAKE2B_KEYBYTES)) < 0) { 336 | goto fail; 337 | } 338 | 339 | while (mlen >= step) { 340 | if ((err = blake2b_update(&S, p, step)) < 0) { 341 | goto fail; 342 | } 343 | mlen -= step; 344 | p += step; 345 | } 346 | if ((err = blake2b_update(&S, p, mlen)) < 0) { 347 | goto fail; 348 | } 349 | if ((err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { 350 | goto fail; 351 | } 352 | 353 | if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { 354 | goto fail; 355 | } 356 | } 357 | } 358 | 359 | puts("ok"); 360 | return 0; 361 | fail: 362 | puts("error"); 363 | return -1; 364 | } 365 | #endif 366 | -------------------------------------------------------------------------------- /city.c: -------------------------------------------------------------------------------- 1 | // city.c - cityhash-c 2 | // CityHash on C 3 | // Copyright (c) 2011-2012, Alexander Nusov 4 | // 5 | // - original copyright notice - 6 | // Copyright (c) 2011 Google, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | // CityHash, by Geoff Pike and Jyrki Alakuijala 27 | // 28 | // This file provides CityHash64() and related functions. 29 | // 30 | // It's probably possible to create even faster hash functions by 31 | // writing a program that systematically explores some of the space of 32 | // possible hash functions, by using SIMD instructions, or by 33 | // compromising on hash quality. 34 | 35 | #include "city.h" 36 | #include 37 | 38 | static uint64 UNALIGNED_LOAD64(const char *p) { 39 | uint64 result; 40 | memcpy(&result, p, sizeof(result)); 41 | return result; 42 | } 43 | 44 | static uint32 UNALIGNED_LOAD32(const char *p) { 45 | uint32 result; 46 | memcpy(&result, p, sizeof(result)); 47 | return result; 48 | } 49 | 50 | #if !defined(WORDS_BIGENDIAN) 51 | 52 | #define uint32_in_expected_order(x) (x) 53 | #define uint64_in_expected_order(x) (x) 54 | 55 | #else 56 | 57 | #ifdef _MSC_VER 58 | #include 59 | #define bswap_32(x) _byteswap_ulong(x) 60 | #define bswap_64(x) _byteswap_uint64(x) 61 | 62 | #elif defined(__APPLE__) 63 | // Mac OS X / Darwin features 64 | #include 65 | #define bswap_32(x) OSSwapInt32(x) 66 | #define bswap_64(x) OSSwapInt64(x) 67 | 68 | #else 69 | #include 70 | #endif 71 | 72 | #define uint32_in_expected_order(x) (bswap_32(x)) 73 | #define uint64_in_expected_order(x) (bswap_64(x)) 74 | 75 | #endif // WORDS_BIGENDIAN 76 | 77 | #if !defined(LIKELY) 78 | #if HAVE_BUILTIN_EXPECT 79 | #define LIKELY(x) (__builtin_expect(!!(x), 1)) 80 | #else 81 | #define LIKELY(x) (x) 82 | #endif 83 | #endif 84 | 85 | static uint64 Fetch64(const char *p) { 86 | return uint64_in_expected_order(UNALIGNED_LOAD64(p)); 87 | } 88 | 89 | static uint32 Fetch32(const char *p) { 90 | return uint32_in_expected_order(UNALIGNED_LOAD32(p)); 91 | } 92 | 93 | // Some primes between 2^63 and 2^64 for various uses. 94 | static const uint64 k0 = 0xc3a5c85c97cb3127ULL; 95 | static const uint64 k1 = 0xb492b66fbe98f273ULL; 96 | static const uint64 k2 = 0x9ae16a3b2f90404fULL; 97 | static const uint64 k3 = 0xc949d7c7509e6557ULL; 98 | 99 | // Hash 128 input bits down to 64 bits of output. 100 | // This is intended to be a reasonably good hash function. 101 | static inline uint64 Hash128to64(const uint128 x) { 102 | // Murmur-inspired hashing. 103 | const uint64 kMul = 0x9ddfea08eb382d69ULL; 104 | uint64 a = (Uint128Low64(x) ^ Uint128High64(x)) * kMul; 105 | a ^= (a >> 47); 106 | uint64 b = (Uint128High64(x) ^ a) * kMul; 107 | b ^= (b >> 47); 108 | b *= kMul; 109 | return b; 110 | } 111 | 112 | // Bitwise right rotate. Normally this will compile to a single 113 | // instruction, especially if the shift is a manifest constant. 114 | static uint64 Rotate(uint64 val, int shift) { 115 | // Avoid shifting by 64: doing so yields an undefined result. 116 | return shift == 0 ? val : ((val >> shift) | (val << (64 - shift))); 117 | } 118 | 119 | // Equivalent to Rotate(), but requires the second arg to be non-zero. 120 | // On x86-64, and probably others, it's possible for this to compile 121 | // to a single instruction if both args are already in registers. 122 | static uint64 RotateByAtLeast1(uint64 val, int shift) { 123 | return (val >> shift) | (val << (64 - shift)); 124 | } 125 | 126 | static uint64 ShiftMix(uint64 val) { return val ^ (val >> 47); } 127 | 128 | static uint64 HashLen16(uint64 u, uint64 v) { 129 | uint128 result; 130 | result.first = u; 131 | result.second = v; 132 | return Hash128to64(result); 133 | } 134 | 135 | static uint64 HashLen0to16(const char *s, size_t len) { 136 | if (len > 8) { 137 | uint64 a = Fetch64(s); 138 | uint64 b = Fetch64(s + len - 8); 139 | return HashLen16(a, RotateByAtLeast1(b + len, len)) ^ b; 140 | } 141 | if (len >= 4) { 142 | uint64 a = Fetch32(s); 143 | return HashLen16(len + (a << 3), Fetch32(s + len - 4)); 144 | } 145 | if (len > 0) { 146 | uint8 a = s[0]; 147 | uint8 b = s[len >> 1]; 148 | uint8 c = s[len - 1]; 149 | uint32 y = (uint32)(a) + ((uint32)(b) << 8); 150 | uint32 z = len + ((uint32)(c) << 2); 151 | return ShiftMix(y * k2 ^ z * k3) * k2; 152 | } 153 | return k2; 154 | } 155 | 156 | // This probably works well for 16-byte strings as well, but it may be overkill 157 | // in that case. 158 | static uint64 HashLen17to32(const char *s, size_t len) { 159 | uint64 a = Fetch64(s) * k1; 160 | uint64 b = Fetch64(s + 8); 161 | uint64 c = Fetch64(s + len - 8) * k2; 162 | uint64 d = Fetch64(s + len - 16) * k0; 163 | return HashLen16(Rotate(a - b, 43) + Rotate(c, 30) + d, 164 | a + Rotate(b ^ k3, 20) - c + len); 165 | } 166 | 167 | // Return a 16-byte hash for 48 bytes. Quick and dirty. 168 | // Callers do best to use "random-looking" values for a and b. 169 | // static pair WeakHashLen32WithSeeds( 170 | uint128 WeakHashLen32WithSeeds6(uint64 w, uint64 x, uint64 y, uint64 z, 171 | uint64 a, uint64 b) { 172 | a += w; 173 | b = Rotate(b + a + z, 21); 174 | uint64 c = a; 175 | a += x; 176 | a += y; 177 | b += Rotate(a, 44); 178 | 179 | uint128 result; 180 | result.first = (uint64)(a + z); 181 | result.second = (uint64)(b + c); 182 | return result; 183 | } 184 | 185 | // Return a 16-byte hash for s[0] ... s[31], a, and b. Quick and dirty. 186 | // static pair WeakHashLen32WithSeeds( 187 | uint128 WeakHashLen32WithSeeds(const char *s, uint64 a, uint64 b) { 188 | return WeakHashLen32WithSeeds6(Fetch64(s), Fetch64(s + 8), Fetch64(s + 16), 189 | Fetch64(s + 24), a, b); 190 | } 191 | 192 | // Return an 8-byte hash for 33 to 64 bytes. 193 | static uint64 HashLen33to64(const char *s, size_t len) { 194 | uint64 z = Fetch64(s + 24); 195 | uint64 a = Fetch64(s) + (len + Fetch64(s + len - 16)) * k0; 196 | uint64 b = Rotate(a + z, 52); 197 | uint64 c = Rotate(a, 37); 198 | a += Fetch64(s + 8); 199 | c += Rotate(a, 7); 200 | a += Fetch64(s + 16); 201 | uint64 vf = a + z; 202 | uint64 vs = b + Rotate(a, 31) + c; 203 | a = Fetch64(s + 16) + Fetch64(s + len - 32); 204 | z = Fetch64(s + len - 8); 205 | b = Rotate(a + z, 52); 206 | c = Rotate(a, 37); 207 | a += Fetch64(s + len - 24); 208 | c += Rotate(a, 7); 209 | a += Fetch64(s + len - 16); 210 | uint64 wf = a + z; 211 | uint64 ws = b + Rotate(a, 31) + c; 212 | uint64 r = ShiftMix((vf + ws) * k2 + (wf + vs) * k0); 213 | return ShiftMix(r * k0 + vs) * k2; 214 | } 215 | 216 | uint64 CityHash64(const char *s, size_t len) { 217 | if (len <= 32) { 218 | if (len <= 16) { 219 | return HashLen0to16(s, len); 220 | } else { 221 | return HashLen17to32(s, len); 222 | } 223 | } else if (len <= 64) { 224 | return HashLen33to64(s, len); 225 | } 226 | 227 | // For strings over 64 bytes we hash the end first, and then as we 228 | // loop we keep 56 bytes of state: v, w, x, y, and z. 229 | uint64 x = Fetch64(s + len - 40); 230 | uint64 y = Fetch64(s + len - 16) + Fetch64(s + len - 56); 231 | uint64 z = HashLen16(Fetch64(s + len - 48) + len, Fetch64(s + len - 24)); 232 | uint64 temp; 233 | uint128 v = WeakHashLen32WithSeeds(s + len - 64, len, z); 234 | uint128 w = WeakHashLen32WithSeeds(s + len - 32, y + k1, x); 235 | x = x * k1 + Fetch64(s); 236 | 237 | // Decrease len to the nearest multiple of 64, and operate on 64-byte chunks. 238 | len = (len - 1) & ~(size_t)(63); 239 | do { 240 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 241 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 242 | x ^= w.second; 243 | y += v.first + Fetch64(s + 40); 244 | z = Rotate(z + w.first, 33) * k1; 245 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 246 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 247 | temp = z; 248 | z = x; 249 | x = temp; 250 | s += 64; 251 | len -= 64; 252 | } while (len != 0); 253 | return HashLen16(HashLen16(v.first, w.first) + ShiftMix(y) * k1 + z, 254 | HashLen16(v.second, w.second) + x); 255 | } 256 | 257 | uint64 CityHash64WithSeed(const char *s, size_t len, uint64 seed) { 258 | return CityHash64WithSeeds(s, len, k2, seed); 259 | } 260 | 261 | uint64 CityHash64WithSeeds(const char *s, size_t len, uint64 seed0, 262 | uint64 seed1) { 263 | return HashLen16(CityHash64(s, len) - seed0, seed1); 264 | } 265 | 266 | // A subroutine for CityHash128(). Returns a decent 128-bit hash for strings 267 | // of any length representable in signed long. Based on City and Murmur. 268 | static uint128 CityMurmur(const char *s, size_t len, uint128 seed) { 269 | uint64 a = Uint128Low64(seed); 270 | uint64 b = Uint128High64(seed); 271 | uint64 c = 0; 272 | uint64 d = 0; 273 | signed long l = len - 16; 274 | if (l <= 0) { // len <= 16 275 | a = ShiftMix(a * k1) * k1; 276 | c = b * k1 + HashLen0to16(s, len); 277 | d = ShiftMix(a + (len >= 8 ? Fetch64(s) : c)); 278 | } else { // len > 16 279 | c = HashLen16(Fetch64(s + len - 8) + k1, a); 280 | d = HashLen16(b + len, c + Fetch64(s + len - 16)); 281 | a += d; 282 | do { 283 | a ^= ShiftMix(Fetch64(s) * k1) * k1; 284 | a *= k1; 285 | b ^= a; 286 | c ^= ShiftMix(Fetch64(s + 8) * k1) * k1; 287 | c *= k1; 288 | d ^= c; 289 | s += 16; 290 | l -= 16; 291 | } while (l > 0); 292 | } 293 | a = HashLen16(a, c); 294 | b = HashLen16(d, b); 295 | 296 | uint128 result; 297 | result.first = (uint64)(a ^ b); 298 | result.second = (uint64)(HashLen16(b, a)); 299 | return result; 300 | } 301 | 302 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed) { 303 | if (len < 128) { 304 | return CityMurmur(s, len, seed); 305 | } 306 | 307 | // We expect len >= 128 to be the common case. Keep 56 bytes of state: 308 | // v, w, x, y, and z. 309 | uint128 v, w; 310 | uint64 x = Uint128Low64(seed); 311 | uint64 y = Uint128High64(seed); 312 | uint64 z = len * k1; 313 | uint64 temp; 314 | v.first = Rotate(y ^ k1, 49) * k1 + Fetch64(s); 315 | v.second = Rotate(v.first, 42) * k1 + Fetch64(s + 8); 316 | w.first = Rotate(y + z, 35) * k1 + x; 317 | w.second = Rotate(x + Fetch64(s + 88), 53) * k1; 318 | 319 | // This is the same inner loop as CityHash64(), manually unrolled. 320 | do { 321 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 322 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 323 | x ^= w.second; 324 | y += v.first + Fetch64(s + 40); 325 | z = Rotate(z + w.first, 33) * k1; 326 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 327 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 328 | temp = z; 329 | z = x; 330 | x = temp; 331 | s += 64; 332 | x = Rotate(x + y + v.first + Fetch64(s + 8), 37) * k1; 333 | y = Rotate(y + v.second + Fetch64(s + 48), 42) * k1; 334 | x ^= w.second; 335 | y += v.first + Fetch64(s + 40); 336 | z = Rotate(z + w.first, 33) * k1; 337 | v = WeakHashLen32WithSeeds(s, v.second * k1, x + w.first); 338 | w = WeakHashLen32WithSeeds(s + 32, z + w.second, y + Fetch64(s + 16)); 339 | temp = z; 340 | z = x; 341 | x = temp; 342 | s += 64; 343 | len -= 128; 344 | } while (LIKELY(len >= 128)); 345 | x += Rotate(v.first + z, 49) * k0; 346 | z += Rotate(w.first, 37) * k0; 347 | // If 0 < len < 128, hash up to 4 chunks of 32 bytes each from the end of s. 348 | size_t tail_done; 349 | for (tail_done = 0; tail_done < len;) { 350 | tail_done += 32; 351 | y = Rotate(x + y, 42) * k0 + v.second; 352 | w.first += Fetch64(s + len - tail_done + 16); 353 | x = x * k0 + w.first; 354 | z += w.second + Fetch64(s + len - tail_done); 355 | w.second += v.first; 356 | v = WeakHashLen32WithSeeds(s + len - tail_done, v.first + z, v.second); 357 | } 358 | // At this point our 56 bytes of state should contain more than 359 | // enough information for a strong 128-bit hash. We use two 360 | // different 56-byte-to-8-byte hashes to get a 16-byte final result. 361 | x = HashLen16(x, v.first); 362 | y = HashLen16(y + z, w.first); 363 | 364 | uint128 result; 365 | result.first = (uint64)(HashLen16(x + v.second, w.second) + y); 366 | result.second = (uint64)HashLen16(x + w.second, y + v.second); 367 | return result; 368 | } 369 | 370 | uint128 CityHash128(const char *s, size_t len) { 371 | uint128 r; 372 | if (len >= 16) { 373 | r.first = (uint64)(Fetch64(s) ^ k3); 374 | r.second = (uint64)(Fetch64(s + 8)); 375 | 376 | return CityHash128WithSeed(s + 16, len - 16, r); 377 | 378 | } else if (len >= 8) { 379 | r.first = (uint64)(Fetch64(s) ^ (len * k0)); 380 | r.second = (uint64)(Fetch64(s + len - 8) ^ k1); 381 | 382 | return CityHash128WithSeed(NULL, 0, r); 383 | } else { 384 | r.first = (uint64)k0; 385 | r.second = (uint64)k1; 386 | return CityHash128WithSeed(s, len, r); 387 | } 388 | } 389 | 390 | #ifdef __SSE4_2__ 391 | #include "citycrc.h" 392 | #include 393 | 394 | // Requires len >= 240. 395 | static void CityHashCrc256Long(const char *s, size_t len, uint32 seed, 396 | uint64 *result) { 397 | uint64 a = Fetch64(s + 56) + k0; 398 | uint64 b = Fetch64(s + 96) + k0; 399 | uint64 c = result[0] = HashLen16(b, len); 400 | uint64 d = result[1] = Fetch64(s + 120) * k0 + len; 401 | uint64 e = Fetch64(s + 184) + seed; 402 | uint64 f = seed; 403 | uint64 g = 0; 404 | uint64 h = 0; 405 | uint64 i = 0; 406 | uint64 j = 0; 407 | uint64 t = c + d; 408 | 409 | // 240 bytes of input per iter. 410 | size_t iters = len / 240; 411 | len -= iters * 240; 412 | do { 413 | #define CHUNK(multiplier, z) \ 414 | { \ 415 | uint64 old_a = a; \ 416 | a = Rotate(b, 41 ^ z) * multiplier + Fetch64(s); \ 417 | b = Rotate(c, 27 ^ z) * multiplier + Fetch64(s + 8); \ 418 | c = Rotate(d, 41 ^ z) * multiplier + Fetch64(s + 16); \ 419 | d = Rotate(e, 33 ^ z) * multiplier + Fetch64(s + 24); \ 420 | e = Rotate(t, 25 ^ z) * multiplier + Fetch64(s + 32); \ 421 | t = old_a; \ 422 | } \ 423 | f = _mm_crc32_u64(f, a); \ 424 | g = _mm_crc32_u64(g, b); \ 425 | h = _mm_crc32_u64(h, c); \ 426 | i = _mm_crc32_u64(i, d); \ 427 | j = _mm_crc32_u64(j, e); \ 428 | s += 40 429 | 430 | CHUNK(1, 1); 431 | CHUNK(k0, 0); 432 | CHUNK(1, 1); 433 | CHUNK(k0, 0); 434 | CHUNK(1, 1); 435 | CHUNK(k0, 0); 436 | } while (--iters > 0); 437 | 438 | while (len >= 40) { 439 | CHUNK(k0, 0); 440 | len -= 40; 441 | } 442 | if (len > 0) { 443 | s = s + len - 40; 444 | CHUNK(k0, 0); 445 | } 446 | j += i << 32; 447 | a = HashLen16(a, j); 448 | h += g << 32; 449 | b += h; 450 | c = HashLen16(c, f) + i; 451 | d = HashLen16(d, e + result[0]); 452 | j += e; 453 | i += HashLen16(h, t); 454 | e = HashLen16(a, d) + j; 455 | f = HashLen16(b, c) + a; 456 | g = HashLen16(j, i) + c; 457 | result[0] = e + f + g + h; 458 | a = ShiftMix((a + g) * k0) * k0 + b; 459 | result[1] += a + result[0]; 460 | a = ShiftMix(a * k0) * k0 + c; 461 | result[2] = a + result[1]; 462 | a = ShiftMix((a + e) * k0) * k0; 463 | result[3] = a + result[2]; 464 | } 465 | 466 | // Requires len < 240. 467 | static void CityHashCrc256Short(const char *s, size_t len, uint64 *result) { 468 | char buf[240]; 469 | memcpy(buf, s, len); 470 | memset(buf + len, 0, 240 - len); 471 | CityHashCrc256Long(buf, 240, ~(uint32)(len), result); 472 | } 473 | 474 | void CityHashCrc256(const char *s, size_t len, uint64 *result) { 475 | if (LIKELY(len >= 240)) { 476 | CityHashCrc256Long(s, len, 0, result); 477 | } else { 478 | CityHashCrc256Short(s, len, result); 479 | } 480 | } 481 | 482 | uint128 CityHashCrc128WithSeed(const char *s, size_t len, uint128 seed) { 483 | if (len <= 900) { 484 | return CityHash128WithSeed(s, len, seed); 485 | } else { 486 | uint64 result[4]; 487 | CityHashCrc256(s, len, result); 488 | uint64 u = Uint128High64(seed) + result[0]; 489 | uint64 v = Uint128Low64(seed) + result[1]; 490 | uint128 crc; 491 | crc.first = (uint64)(HashLen16(u, v + result[2])); 492 | crc.second = (uint64)(HashLen16(Rotate(v, 32), u * k0 + result[3])); 493 | return crc; 494 | } 495 | } 496 | 497 | uint128 CityHashCrc128(const char *s, size_t len) { 498 | if (len <= 900) { 499 | return CityHash128(s, len); 500 | } else { 501 | uint64 result[4]; 502 | CityHashCrc256(s, len, result); 503 | uint128 crc; 504 | crc.first = (uint64)result[2]; 505 | crc.second = (uint64)result[3]; 506 | return crc; 507 | } 508 | } 509 | 510 | #endif 511 | -------------------------------------------------------------------------------- /city.h: -------------------------------------------------------------------------------- 1 | // city.h - cityhash-c 2 | // CityHash on C 3 | // Copyright (c) 2011-2012, Alexander Nusov 4 | // 5 | // - original copyright notice - 6 | // Copyright (c) 2011 Google, Inc. 7 | // 8 | // Permission is hereby granted, free of charge, to any person obtaining a copy 9 | // of this software and associated documentation files (the "Software"), to deal 10 | // in the Software without restriction, including without limitation the rights 11 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | // copies of the Software, and to permit persons to whom the Software is 13 | // furnished to do so, subject to the following conditions: 14 | // 15 | // The above copyright notice and this permission notice shall be included in 16 | // all copies or substantial portions of the Software. 17 | // 18 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | // THE SOFTWARE. 25 | // 26 | // CityHash, by Geoff Pike and Jyrki Alakuijala 27 | // 28 | // This file provides a few functions for hashing strings. On x86-64 29 | // hardware in 2011, CityHash64() is faster than other high-quality 30 | // hash functions, such as Murmur. This is largely due to higher 31 | // instruction-level parallelism. CityHash64() and CityHash128() also perform 32 | // well on hash-quality tests. 33 | // 34 | // CityHash128() is optimized for relatively long strings and returns 35 | // a 128-bit hash. For strings more than about 2000 bytes it can be 36 | // faster than CityHash64(). 37 | // 38 | // Functions in the CityHash family are not suitable for cryptography. 39 | // 40 | // WARNING: This code has not been tested on big-endian platforms! 41 | // It is known to work well on little-endian platforms that have a small penalty 42 | // for unaligned reads, such as current Intel and AMD moderate-to-high-end CPUs. 43 | // 44 | // By the way, for some hash functions, given strings a and b, the hash 45 | // of a+b is easily derived from the hashes of a and b. This property 46 | // doesn't hold for any hash functions in this file. 47 | 48 | #ifndef CITY_HASH_H_ 49 | #define CITY_HASH_H_ 50 | 51 | #include 52 | #include 53 | 54 | typedef uint8_t uint8; 55 | typedef uint32_t uint32; 56 | typedef uint64_t uint64; 57 | 58 | typedef struct _uint128 uint128; 59 | struct _uint128 { 60 | uint64 first; 61 | uint64 second; 62 | }; 63 | 64 | #define Uint128Low64(x) (x).first 65 | #define Uint128High64(x) (x).second 66 | 67 | // Hash function for a byte array. 68 | uint64 CityHash64(const char *buf, size_t len); 69 | 70 | // Hash function for a byte array. For convenience, a 64-bit seed is also 71 | // hashed into the result. 72 | uint64 CityHash64WithSeed(const char *buf, size_t len, uint64 seed); 73 | 74 | // Hash function for a byte array. For convenience, two seeds are also 75 | // hashed into the result. 76 | uint64 CityHash64WithSeeds(const char *buf, size_t len, uint64 seed0, 77 | uint64 seed1); 78 | 79 | // Hash function for a byte array. 80 | uint128 CityHash128(const char *s, size_t len); 81 | 82 | // Hash function for a byte array. For convenience, a 128-bit seed is also 83 | // hashed into the result. 84 | uint128 CityHash128WithSeed(const char *s, size_t len, uint128 seed); 85 | 86 | #endif // CITY_HASH_H_ 87 | -------------------------------------------------------------------------------- /elastic_cuckoo_table.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Dimitrios Skarlatos 3 | * Contact: skarlat2@illinois.edu - http://skarlat2.web.engr.illinois.edu/ 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include "elastic_cuckoo_table.h" 11 | //#define DEBUG 1 12 | 13 | #define HASH_SIZE 8 14 | #define MAX_RETRIES 64 15 | #define EXTEND 1.25 16 | 17 | uint64_t hash_size(uint64_t size); 18 | 19 | void create(uint32_t d, uint64_t size, cuckooTable_t *hashtable, 20 | char *hash_func) { 21 | size_t i, j; 22 | 23 | hashtable->d = d; 24 | strcpy(hashtable->hash_func, hash_func); 25 | if (strcmp(hashtable->hash_func, "blake2") == 0) { 26 | hashtable->hash_size = hash_size(size); 27 | } else if (strcmp(hashtable->hash_func, "city") == 0) { 28 | hashtable->hash_size = log2(size); 29 | } else { 30 | assert(1 == 0 && "Unknown hash function\n"); 31 | } 32 | 33 | hashtable->size = size; 34 | hashtable->keys = (uint64_t *)malloc(d * sizeof(uint64_t)); 35 | hashtable->num_elems = (uint64_t *)malloc(d * sizeof(uint64_t)); 36 | hashtable->rehashed = (uint64_t *)malloc(d * sizeof(uint64_t)); 37 | hashtable->util = (float *)malloc(d * sizeof(float)); 38 | hashtable->occupancy = 0.0; 39 | 40 | #ifdef DEBUG 41 | printf("Creating a %u-ary Cuckoo hashtable\n", hashtable->d); 42 | printf("Hash function %s\n", hashtable->hash_func); 43 | printf("Total number of slots %lu\n", hashtable->size * d); 44 | printf("Hash-size %lu\n", hashtable->hash_size); 45 | #endif 46 | 47 | for (i = 0; i < d; i++) { 48 | hashtable->num_elems[i] = 0; 49 | hashtable->rehashed[i] = 0; 50 | hashtable->util[i] = 0.0; 51 | for (j = 0; j < 10; j++) { 52 | _rdrand64_step((unsigned long long *)&hashtable->keys[i]); 53 | } 54 | #ifdef DEBUG 55 | printf("Key[%lu] = %lu\n", i, hashtable->keys[i]); 56 | #endif 57 | } 58 | 59 | hashtable->hashtable = (elem_t **)malloc(d * sizeof(elem_t *)); 60 | for (i = 0; i < hashtable->d; i++) { 61 | hashtable->hashtable[i] = 62 | (elem_t *)malloc(hashtable->size * sizeof(elem_t)); 63 | for (j = 0; j < hashtable->size; j++) { 64 | hashtable->hashtable[i][j].valid = 0; 65 | hashtable->hashtable[i][j].value = i; 66 | } 67 | } 68 | } 69 | 70 | void create_elastic(uint32_t d, uint64_t size, elasticCuckooTable_t *hashtable, 71 | char *hash_func, float rehash_threshold, uint32_t scale, 72 | uint32_t swaps, uint8_t priority) { 73 | hashtable->current = (cuckooTable_t *)malloc(sizeof(cuckooTable_t)); 74 | hashtable->migrate = NULL; 75 | hashtable->rehash_threshold = rehash_threshold; 76 | hashtable->rehashing = 0; 77 | hashtable->d = d; 78 | hashtable->curr_size = size; 79 | if (strcmp(hash_func, "blake2") == 0) { 80 | hashtable->scale = 256; 81 | } else { 82 | hashtable->scale = scale; 83 | } 84 | hashtable->swaps = swaps; 85 | hashtable->priority = priority; 86 | hashtable->rehash_probes = 0; 87 | hashtable->rehash_elems = 0; 88 | strcpy(hashtable->hash_func, hash_func); 89 | create(d, size, hashtable->current, hash_func); 90 | } 91 | 92 | uint64_t rehash(elasticCuckooTable_t *hashtable, uint64_t swaps) { 93 | uint64_t i = 0, j = 0, retries = 0; 94 | uint32_t rehashed = 0; 95 | uint16_t nest = 0, new_nest = 0; 96 | cuckooTable_t *current = hashtable->current; 97 | elem_t move; 98 | 99 | do { 100 | _rdrand16_step(&nest); 101 | nest = nest % current->d; 102 | } while (current->rehashed[nest] == current->size); 103 | 104 | for (i = 0; i < swaps; i++) { 105 | if (current->rehashed[nest] < current->size) { 106 | if (current->hashtable[nest][current->rehashed[nest]].valid == 1) { 107 | move.valid = 1; 108 | move.value = current->hashtable[nest][current->rehashed[nest]].value; 109 | current->hashtable[nest][current->rehashed[nest]].valid = 0; 110 | current->rehashed[nest]++; 111 | current->num_elems[nest]--; 112 | if (hashtable->priority) { 113 | // this will end up trying first the new hashtable 114 | retries += insert_elastic(&move, hashtable, 1, nest); 115 | } else { 116 | // perform a random walk 117 | retries += insert_elastic(&move, hashtable, 0, 0); 118 | } 119 | hashtable->rehash_probes += retries; 120 | hashtable->rehash_elems++; 121 | } else { 122 | current->rehashed[nest]++; 123 | } 124 | for (j = 0; j < current->d; j++) { 125 | rehashed += current->rehashed[j]; 126 | } 127 | if (rehashed == current->size * current->d) { 128 | break; 129 | } 130 | do { 131 | _rdrand16_step(&new_nest); 132 | new_nest = new_nest % current->d; 133 | } while (new_nest == nest && current->rehashed[nest] == current->size); 134 | nest = new_nest; 135 | } 136 | } 137 | update_occupancy(current); 138 | update_occupancy(hashtable->migrate); 139 | return retries; 140 | } 141 | 142 | uint64_t evaluate_elasticity(elasticCuckooTable_t *hashtable, 143 | uint8_t complete) { 144 | uint64_t retries = 0; 145 | if (hashtable->current->occupancy > hashtable->rehash_threshold && 146 | !hashtable->rehashing) { 147 | hashtable->rehashing = 1; 148 | hashtable->migrate = (cuckooTable_t *)malloc(sizeof(cuckooTable_t)); 149 | create(hashtable->d, hashtable->curr_size * hashtable->scale, 150 | hashtable->migrate, hashtable->hash_func); 151 | } 152 | if (complete) { 153 | if (hashtable->rehashing) { 154 | while (hashtable->current->occupancy != 0) { 155 | retries += rehash(hashtable, hashtable->swaps); 156 | } 157 | hashtable->rehashing = 0; 158 | destroy(hashtable->current); 159 | hashtable->current = hashtable->migrate; 160 | hashtable->migrate = NULL; 161 | hashtable->curr_size *= hashtable->scale; 162 | } 163 | 164 | } else { 165 | if (hashtable->rehashing) { 166 | retries += rehash(hashtable, hashtable->swaps); 167 | } 168 | if (hashtable->current->occupancy == 0 && hashtable->rehashing) { 169 | hashtable->rehashing = 0; 170 | destroy(hashtable->current); 171 | hashtable->current = hashtable->migrate; 172 | hashtable->migrate = NULL; 173 | hashtable->curr_size *= hashtable->scale; 174 | } 175 | } 176 | 177 | return retries; 178 | } 179 | 180 | void destroy(cuckooTable_t *hashtable) { 181 | uint32_t i; 182 | free(hashtable->keys); 183 | free(hashtable->num_elems); 184 | free(hashtable->util); 185 | 186 | for (i = 0; i < hashtable->d; i++) { 187 | free(hashtable->hashtable[i]); 188 | } 189 | free(hashtable->hashtable); 190 | } 191 | 192 | void destroy_elastic(elasticCuckooTable_t *hashtable) { 193 | destroy(hashtable->current); 194 | if (hashtable->migrate != NULL) { 195 | destroy(hashtable->migrate); 196 | } 197 | } 198 | 199 | uint32_t insert_recursion(elem_t *elem, cuckooTable_t *hashtable, uint32_t nest, 200 | uint32_t tries) { 201 | uint16_t new_nest; 202 | uint64_t hash = 0; 203 | 204 | tries++; 205 | 206 | hash = gen_hash(elem, hashtable, nest); 207 | elem_t tmp; 208 | tmp.valid = 0; 209 | if (hashtable->hashtable[nest][hash].valid == 1) { 210 | tmp.valid = hashtable->hashtable[nest][hash].valid; 211 | tmp.value = hashtable->hashtable[nest][hash].value; 212 | hashtable->num_elems[nest]--; 213 | } 214 | 215 | hashtable->hashtable[nest][hash].valid = 1; 216 | hashtable->hashtable[nest][hash].value = elem->value; 217 | hashtable->num_elems[nest]++; 218 | 219 | // need to allocate the replaced element 220 | if (tmp.valid) { 221 | 222 | do { 223 | _rdrand16_step(&new_nest); 224 | new_nest = new_nest % hashtable->d; 225 | } while (new_nest == nest); 226 | nest = new_nest; 227 | 228 | if (tries > MAX_RETRIES) { 229 | return tries; 230 | } 231 | return insert_recursion(&tmp, hashtable, nest, tries); 232 | } 233 | update_occupancy(hashtable); 234 | return tries; 235 | } 236 | 237 | uint32_t insert(elem_t *elem, cuckooTable_t *hashtable) { 238 | uint32_t tries = 0; 239 | uint16_t nest = 0, new_nest = 0; 240 | uint64_t hash = 0; 241 | elem_t old; 242 | 243 | do { 244 | _rdrand16_step(&new_nest); 245 | new_nest = new_nest % hashtable->d; 246 | } while (new_nest == nest); 247 | nest = new_nest; 248 | 249 | // try to insert until MAX_RETRIES insertion attempts 250 | for (tries = 0; tries < MAX_RETRIES; tries++) { 251 | 252 | #ifdef DEBUG 253 | printf("Inserting element with value %llu, nest %u\n", elem->value, nest); 254 | #endif 255 | 256 | hash = gen_hash(elem, hashtable, nest); 257 | 258 | old.valid = 0; 259 | // remove previous element if it exists 260 | if (hashtable->hashtable[nest][hash].valid == 1) { 261 | old.valid = hashtable->hashtable[nest][hash].valid; 262 | old.value = hashtable->hashtable[nest][hash].value; 263 | hashtable->num_elems[nest]--; 264 | } 265 | 266 | // insert new element 267 | hashtable->hashtable[nest][hash].valid = 1; 268 | hashtable->hashtable[nest][hash].value = elem->value; 269 | hashtable->num_elems[nest]++; 270 | 271 | // we removed an element and we have to put it back 272 | if (old.valid) { 273 | // copy old element 274 | elem->value = old.value; 275 | elem->valid = 1; 276 | 277 | // pick new nest to try 278 | do { 279 | _rdrand16_step(&new_nest); 280 | new_nest = new_nest % hashtable->d; 281 | } while (new_nest == nest); 282 | nest = new_nest; 283 | } 284 | // we are done 285 | else { 286 | break; 287 | } 288 | } 289 | update_occupancy(hashtable); 290 | // printf("Insert Tries %lu\n",tries+1); 291 | return tries + 1; 292 | } 293 | 294 | uint32_t insert_elastic(elem_t *elem, elasticCuckooTable_t *hashtable, 295 | uint8_t bias, uint16_t bias_nest) { 296 | uint32_t tries = 0, current_inserts = 0, migrate_inserts = 0; 297 | uint16_t nest = 0, new_nest = 0; 298 | uint64_t hash = 0; 299 | elem_t old; 300 | cuckooTable_t *selectTable; 301 | 302 | if (bias) { 303 | nest = bias_nest; 304 | } else { 305 | _rdrand16_step(&nest); 306 | nest = nest % hashtable->current->d; 307 | } 308 | 309 | // try to insert until MAX_RETRIES insertion attempts 310 | for (tries = 0; tries < MAX_RETRIES; tries++) { 311 | 312 | #ifdef DEBUG 313 | printf("Inserting element with value %llu, nest %u\n", elem->value, nest); 314 | #endif 315 | 316 | hash = gen_hash(elem, hashtable->current, nest); 317 | 318 | if (hashtable->rehashing && hash < hashtable->current->rehashed[nest]) { 319 | hash = gen_hash(elem, hashtable->migrate, nest); 320 | selectTable = hashtable->migrate; 321 | migrate_inserts++; 322 | } else { 323 | selectTable = hashtable->current; 324 | current_inserts++; 325 | } 326 | 327 | old.valid = 0; 328 | // remove previous element if it exists 329 | if (selectTable->hashtable[nest][hash].valid == 1) { 330 | old.valid = selectTable->hashtable[nest][hash].valid; 331 | old.value = selectTable->hashtable[nest][hash].value; 332 | selectTable->num_elems[nest]--; 333 | } 334 | 335 | // insert new element 336 | selectTable->hashtable[nest][hash].valid = 1; 337 | selectTable->hashtable[nest][hash].value = elem->value; 338 | selectTable->num_elems[nest]++; 339 | 340 | // we removed an element and we have to put it back 341 | if (old.valid) { 342 | // copy old element 343 | elem->value = old.value; 344 | elem->valid = 1; 345 | 346 | // pick new nest to try 347 | do { 348 | _rdrand16_step(&new_nest); 349 | new_nest = new_nest % selectTable->d; 350 | } while (new_nest == nest); 351 | nest = new_nest; 352 | } 353 | // we are done 354 | else { 355 | break; 356 | } 357 | } 358 | if (migrate_inserts) { 359 | update_occupancy(hashtable->migrate); 360 | } 361 | if (current_inserts) { 362 | update_occupancy(hashtable->current); 363 | } 364 | return tries + 1; 365 | } 366 | 367 | void delete (elem_t *elem, cuckooTable_t *hashtable) { 368 | uint32_t nest; 369 | uint64_t hash = 0; 370 | 371 | for (nest = 0; nest < hashtable->d; nest++) { 372 | hash = gen_hash(elem, hashtable, nest); 373 | 374 | if (hashtable->hashtable[nest][hash].valid == 1 && 375 | hashtable->hashtable[nest][hash].value == elem->value) { 376 | 377 | hashtable->hashtable[nest][hash].valid = 0; 378 | hashtable->hashtable[nest][hash].value = 0; 379 | hashtable->num_elems[nest]--; 380 | update_occupancy(hashtable); 381 | return; 382 | } 383 | } 384 | } 385 | 386 | void delete_elastic(elem_t *elem, elasticCuckooTable_t *hashtable) { 387 | uint32_t nest = 0; 388 | uint64_t hash = 0; 389 | cuckooTable_t *selectTable; 390 | 391 | for (nest = 0; nest < hashtable->current->d; nest++) { 392 | hash = gen_hash(elem, hashtable->current, nest); 393 | 394 | if (hashtable->rehashing && hash < hashtable->current->rehashed[nest]) { 395 | hash = gen_hash(elem, hashtable->migrate, nest); 396 | selectTable = hashtable->migrate; 397 | } else { 398 | selectTable = hashtable->current; 399 | } 400 | 401 | if (selectTable->hashtable[nest][hash].valid == 1 && 402 | selectTable->hashtable[nest][hash].value == elem->value) { 403 | selectTable->hashtable[nest][hash].valid = 0; 404 | selectTable->hashtable[nest][hash].value = 0; 405 | selectTable->num_elems[nest]--; 406 | update_occupancy(selectTable); 407 | return; 408 | } 409 | } 410 | } 411 | 412 | elem_t *find(elem_t *elem, cuckooTable_t *hashtable) { 413 | uint32_t nest = 0; 414 | uint64_t hash = 0; 415 | 416 | for (nest = 0; nest < hashtable->d; nest++) { 417 | hash = gen_hash(elem, hashtable, nest); 418 | 419 | if (hashtable->hashtable[nest][hash].valid == 1 && 420 | hashtable->hashtable[nest][hash].value == elem->value) { 421 | return &hashtable->hashtable[nest][hash]; 422 | } 423 | } 424 | return NULL; 425 | } 426 | 427 | elem_t *find_elastic(elem_t *elem, elasticCuckooTable_t *hashtable) { 428 | uint32_t nest = 0; 429 | uint64_t hash = 0; 430 | cuckooTable_t *selectTable; 431 | 432 | for (nest = 0; nest < hashtable->current->d; nest++) { 433 | hash = gen_hash(elem, hashtable->current, nest); 434 | 435 | if (hashtable->rehashing && hash < hashtable->current->rehashed[nest]) { 436 | hash = gen_hash(elem, hashtable->migrate, nest); 437 | selectTable = hashtable->migrate; 438 | } else { 439 | selectTable = hashtable->current; 440 | } 441 | 442 | if (selectTable->hashtable[nest][hash].valid == 1 && 443 | selectTable->hashtable[nest][hash].value == elem->value) { 444 | return &selectTable->hashtable[nest][hash]; 445 | } 446 | } 447 | return NULL; 448 | } 449 | 450 | uint64_t gen_hash(elem_t *elem, cuckooTable_t *hashtable, uint32_t nest) { 451 | uint64_t hash = 0; 452 | 453 | #ifdef DEBUG 454 | for (i = 0; i < hashtable->d; i++) { 455 | if (strcmp(hashtable->hash_func, "blake2") == 0) { 456 | blake2b(&hash, hashtable->hash_size / 8, &elem->value, HASH_SIZE, 457 | &hashtable->keys[i], 8); 458 | } else if (strcmp(hashtable->hash_func, "city") == 0) { 459 | hash = CityHash64WithSeed(&elem->value, HASH_SIZE, hashtable->keys[i]); 460 | hash = hash & hmask((uint64_t)hashtable->hash_size); 461 | } else { 462 | assert(1 == 0 && "Unknown hash function\n"); 463 | } 464 | printf("Hash %lu\n", hash); 465 | } 466 | #endif 467 | 468 | if (strcmp(hashtable->hash_func, "blake2") == 0) { 469 | blake2b(&hash, hashtable->hash_size / 8, &elem->value, HASH_SIZE, 470 | &hashtable->keys[nest], 8); 471 | } else if (strcmp(hashtable->hash_func, "city") == 0) { 472 | hash = CityHash64WithSeed((const char *)&elem->value, HASH_SIZE, 473 | hashtable->keys[nest]); 474 | hash = hash & hmask((uint64_t)hashtable->hash_size); 475 | } else { 476 | assert(1 == 0 && "Unknown hash function\n"); 477 | } 478 | if (hash > hashtable->size) { 479 | printf("Hash value %llu, size %llu\n", hash, hashtable->size); 480 | assert(1 == 0 && "Hash value is larger than index\n"); 481 | } 482 | return hash; 483 | } 484 | 485 | void update_occupancy(cuckooTable_t *hashtable) { 486 | uint32_t i = 0; 487 | uint64_t total_elems = 0; 488 | for (i = 0; i < hashtable->d; i++) { 489 | hashtable->util[i] = hashtable->num_elems[i] / (float)hashtable->size; 490 | total_elems += hashtable->num_elems[i]; 491 | } 492 | hashtable->occupancy = total_elems / (float)(hashtable->d * hashtable->size); 493 | #ifdef DEBUG 494 | printf("Total elements: %lu\n", total_elems); 495 | printf("Occupancy: %f\n", hashtable->occupancy); 496 | #endif 497 | } 498 | 499 | void printTable(cuckooTable_t *hashtable) { 500 | size_t i, j; 501 | for (i = 0; i < hashtable->d; i++) { 502 | for (j = 0; j < hashtable->size; j++) { 503 | printf("(%u,%llu) | ", hashtable->hashtable[i][j].valid, 504 | hashtable->hashtable[i][j].value); 505 | } 506 | printf("\n"); 507 | } 508 | } 509 | 510 | uint64_t hash_size(uint64_t size) { 511 | uint64_t hash_size = 0; 512 | 513 | while (log2(size) > (float)hash_size) { 514 | hash_size += 8; 515 | } 516 | #ifdef DEBUG 517 | printf("hashtable size = %lu, requested size = %lu\n", hash_size, size); 518 | #endif 519 | return hash_size; 520 | } 521 | 522 | void simple_example(uint32_t d, uint64_t size, char *hash_func, uint8 elastic, 523 | uint8_t oneshot, float rehash_threshold, uint8_t scale, 524 | uint8_t swaps, uint8_t priority) { 525 | uint32_t i = 0, N = 512; 526 | uint64_t *test_values = NULL; 527 | elem_t new_elem; 528 | elasticCuckooTable_t elasticCuckooHT; 529 | cuckooTable_t cuckooHT; 530 | 531 | if (elastic) { 532 | create_elastic(d, size, &elasticCuckooHT, hash_func, rehash_threshold, 533 | scale, swaps, priority); 534 | } else { 535 | create(d, size, &cuckooHT, hash_func); 536 | } 537 | test_values = (uint64_t *)malloc(N * sizeof(uint64_t)); 538 | 539 | for (i = 0; i < N; i++) { 540 | test_values[i] = 0; 541 | } 542 | 543 | for (i = 0; i < N; i++) { 544 | new_elem.valid = 1; 545 | 546 | if (elastic) { 547 | do { 548 | _rdrand64_step((unsigned long long *)&new_elem.value); 549 | } while (find_elastic(&new_elem, &elasticCuckooHT) != NULL); 550 | 551 | } else { 552 | do { 553 | _rdrand64_step((unsigned long long *)&new_elem.value); 554 | } while (find(&new_elem, &cuckooHT) != NULL); 555 | } 556 | 557 | test_values[i] = new_elem.value; 558 | 559 | if (elastic) { 560 | insert_elastic(&new_elem, &elasticCuckooHT, 0, 0); 561 | } else { 562 | insert(&new_elem, &cuckooHT); 563 | } 564 | 565 | if (elastic) { 566 | evaluate_elasticity(&elasticCuckooHT, oneshot); 567 | } 568 | } 569 | 570 | if (elastic) { 571 | destroy_elastic(&elasticCuckooHT); 572 | } else { 573 | destroy(&cuckooHT); 574 | } 575 | } 576 | 577 | int main(int argc, char **argv) { 578 | uint32_t d = 4, size = 0, elastic = 0, oneshot = 0, scale = 0, swaps = 0; 579 | char hash_func[20]; 580 | float threshold = 0; 581 | uint8_t priority = 0; 582 | 583 | assert(argc == 10 || argc == 5); 584 | d = strtol(argv[1], NULL, 10); 585 | if (d < 2) { 586 | printf("Number of ways required to be greater than 2\n"); 587 | } 588 | size = strtol(argv[2], NULL, 10); 589 | if (strcmp(argv[3], "blake2") == 0) { 590 | strcpy(hash_func, "blake2"); 591 | } else if (strcmp(argv[3], "city") == 0) { 592 | strcpy(hash_func, "city"); 593 | } else { 594 | printf("Hash function not found\n"); 595 | return 0; 596 | } 597 | 598 | if (strcmp(argv[4], "elastic") == 0) { 599 | elastic = 1; 600 | } else { 601 | elastic = 0; 602 | } 603 | 604 | if (elastic) { 605 | if (strcmp(argv[5], "oneshot") == 0) { 606 | oneshot = 1; 607 | } else { 608 | oneshot = 0; 609 | } 610 | threshold = strtof(argv[6], NULL); 611 | scale = strtol(argv[7], NULL, 10); 612 | swaps = strtol(argv[8], NULL, 10); 613 | priority = strtol(argv[9], NULL, 10); 614 | } 615 | 616 | simple_example(d, size, hash_func, elastic, oneshot, threshold, scale, swaps, 617 | priority); 618 | 619 | return 0; 620 | } 621 | -------------------------------------------------------------------------------- /elastic_cuckoo_table.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Dimitrios Skarlatos 3 | * Contact: skarlat2@illinois.edu - http://skarlat2.web.engr.illinois.edu/ 4 | */ 5 | 6 | #ifndef ELASTIC_CUCKOO_TABLE_H 7 | #define ELASTIC_CUCKOO_TABLE_H 8 | 9 | #include "blake2-impl.h" 10 | #include "blake2.h" 11 | #include "city.h" 12 | #include 13 | #include 14 | #include 15 | 16 | #define uint64_t unsigned long long 17 | #define uint32_t unsigned long 18 | 19 | #define hsize(n) ((uint64_t)1 << (n)) 20 | #define hmask(n) (hsize(n) - 1) 21 | 22 | typedef struct elem_t { 23 | uint8_t valid; 24 | uint64_t value; 25 | } elem_t; 26 | 27 | typedef struct cuckooTable_t { 28 | elem_t **hashtable; // hashtable structure 29 | uint32_t d; // d-ary cuckoo hashtable 30 | uint64_t hash_size; // number of bits required for hashing 31 | char hash_func[20]; // hash function to be used 32 | uint64_t size; // size per d way 33 | uint64_t *num_elems; // current # elements per d way 34 | uint64_t *keys; // key per d way 35 | uint64_t *rehashed; // current rehashed entries 36 | float *util; // utilization per d way 37 | float occupancy; // overall hashtable occupancy 38 | } cuckooTable_t; 39 | 40 | typedef struct elasticCuckooTable_t { 41 | cuckooTable_t *current; // current hashtable 42 | cuckooTable_t *migrate; // migrate hashtable 43 | float rehash_threshold; // rehash treshold 44 | uint8_t rehashing; // flag if rehashing 45 | uint32_t d; // d-ary cuckoo hashtable 46 | uint64_t curr_size; // size per d hashtable of current 47 | uint32_t scale; // scaling factor 48 | uint32_t swaps; // number of swaps 49 | uint8_t priority; // priority of table during rehashing 50 | uint64_t rehash_probes; // number of rehash probes 51 | uint64_t rehash_elems; // number of rehash elements 52 | char hash_func[20]; // hash function to be used 53 | } elasticCuckooTable_t; 54 | 55 | /* 56 | * create_elastic allocates an elastic cuckoo hashtable 57 | * @d number of ways/nests 58 | * @size size of each way/nest 59 | * @hashtable the hashtable 60 | * @hash_func name of the hash function 61 | */ 62 | void create(uint32_t d, uint64_t size, cuckooTable_t *hashtable, 63 | char *hash_func); 64 | /* 65 | * create_elastic allocates an elastic cuckoo hashtable 66 | * @d number of ways/nests 67 | * @size size of each way/nest 68 | * @hashtable the hashtable 69 | * @hash_func name of the hash function 70 | * @rehash_treshold resizing threshold as a fraction 71 | * @scale scaling factor during resizing 72 | * @swaps number of swaps during rehashing 73 | * @priority bias the rehashing inserts vs random 74 | */ 75 | void create_elastic(uint32_t d, uint64_t size, elasticCuckooTable_t *hashtable, 76 | char *hash_func, float rehash_threshold, uint32_t scale, 77 | uint32_t swaps, uint8_t priority); 78 | 79 | /* 80 | * rehash rehash elements in th elastic cuckoo hashtable 81 | * @hashtable the elastic cuckoo hashtable 82 | * @swaps the number of swaps to perform 83 | * @return number of tries 84 | */ 85 | uint64_t rehash(elasticCuckooTable_t *hashtable, uint64_t swaps); 86 | 87 | /* 88 | * evaluate_elasticity evaluates the "elasticity" of the elastic cuckoo hashtable 89 | * if a threhold of occupancy is passed reszing is initiated 90 | * @hashtable elastic cuckoo hashtable to be evaluated 91 | * @complete if a resize is triggered perform a complete or gradual resize 92 | * @return number of retries if rehash was initiated 93 | */ 94 | uint64_t evaluate_elasticity(elasticCuckooTable_t *hashtable, uint8_t complete); 95 | 96 | /* 97 | * destroy de-allocate the cuckoo hashtable 98 | * @hashtable the cuckoo hashtable to de-allocate 99 | */ 100 | void destroy(cuckooTable_t *hashtable); 101 | 102 | /* 103 | * destroy_elastic de-allocate the elastic cuckoo hashtable 104 | * @hashtable the elastic cuckoo hashtable to de-allocate 105 | */ 106 | void destroy_elastic(elasticCuckooTable_t *hashtable); 107 | 108 | /* 109 | * insert try to insert an element in the cuckoo hashtable with recursion 110 | * @elem element to insert 111 | * @hashtable cuckoo hashtable to be updated 112 | * @nest nest/way to insert 113 | * @tries number of tries before aborting 114 | */ 115 | uint32_t insert_recursion(elem_t *elem, cuckooTable_t *hashtable, uint32_t nest, 116 | uint32_t tries); 117 | 118 | /* 119 | * insert try to insert an element in the cuckoo hashtable 120 | * @elem element to insert 121 | * @hashtable cuckoo hashtable to be updated 122 | */ 123 | uint32_t insert(elem_t *elem, cuckooTable_t *hashtable); 124 | 125 | /* 126 | * insert_elastic try to insert an element in the elastic cuckoo hashtable 127 | * @elem element to insert 128 | * @hashtable elasticCuckoo hashtable to be updated 129 | * @bias enable to selected the bias_nest 130 | * @bias_nest when bias is enabled select @bias_nest as the first try 131 | */ 132 | uint32_t insert_elastic(elem_t *elem, elasticCuckooTable_t *hashtable, 133 | uint8_t bias, uint16_t bias_nest); 134 | 135 | /* 136 | * find find an element in the cuckoo hashtable 137 | * @elem element to search for 138 | * @hashtable cuckoo hashtable to search in 139 | */ 140 | elem_t *find(elem_t *elem, cuckooTable_t *hashtable); 141 | 142 | /* 143 | * find_elastic find an element in the elastic cuckoo hashtable 144 | * @elem element to search for 145 | * @hashtable elasticCuckoo hashtable to search in 146 | */ 147 | elem_t *find_elastic(elem_t *elem, elasticCuckooTable_t *hashtable); 148 | 149 | /* 150 | * delete marks an element invalid from the cuckoo hashtable 151 | * @elem element to be marked invalid (if found) 152 | * @hashtable cuckoo hashtable to update 153 | */ 154 | void delete (elem_t *elem, cuckooTable_t *hashtable); 155 | 156 | /* 157 | * delete_elastic marks an element invalid from the elastic cuckoo hashtable 158 | * @elem element to be marked invalid (if found) 159 | * @hashtable elasticCuckoo hashtable to update 160 | */ 161 | void delete_elastic(elem_t *elem, elasticCuckooTable_t *hashtable); 162 | 163 | /* 164 | * update_occupancy updates the occupancy of the hashtable 165 | * @hashtable the cuckoo hashtable of which the occupancy will be updated 166 | */ 167 | void update_occupancy(cuckooTable_t *hashtable); 168 | 169 | /* 170 | * gen_hash generates a hash index 171 | * @elem used to generate the hash 172 | * @hashtable use the hash function defined in the hashtable 173 | * @nest the nest/way for which a hash is generated 174 | */ 175 | uint64_t gen_hash(elem_t *elem, cuckooTable_t *hashtable, uint32_t nest); 176 | 177 | /* 178 | * printTable prints is a helper functions that prints the hashtable 179 | * @hashtable is a cuckoo hashtable 180 | */ 181 | void printTable(cuckooTable_t *hashtable); 182 | #endif 183 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-O2 -g -Wall -Wextra -Wno-long-long -mrdrnd -m64 -lm 3 | CUCKOOBINS=elastic_cuckoo 4 | 5 | all: $(CUCKOOBINS) 6 | 7 | elastic_cuckoo: elastic_cuckoo_table.c blake2b-ref.c city.c 8 | $(CC) elastic_cuckoo_table.c blake2b-ref.c city.c -o $@ $(CFLAGS) 9 | 10 | clean: 11 | rm -rf *.o $(CUCKOOBINS) 12 | --------------------------------------------------------------------------------