blob: 3ef79c0d7d21de0040223aaf5454f5b3f07b7f1d [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Craig Tiller2e190362016-03-25 14:33:26 -07003 * Copyright 2015-2016, Google Inc.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 * * Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * * Redistributions in binary form must reproduce the above
13 * copyright notice, this list of conditions and the following disclaimer
14 * in the documentation and/or other materials provided with the
15 * distribution.
16 * * Neither the name of Google Inc. nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 *
32 */
33
34#include "src/core/statistics/hash_table.h"
35
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080036#include <stddef.h>
Craig Tillerf40df232016-03-25 13:38:14 -070037#include <stdio.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080038
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080039#include <grpc/support/alloc.h>
Craig Tillerf40df232016-03-25 13:38:14 -070040#include <grpc/support/log.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080041#include <grpc/support/port_platform.h>
42
43#define CENSUS_HT_NUM_BUCKETS 1999
44
45/* A single hash table data entry */
Craig Tillera82950e2015-09-22 12:33:20 -070046typedef struct ht_entry {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080047 census_ht_key key;
Craig Tiller45724b32015-09-22 10:42:19 -070048 void *data;
49 struct ht_entry *next;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080050} ht_entry;
51
52/* hash table bucket */
Craig Tillera82950e2015-09-22 12:33:20 -070053typedef struct bucket {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080054 /* NULL if bucket is empty */
Craig Tiller45724b32015-09-22 10:42:19 -070055 ht_entry *next;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080056 /* -1 if all buckets are empty. */
Craig Tiller7536af02015-12-22 13:49:30 -080057 int32_t prev_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080058 /* -1 if all buckets are empty. */
Craig Tiller7536af02015-12-22 13:49:30 -080059 int32_t next_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080060} bucket;
61
Craig Tillera82950e2015-09-22 12:33:20 -070062struct unresizable_hash_table {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080063 /* Number of entries in the table */
64 size_t size;
65 /* Number of buckets */
Craig Tiller7536af02015-12-22 13:49:30 -080066 uint32_t num_buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080067 /* Array of buckets initialized at creation time. Memory consumption is
68 16 bytes per bucket on a 64-bit platform. */
Craig Tiller45724b32015-09-22 10:42:19 -070069 bucket *buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080070 /* Index of the first non-empty bucket. -1 iff size == 0. */
Craig Tiller7536af02015-12-22 13:49:30 -080071 int32_t first_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080072 /* Index of the last non_empty bucket. -1 iff size == 0. */
Craig Tiller7536af02015-12-22 13:49:30 -080073 int32_t last_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080074 /* Immutable options of this hash table, initialized at creation time. */
75 census_ht_option options;
76};
77
Craig Tillera82950e2015-09-22 12:33:20 -070078typedef struct entry_locator {
Craig Tiller7536af02015-12-22 13:49:30 -080079 int32_t bucket_idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080080 int is_first_in_chain;
81 int found;
Craig Tiller45724b32015-09-22 10:42:19 -070082 ht_entry *prev_entry;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080083} entry_locator;
84
85/* Asserts if option is not valid. */
Craig Tillera82950e2015-09-22 12:33:20 -070086void check_options(const census_ht_option *option) {
87 GPR_ASSERT(option != NULL);
88 GPR_ASSERT(option->num_buckets > 0);
89 GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 ||
90 option->key_type == CENSUS_HT_POINTER);
91 if (option->key_type == CENSUS_HT_UINT64) {
92 GPR_ASSERT(option->hash == NULL);
93 } else if (option->key_type == CENSUS_HT_POINTER) {
94 GPR_ASSERT(option->hash != NULL);
95 GPR_ASSERT(option->compare_keys != NULL);
96 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080097}
98
99#define REMOVE_NEXT(options, ptr) \
100 do { \
Craig Tillera82950e2015-09-22 12:33:20 -0700101 ht_entry *tmp = (ptr)->next; \
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800102 (ptr)->next = tmp->next; \
103 delete_entry(options, tmp); \
104 } while (0)
105
Craig Tillera82950e2015-09-22 12:33:20 -0700106static void delete_entry(const census_ht_option *opt, ht_entry *p) {
107 if (opt->delete_data != NULL) {
108 opt->delete_data(p->data);
109 }
110 if (opt->delete_key != NULL) {
111 opt->delete_key(p->key.ptr);
112 }
113 gpr_free(p);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800114}
115
Craig Tiller7536af02015-12-22 13:49:30 -0800116static uint64_t hash(const census_ht_option *opt, census_ht_key key) {
Craig Tillera82950e2015-09-22 12:33:20 -0700117 return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800118}
119
Craig Tillera82950e2015-09-22 12:33:20 -0700120census_ht *census_ht_create(const census_ht_option *option) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800121 int i;
Craig Tiller45724b32015-09-22 10:42:19 -0700122 census_ht *ret = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700123 check_options(option);
124 ret = (census_ht *)gpr_malloc(sizeof(census_ht));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800125 ret->size = 0;
126 ret->num_buckets = option->num_buckets;
Craig Tillera82950e2015-09-22 12:33:20 -0700127 ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800128 ret->options = *option;
129 /* initialize each bucket */
Craig Tillera82950e2015-09-22 12:33:20 -0700130 for (i = 0; i < ret->options.num_buckets; i++) {
131 ret->buckets[i].prev_non_empty_bucket = -1;
132 ret->buckets[i].next_non_empty_bucket = -1;
133 ret->buckets[i].next = NULL;
134 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800135 return ret;
136}
137
Craig Tiller7536af02015-12-22 13:49:30 -0800138static int32_t find_bucket_idx(const census_ht *ht, census_ht_key key) {
Craig Tillera82950e2015-09-22 12:33:20 -0700139 return hash(&ht->options, key) % ht->num_buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800140}
141
Craig Tillera82950e2015-09-22 12:33:20 -0700142static int keys_match(const census_ht_option *opt, const ht_entry *p,
143 const census_ht_key key) {
144 GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 ||
145 opt->key_type == CENSUS_HT_POINTER);
146 if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val;
147 return !opt->compare_keys((p->key).ptr, key.ptr);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800148}
149
Craig Tillera82950e2015-09-22 12:33:20 -0700150static entry_locator ht_find(const census_ht *ht, census_ht_key key) {
151 entry_locator loc = {0, 0, 0, NULL};
Craig Tiller7536af02015-12-22 13:49:30 -0800152 int32_t idx = 0;
Craig Tiller45724b32015-09-22 10:42:19 -0700153 ht_entry *ptr = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700154 GPR_ASSERT(ht != NULL);
155 idx = find_bucket_idx(ht, key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800156 ptr = ht->buckets[idx].next;
Craig Tillera82950e2015-09-22 12:33:20 -0700157 if (ptr == NULL) {
158 /* bucket is empty */
159 return loc;
160 }
161 if (keys_match(&ht->options, ptr, key)) {
162 loc.bucket_idx = idx;
163 loc.is_first_in_chain = 1;
164 loc.found = 1;
165 return loc;
166 } else {
167 for (; ptr->next != NULL; ptr = ptr->next) {
168 if (keys_match(&ht->options, ptr->next, key)) {
169 loc.bucket_idx = idx;
170 loc.is_first_in_chain = 0;
171 loc.found = 1;
172 loc.prev_entry = ptr;
173 return loc;
174 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800175 }
Craig Tillera82950e2015-09-22 12:33:20 -0700176 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800177 /* Could not find the key */
178 return loc;
179}
180
Craig Tillera82950e2015-09-22 12:33:20 -0700181void *census_ht_find(const census_ht *ht, census_ht_key key) {
182 entry_locator loc = ht_find(ht, key);
183 if (loc.found == 0) {
184 return NULL;
185 }
186 return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data
187 : loc.prev_entry->next->data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800188}
189
Craig Tillera82950e2015-09-22 12:33:20 -0700190void census_ht_insert(census_ht *ht, census_ht_key key, void *data) {
Craig Tiller7536af02015-12-22 13:49:30 -0800191 int32_t idx = find_bucket_idx(ht, key);
Craig Tiller45724b32015-09-22 10:42:19 -0700192 ht_entry *ptr = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700193 entry_locator loc = ht_find(ht, key);
194 if (loc.found) {
195 /* Replace old value with new value. */
196 ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next
197 : loc.prev_entry->next;
198 if (ht->options.delete_data != NULL) {
199 ht->options.delete_data(ptr->data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800200 }
Craig Tillera82950e2015-09-22 12:33:20 -0700201 ptr->data = data;
202 return;
203 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800204
205 /* first entry in the table. */
Craig Tillera82950e2015-09-22 12:33:20 -0700206 if (ht->size == 0) {
207 ht->buckets[idx].next_non_empty_bucket = -1;
208 ht->buckets[idx].prev_non_empty_bucket = -1;
209 ht->first_non_empty_bucket = idx;
210 ht->last_non_empty_bucket = idx;
211 } else if (ht->buckets[idx].next == NULL) {
212 /* first entry in the bucket. */
213 ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx;
214 ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket;
215 ht->buckets[idx].next_non_empty_bucket = -1;
216 ht->last_non_empty_bucket = idx;
217 }
218 ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800219 ptr->key = key;
220 ptr->data = data;
221 ptr->next = ht->buckets[idx].next;
222 ht->buckets[idx].next = ptr;
223 ht->size++;
224}
225
Craig Tillera82950e2015-09-22 12:33:20 -0700226void census_ht_erase(census_ht *ht, census_ht_key key) {
227 entry_locator loc = ht_find(ht, key);
228 if (loc.found == 0) {
229 /* noop if not found */
230 return;
231 }
Craig Tiller45724b32015-09-22 10:42:19 -0700232 ht->size--;
Craig Tillera82950e2015-09-22 12:33:20 -0700233 if (loc.is_first_in_chain) {
234 bucket *b = &ht->buckets[loc.bucket_idx];
235 GPR_ASSERT(b->next != NULL);
236 /* The only entry in the bucket */
237 if (b->next->next == NULL) {
238 int prev = b->prev_non_empty_bucket;
239 int next = b->next_non_empty_bucket;
240 if (prev != -1) {
241 ht->buckets[prev].next_non_empty_bucket = next;
242 } else {
243 ht->first_non_empty_bucket = next;
244 }
245 if (next != -1) {
246 ht->buckets[next].prev_non_empty_bucket = prev;
247 } else {
248 ht->last_non_empty_bucket = prev;
249 }
Craig Tiller45724b32015-09-22 10:42:19 -0700250 }
Craig Tillera82950e2015-09-22 12:33:20 -0700251 REMOVE_NEXT(&ht->options, b);
252 } else {
253 GPR_ASSERT(loc.prev_entry->next != NULL);
254 REMOVE_NEXT(&ht->options, loc.prev_entry);
255 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800256}
257
258/* Returns NULL if input table is empty. */
Craig Tillera82950e2015-09-22 12:33:20 -0700259census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) {
Craig Tiller45724b32015-09-22 10:42:19 -0700260 census_ht_kv *ret = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800261 int i = 0;
Craig Tiller7536af02015-12-22 13:49:30 -0800262 int32_t idx = -1;
Craig Tillera82950e2015-09-22 12:33:20 -0700263 GPR_ASSERT(ht != NULL && num != NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800264 *num = ht->size;
Craig Tillera82950e2015-09-22 12:33:20 -0700265 if (*num == 0) {
266 return NULL;
267 }
Craig Tiller45724b32015-09-22 10:42:19 -0700268
Craig Tillera82950e2015-09-22 12:33:20 -0700269 ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size);
Craig Tiller45724b32015-09-22 10:42:19 -0700270 idx = ht->first_non_empty_bucket;
Craig Tillera82950e2015-09-22 12:33:20 -0700271 while (idx >= 0) {
272 ht_entry *ptr = ht->buckets[idx].next;
273 for (; ptr != NULL; ptr = ptr->next) {
274 ret[i].k = ptr->key;
275 ret[i].v = ptr->data;
276 i++;
Craig Tiller45724b32015-09-22 10:42:19 -0700277 }
Craig Tillera82950e2015-09-22 12:33:20 -0700278 idx = ht->buckets[idx].next_non_empty_bucket;
279 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800280 return ret;
281}
282
Craig Tillera82950e2015-09-22 12:33:20 -0700283static void ht_delete_entry_chain(const census_ht_option *options,
284 ht_entry *first) {
285 if (first == NULL) {
286 return;
287 }
288 if (first->next != NULL) {
289 ht_delete_entry_chain(options, first->next);
290 }
291 delete_entry(options, first);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800292}
293
Craig Tillera82950e2015-09-22 12:33:20 -0700294void census_ht_destroy(census_ht *ht) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100295 unsigned i;
Craig Tillera82950e2015-09-22 12:33:20 -0700296 for (i = 0; i < ht->num_buckets; ++i) {
297 ht_delete_entry_chain(&ht->options, ht->buckets[i].next);
298 }
299 gpr_free(ht->buckets);
300 gpr_free(ht);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800301}
302
Craig Tillera82950e2015-09-22 12:33:20 -0700303size_t census_ht_get_size(const census_ht *ht) { return ht->size; }