blob: 545b0857c7600cb30deab2e7e1e0863784227b99 [file] [log] [blame]
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08001/*
2 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02003 * Copyright 2015 gRPC authors.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08004 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02005 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -08008 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +02009 * http://www.apache.org/licenses/LICENSE-2.0
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080010 *
Jan Tattermusch7897ae92017-06-07 22:57:36 +020011 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080016 *
17 */
18
Craig Tiller8f8e9f92016-03-29 09:41:28 -070019#include "src/core/ext/census/hash_table.h"
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080020
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080021#include <stddef.h>
Craig Tillerf40df232016-03-25 13:38:14 -070022#include <stdio.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080023
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080024#include <grpc/support/alloc.h>
Craig Tillerf40df232016-03-25 13:38:14 -070025#include <grpc/support/log.h>
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080026#include <grpc/support/port_platform.h>
27
28#define CENSUS_HT_NUM_BUCKETS 1999
29
30/* A single hash table data entry */
Craig Tillera82950e2015-09-22 12:33:20 -070031typedef struct ht_entry {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080032 census_ht_key key;
Craig Tiller45724b32015-09-22 10:42:19 -070033 void *data;
34 struct ht_entry *next;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080035} ht_entry;
36
37/* hash table bucket */
Craig Tillera82950e2015-09-22 12:33:20 -070038typedef struct bucket {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080039 /* NULL if bucket is empty */
Craig Tiller45724b32015-09-22 10:42:19 -070040 ht_entry *next;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080041 /* -1 if all buckets are empty. */
Craig Tiller7536af02015-12-22 13:49:30 -080042 int32_t prev_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080043 /* -1 if all buckets are empty. */
Craig Tiller7536af02015-12-22 13:49:30 -080044 int32_t next_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080045} bucket;
46
Craig Tillera82950e2015-09-22 12:33:20 -070047struct unresizable_hash_table {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080048 /* Number of entries in the table */
49 size_t size;
50 /* Number of buckets */
Craig Tiller7536af02015-12-22 13:49:30 -080051 uint32_t num_buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080052 /* Array of buckets initialized at creation time. Memory consumption is
53 16 bytes per bucket on a 64-bit platform. */
Craig Tiller45724b32015-09-22 10:42:19 -070054 bucket *buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080055 /* Index of the first non-empty bucket. -1 iff size == 0. */
Craig Tiller7536af02015-12-22 13:49:30 -080056 int32_t first_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080057 /* Index of the last non_empty bucket. -1 iff size == 0. */
Craig Tiller7536af02015-12-22 13:49:30 -080058 int32_t last_non_empty_bucket;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080059 /* Immutable options of this hash table, initialized at creation time. */
60 census_ht_option options;
61};
62
Craig Tillera82950e2015-09-22 12:33:20 -070063typedef struct entry_locator {
Craig Tiller7536af02015-12-22 13:49:30 -080064 int32_t bucket_idx;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080065 int is_first_in_chain;
66 int found;
Craig Tiller45724b32015-09-22 10:42:19 -070067 ht_entry *prev_entry;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080068} entry_locator;
69
70/* Asserts if option is not valid. */
Craig Tillera82950e2015-09-22 12:33:20 -070071void check_options(const census_ht_option *option) {
72 GPR_ASSERT(option != NULL);
73 GPR_ASSERT(option->num_buckets > 0);
74 GPR_ASSERT(option->key_type == CENSUS_HT_UINT64 ||
75 option->key_type == CENSUS_HT_POINTER);
76 if (option->key_type == CENSUS_HT_UINT64) {
77 GPR_ASSERT(option->hash == NULL);
78 } else if (option->key_type == CENSUS_HT_POINTER) {
79 GPR_ASSERT(option->hash != NULL);
80 GPR_ASSERT(option->compare_keys != NULL);
81 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080082}
83
84#define REMOVE_NEXT(options, ptr) \
85 do { \
Craig Tillera82950e2015-09-22 12:33:20 -070086 ht_entry *tmp = (ptr)->next; \
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080087 (ptr)->next = tmp->next; \
88 delete_entry(options, tmp); \
89 } while (0)
90
Craig Tillera82950e2015-09-22 12:33:20 -070091static void delete_entry(const census_ht_option *opt, ht_entry *p) {
92 if (opt->delete_data != NULL) {
93 opt->delete_data(p->data);
94 }
95 if (opt->delete_key != NULL) {
96 opt->delete_key(p->key.ptr);
97 }
98 gpr_free(p);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -080099}
100
Craig Tiller7536af02015-12-22 13:49:30 -0800101static uint64_t hash(const census_ht_option *opt, census_ht_key key) {
Craig Tillera82950e2015-09-22 12:33:20 -0700102 return opt->key_type == CENSUS_HT_UINT64 ? key.val : opt->hash(key.ptr);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800103}
104
Craig Tillera82950e2015-09-22 12:33:20 -0700105census_ht *census_ht_create(const census_ht_option *option) {
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800106 int i;
Craig Tiller45724b32015-09-22 10:42:19 -0700107 census_ht *ret = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700108 check_options(option);
109 ret = (census_ht *)gpr_malloc(sizeof(census_ht));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800110 ret->size = 0;
111 ret->num_buckets = option->num_buckets;
Craig Tillera82950e2015-09-22 12:33:20 -0700112 ret->buckets = (bucket *)gpr_malloc(sizeof(bucket) * ret->num_buckets);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800113 ret->options = *option;
114 /* initialize each bucket */
Craig Tillera82950e2015-09-22 12:33:20 -0700115 for (i = 0; i < ret->options.num_buckets; i++) {
116 ret->buckets[i].prev_non_empty_bucket = -1;
117 ret->buckets[i].next_non_empty_bucket = -1;
118 ret->buckets[i].next = NULL;
119 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800120 return ret;
121}
122
Craig Tiller7536af02015-12-22 13:49:30 -0800123static int32_t find_bucket_idx(const census_ht *ht, census_ht_key key) {
Craig Tillera82950e2015-09-22 12:33:20 -0700124 return hash(&ht->options, key) % ht->num_buckets;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800125}
126
Craig Tillera82950e2015-09-22 12:33:20 -0700127static int keys_match(const census_ht_option *opt, const ht_entry *p,
128 const census_ht_key key) {
129 GPR_ASSERT(opt->key_type == CENSUS_HT_UINT64 ||
130 opt->key_type == CENSUS_HT_POINTER);
131 if (opt->key_type == CENSUS_HT_UINT64) return p->key.val == key.val;
132 return !opt->compare_keys((p->key).ptr, key.ptr);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800133}
134
Craig Tillera82950e2015-09-22 12:33:20 -0700135static entry_locator ht_find(const census_ht *ht, census_ht_key key) {
136 entry_locator loc = {0, 0, 0, NULL};
Craig Tiller7536af02015-12-22 13:49:30 -0800137 int32_t idx = 0;
Craig Tiller45724b32015-09-22 10:42:19 -0700138 ht_entry *ptr = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700139 GPR_ASSERT(ht != NULL);
140 idx = find_bucket_idx(ht, key);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800141 ptr = ht->buckets[idx].next;
Craig Tillera82950e2015-09-22 12:33:20 -0700142 if (ptr == NULL) {
143 /* bucket is empty */
144 return loc;
145 }
146 if (keys_match(&ht->options, ptr, key)) {
147 loc.bucket_idx = idx;
148 loc.is_first_in_chain = 1;
149 loc.found = 1;
150 return loc;
151 } else {
152 for (; ptr->next != NULL; ptr = ptr->next) {
153 if (keys_match(&ht->options, ptr->next, key)) {
154 loc.bucket_idx = idx;
155 loc.is_first_in_chain = 0;
156 loc.found = 1;
157 loc.prev_entry = ptr;
158 return loc;
159 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800160 }
Craig Tillera82950e2015-09-22 12:33:20 -0700161 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800162 /* Could not find the key */
163 return loc;
164}
165
Craig Tillera82950e2015-09-22 12:33:20 -0700166void *census_ht_find(const census_ht *ht, census_ht_key key) {
167 entry_locator loc = ht_find(ht, key);
168 if (loc.found == 0) {
169 return NULL;
170 }
171 return loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next->data
172 : loc.prev_entry->next->data;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800173}
174
Craig Tillera82950e2015-09-22 12:33:20 -0700175void census_ht_insert(census_ht *ht, census_ht_key key, void *data) {
Craig Tiller7536af02015-12-22 13:49:30 -0800176 int32_t idx = find_bucket_idx(ht, key);
Craig Tiller45724b32015-09-22 10:42:19 -0700177 ht_entry *ptr = NULL;
Craig Tillera82950e2015-09-22 12:33:20 -0700178 entry_locator loc = ht_find(ht, key);
179 if (loc.found) {
180 /* Replace old value with new value. */
181 ptr = loc.is_first_in_chain ? ht->buckets[loc.bucket_idx].next
182 : loc.prev_entry->next;
183 if (ht->options.delete_data != NULL) {
184 ht->options.delete_data(ptr->data);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800185 }
Craig Tillera82950e2015-09-22 12:33:20 -0700186 ptr->data = data;
187 return;
188 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800189
190 /* first entry in the table. */
Craig Tillera82950e2015-09-22 12:33:20 -0700191 if (ht->size == 0) {
192 ht->buckets[idx].next_non_empty_bucket = -1;
193 ht->buckets[idx].prev_non_empty_bucket = -1;
194 ht->first_non_empty_bucket = idx;
195 ht->last_non_empty_bucket = idx;
196 } else if (ht->buckets[idx].next == NULL) {
197 /* first entry in the bucket. */
198 ht->buckets[ht->last_non_empty_bucket].next_non_empty_bucket = idx;
199 ht->buckets[idx].prev_non_empty_bucket = ht->last_non_empty_bucket;
200 ht->buckets[idx].next_non_empty_bucket = -1;
201 ht->last_non_empty_bucket = idx;
202 }
203 ptr = (ht_entry *)gpr_malloc(sizeof(ht_entry));
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800204 ptr->key = key;
205 ptr->data = data;
206 ptr->next = ht->buckets[idx].next;
207 ht->buckets[idx].next = ptr;
208 ht->size++;
209}
210
Craig Tillera82950e2015-09-22 12:33:20 -0700211void census_ht_erase(census_ht *ht, census_ht_key key) {
212 entry_locator loc = ht_find(ht, key);
213 if (loc.found == 0) {
214 /* noop if not found */
215 return;
216 }
Craig Tiller45724b32015-09-22 10:42:19 -0700217 ht->size--;
Craig Tillera82950e2015-09-22 12:33:20 -0700218 if (loc.is_first_in_chain) {
219 bucket *b = &ht->buckets[loc.bucket_idx];
220 GPR_ASSERT(b->next != NULL);
221 /* The only entry in the bucket */
222 if (b->next->next == NULL) {
223 int prev = b->prev_non_empty_bucket;
224 int next = b->next_non_empty_bucket;
225 if (prev != -1) {
226 ht->buckets[prev].next_non_empty_bucket = next;
227 } else {
228 ht->first_non_empty_bucket = next;
229 }
230 if (next != -1) {
231 ht->buckets[next].prev_non_empty_bucket = prev;
232 } else {
233 ht->last_non_empty_bucket = prev;
234 }
Craig Tiller45724b32015-09-22 10:42:19 -0700235 }
Craig Tillera82950e2015-09-22 12:33:20 -0700236 REMOVE_NEXT(&ht->options, b);
237 } else {
238 GPR_ASSERT(loc.prev_entry->next != NULL);
239 REMOVE_NEXT(&ht->options, loc.prev_entry);
240 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800241}
242
243/* Returns NULL if input table is empty. */
Craig Tillera82950e2015-09-22 12:33:20 -0700244census_ht_kv *census_ht_get_all_elements(const census_ht *ht, size_t *num) {
Craig Tiller45724b32015-09-22 10:42:19 -0700245 census_ht_kv *ret = NULL;
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800246 int i = 0;
Craig Tiller7536af02015-12-22 13:49:30 -0800247 int32_t idx = -1;
Craig Tillera82950e2015-09-22 12:33:20 -0700248 GPR_ASSERT(ht != NULL && num != NULL);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800249 *num = ht->size;
Craig Tillera82950e2015-09-22 12:33:20 -0700250 if (*num == 0) {
251 return NULL;
252 }
Craig Tiller45724b32015-09-22 10:42:19 -0700253
Craig Tillera82950e2015-09-22 12:33:20 -0700254 ret = (census_ht_kv *)gpr_malloc(sizeof(census_ht_kv) * ht->size);
Craig Tiller45724b32015-09-22 10:42:19 -0700255 idx = ht->first_non_empty_bucket;
Craig Tillera82950e2015-09-22 12:33:20 -0700256 while (idx >= 0) {
257 ht_entry *ptr = ht->buckets[idx].next;
258 for (; ptr != NULL; ptr = ptr->next) {
259 ret[i].k = ptr->key;
260 ret[i].v = ptr->data;
261 i++;
Craig Tiller45724b32015-09-22 10:42:19 -0700262 }
Craig Tillera82950e2015-09-22 12:33:20 -0700263 idx = ht->buckets[idx].next_non_empty_bucket;
264 }
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800265 return ret;
266}
267
Craig Tillera82950e2015-09-22 12:33:20 -0700268static void ht_delete_entry_chain(const census_ht_option *options,
269 ht_entry *first) {
270 if (first == NULL) {
271 return;
272 }
273 if (first->next != NULL) {
274 ht_delete_entry_chain(options, first->next);
275 }
276 delete_entry(options, first);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800277}
278
Craig Tillera82950e2015-09-22 12:33:20 -0700279void census_ht_destroy(census_ht *ht) {
Nicolas "Pixel" Noble213ed912015-01-30 02:11:35 +0100280 unsigned i;
Craig Tillera82950e2015-09-22 12:33:20 -0700281 for (i = 0; i < ht->num_buckets; ++i) {
282 ht_delete_entry_chain(&ht->options, ht->buckets[i].next);
283 }
284 gpr_free(ht->buckets);
285 gpr_free(ht);
Nicolas Nobleb7ebd3b2014-11-26 16:33:03 -0800286}
287
Craig Tillera82950e2015-09-22 12:33:20 -0700288size_t census_ht_get_size(const census_ht *ht) { return ht->size; }