blob: ef3fe4d1658cc7149e91792afe3a19d9f19a6b8c [file] [log] [blame]
Radek Krejciee627582015-04-20 17:39:59 +02001/**
Michal Vasko6c810702018-03-14 16:23:21 +01002 * @file hash_table.c
Radek Krejciee627582015-04-20 17:39:59 +02003 * @author Radek Krejci <rkrejci@cesnet.cz>
Michal Vasko6c810702018-03-14 16:23:21 +01004 * @brief libyang dictionary for storing strings and generic hash table
Radek Krejciee627582015-04-20 17:39:59 +02005 *
Michal Vasko6c810702018-03-14 16:23:21 +01006 * Copyright (c) 2015 - 2018 CESNET, z.s.p.o.
Radek Krejciee627582015-04-20 17:39:59 +02007 *
Radek Krejci54f6fb32016-02-24 12:56:39 +01008 * This source code is licensed under BSD 3-Clause License (the "License").
9 * You may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
Michal Vasko8de098c2016-02-26 10:00:25 +010011 *
Radek Krejci54f6fb32016-02-24 12:56:39 +010012 * https://opensource.org/licenses/BSD-3-Clause
Radek Krejciee627582015-04-20 17:39:59 +020013 */
14
15#include <string.h>
16#include <stdint.h>
17#include <stdlib.h>
Radek Krejcifebcbfb2016-02-04 14:58:42 +010018#include <pthread.h>
Michal Vasko6c810702018-03-14 16:23:21 +010019#include <assert.h>
Radek Krejciee627582015-04-20 17:39:59 +020020
Radek Krejciee627582015-04-20 17:39:59 +020021#include "common.h"
Radek Krejcida04f4a2015-05-21 12:54:09 +020022#include "context.h"
Michal Vasko6c810702018-03-14 16:23:21 +010023#include "hash_table.h"
Radek Krejciee627582015-04-20 17:39:59 +020024
David Sedlákc6ecb892018-08-05 23:16:50 +020025static int
David Sedlák2aabdea2018-08-08 10:10:36 +020026lydict_val_eq(void *val1_p, void *val2_p, int UNUSED(mod), void *cb_data)
David Sedlákc6ecb892018-08-05 23:16:50 +020027{
David Sedlákcaac88b2018-08-09 11:15:26 +020028 if (!val1_p || !val2_p) {
29 LOGARG;
30 return 0;
31 }
32
David Sedlákc6ecb892018-08-05 23:16:50 +020033 const char *str1 = ((struct dict_rec *)val1_p)->value;
34 const char *str2 = ((struct dict_rec *)val2_p)->value;
35
David Sedlák2aabdea2018-08-08 10:10:36 +020036 if (!str1 || !str2 || !cb_data) {
David Sedlák3468d3c2018-08-08 13:38:36 +020037 LOGARG;
David Sedlákc6ecb892018-08-05 23:16:50 +020038 return 0;
39 }
40
David Sedlák2aabdea2018-08-08 10:10:36 +020041 if (strncmp(str1, str2, *(size_t *)cb_data) == 0) {
David Sedlákc6ecb892018-08-05 23:16:50 +020042 return 1;
43 }
44
45 return 0;
46}
47
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020048void
49lydict_init(struct dict_table *dict)
Radek Krejcida04f4a2015-05-21 12:54:09 +020050{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020051 if (!dict) {
Michal Vasko53b7da02018-02-13 15:28:42 +010052 LOGARG;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020053 return;
54 }
Radek Krejciee627582015-04-20 17:39:59 +020055
David Sedlákfb863342018-08-13 09:43:17 +020056 dict->hash_tab = lyht_new(1024, sizeof(struct dict_rec), lydict_val_eq, NULL, 1);
David Sedlák3468d3c2018-08-08 13:38:36 +020057 LY_CHECK_ERR_RETURN(!dict->hash_tab, LOGINT(NULL), );
Radek Krejcifebcbfb2016-02-04 14:58:42 +010058 pthread_mutex_init(&dict->lock, NULL);
Radek Krejcida04f4a2015-05-21 12:54:09 +020059}
60
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020061void
62lydict_clean(struct dict_table *dict)
Radek Krejcida04f4a2015-05-21 12:54:09 +020063{
David Sedlákc6ecb892018-08-05 23:16:50 +020064 unsigned int i;
Radek Krejcida04f4a2015-05-21 12:54:09 +020065
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020066 if (!dict) {
Michal Vasko53b7da02018-02-13 15:28:42 +010067 LOGARG;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020068 return;
69 }
Radek Krejcida04f4a2015-05-21 12:54:09 +020070
David Sedlákc6ecb892018-08-05 23:16:50 +020071 for (i = 0; i < dict->hash_tab->size; i++)
72 {
David Sedlák1c151f22018-08-08 11:01:22 +020073 /* get ith record */
74 struct ht_rec *rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
75 if (rec->hits == 1) {
David Sedlákee0ffc72018-08-08 14:08:56 +020076 /*
77 * this should not happen, all records inserted into
David Sedlák1c151f22018-08-08 11:01:22 +020078 * dictionary are supposed to be removed using lydict_remove()
79 * before calling lydict_clean()
80 */
David Sedlák3468d3c2018-08-08 13:38:36 +020081 assert(!(rec->hits == 1));
David Sedlákfd4235c2018-08-09 10:09:18 +020082 /* if record wasn't removed before free string allocated for that record */
83 free(((struct dict_rec *)rec->val)->value);
David Sedlák1c151f22018-08-08 11:01:22 +020084 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020085 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +010086
David Sedlákee0ffc72018-08-08 14:08:56 +020087 /* free table and destroy mutex */
David Sedlákc6ecb892018-08-05 23:16:50 +020088 lyht_free(dict->hash_tab);
Radek Krejcifebcbfb2016-02-04 14:58:42 +010089 pthread_mutex_destroy(&dict->lock);
Radek Krejcida04f4a2015-05-21 12:54:09 +020090}
Radek Krejciee627582015-04-20 17:39:59 +020091
92/*
93 * Bob Jenkin's one-at-a-time hash
94 * http://www.burtleburtle.net/bob/hash/doobs.html
95 *
96 * Spooky hash is faster, but it works only for little endian architectures.
97 */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020098static uint32_t
99dict_hash(const char *key, size_t len)
Radek Krejciee627582015-04-20 17:39:59 +0200100{
101 uint32_t hash, i;
102
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200103 for (hash = i = 0; i < len; ++i) {
Radek Krejciee627582015-04-20 17:39:59 +0200104 hash += key[i];
105 hash += (hash << 10);
106 hash ^= (hash >> 6);
107 }
108 hash += (hash << 3);
109 hash ^= (hash >> 11);
110 hash += (hash << 15);
111 return hash;
112}
113
Radek Krejci63b79c82016-08-10 10:09:33 +0200114/*
115 * Usage:
116 * - init hash to 0
117 * - repeatedly call dict_hash_multi(), provide hash from the last call
118 * - call dict_hash_multi() with key_part = NULL to finish the hash
119 */
120uint32_t
121dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
122{
123 uint32_t i;
124
125 if (key_part) {
126 for (i = 0; i < len; ++i) {
127 hash += key_part[i];
128 hash += (hash << 10);
129 hash ^= (hash >> 6);
130 }
131 } else {
132 hash += (hash << 3);
133 hash ^= (hash >> 11);
134 hash += (hash << 15);
135 }
136
137 return hash;
138}
139
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200140API void
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200141lydict_remove(struct ly_ctx *ctx, const char *value)
Radek Krejciee627582015-04-20 17:39:59 +0200142{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200143 size_t len;
David Sedlákc6ecb892018-08-05 23:16:50 +0200144 int ret;
145 uint32_t hash;
146 struct dict_rec rec, *match = NULL;
David Sedlák2aabdea2018-08-08 10:10:36 +0200147 char *val_p;
Radek Krejciee627582015-04-20 17:39:59 +0200148
Michal Vasko5c328e72016-10-13 12:17:37 +0200149 if (!value || !ctx) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200150 return;
151 }
Radek Krejciee627582015-04-20 17:39:59 +0200152
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200153 len = strlen(value);
David Sedlákc6ecb892018-08-05 23:16:50 +0200154 hash = dict_hash(value, len);
David Sedlák2aabdea2018-08-08 10:10:36 +0200155
David Sedlák0f860492018-08-07 16:24:24 +0200156 /* create record for lyht_find call */
David Sedlák2aabdea2018-08-08 10:10:36 +0200157 rec.value = (char *)value;
David Sedlákc6ecb892018-08-05 23:16:50 +0200158 rec.refcount = 0;
Radek Krejciee627582015-04-20 17:39:59 +0200159
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100160 pthread_mutex_lock(&ctx->dict.lock);
David Sedlák3468d3c2018-08-08 13:38:36 +0200161 /* set len as data for compare callback */
David Sedlák9af9ed52018-08-08 12:54:20 +0200162 lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
David Sedlákee0ffc72018-08-08 14:08:56 +0200163 /* check if value is already inserted */
David Sedlákc6ecb892018-08-05 23:16:50 +0200164 ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100165
David Sedlák3468d3c2018-08-08 13:38:36 +0200166 if (ret == 0) {
167 if (!match) {
168 LOGINT(ctx);
169 pthread_mutex_unlock(&ctx->dict.lock);
170 return;
171 }
David Sedlákb6ea11c2018-08-06 15:53:42 +0200172 /* if value is already in dictionary, decrement reference counter */
David Sedlákcaac88b2018-08-09 11:15:26 +0200173 match->refcount--;
David Sedlákb6ea11c2018-08-06 15:53:42 +0200174 if (match->refcount == 0) {
David Sedlákee0ffc72018-08-08 14:08:56 +0200175 /*
176 * remove record
David Sedlák2aabdea2018-08-08 10:10:36 +0200177 * save pointer to stored string before lyht_remove to
David Sedlák3468d3c2018-08-08 13:38:36 +0200178 * free it after it is removed from hash table
179 */
David Sedlák2aabdea2018-08-08 10:10:36 +0200180 val_p = match->value;
181 ret = lyht_remove(ctx->dict.hash_tab, &rec, hash);
182 free(val_p);
David Sedlák3468d3c2018-08-08 13:38:36 +0200183 LY_CHECK_ERR_RETURN(ret != 0, LOGINT(ctx), );
David Sedlákc6ecb892018-08-05 23:16:50 +0200184 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200185 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100186 pthread_mutex_unlock(&ctx->dict.lock);
Radek Krejciee627582015-04-20 17:39:59 +0200187}
188
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200189static char *
190dict_insert(struct ly_ctx *ctx, char *value, size_t len, int zerocopy)
Radek Krejciee627582015-04-20 17:39:59 +0200191{
David Sedlákc6ecb892018-08-05 23:16:50 +0200192 struct dict_rec *match = NULL, rec;
193 int ret = 0;
194 uint32_t hash;
Radek Krejciee627582015-04-20 17:39:59 +0200195
David Sedlákc6ecb892018-08-05 23:16:50 +0200196 hash = dict_hash(value, len);
David Sedlák3468d3c2018-08-08 13:38:36 +0200197 /* set len as data for compare callback */
David Sedlák2aabdea2018-08-08 10:10:36 +0200198 lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
David Sedlákcaac88b2018-08-09 11:15:26 +0200199 /* create record for lyht_insert */
David Sedlák2aabdea2018-08-08 10:10:36 +0200200 rec.value = value;
David Sedlákc6ecb892018-08-05 23:16:50 +0200201 rec.refcount = 1;
202
David Sedlákcaac88b2018-08-09 11:15:26 +0200203 LOGDBG(LY_LDGDICT, "inserting \"%s\"", rec.value);
204 ret = lyht_insert(ctx->dict.hash_tab, (void *)&rec, hash, (void **)&match);
205 if (ret == 1) {
206 match->refcount++;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200207 if (zerocopy) {
David Sedlákc6ecb892018-08-05 23:16:50 +0200208 free(value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200209 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200210 } else if (ret == 0) {
David Sedlák2aabdea2018-08-08 10:10:36 +0200211 if (!zerocopy) {
David Sedlákcaac88b2018-08-09 11:15:26 +0200212 /*
213 * allocate string for new record
214 * record is already inserted in hash table
215 */
216 match->value = malloc(sizeof *match->value * (len + 1));
217 LY_CHECK_ERR_RETURN(!match->value, LOGMEM(ctx), NULL);
218 memcpy(match->value, value, len);
219 match->value[len] = '\0';
Radek Krejci592e00d2016-05-19 12:38:08 +0200220 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200221 } else {
222 /* lyht_insert returned error */
223 LOGINT(ctx);
224 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200225 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200226
227 return match->value;
Radek Krejciee627582015-04-20 17:39:59 +0200228}
Radek Krejci4ea08382015-04-21 09:41:40 +0200229
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200230API const char *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200231lydict_insert(struct ly_ctx *ctx, const char *value, size_t len)
Radek Krejci4ea08382015-04-21 09:41:40 +0200232{
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100233 const char *result;
234
Radek Krejcia5269642015-07-20 19:04:11 +0200235 if (!value) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200236 return NULL;
237 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100238
David Sedlák885c78a2018-08-08 14:11:06 +0200239 if (!len) {
240 len = strlen(value);
241 }
242
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100243 pthread_mutex_lock(&ctx->dict.lock);
244 result = dict_insert(ctx, (char *)value, len, 0);
245 pthread_mutex_unlock(&ctx->dict.lock);
246
247 return result;
Radek Krejci4ea08382015-04-21 09:41:40 +0200248}
249
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200250API const char *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200251lydict_insert_zc(struct ly_ctx *ctx, char *value)
Radek Krejci4ea08382015-04-21 09:41:40 +0200252{
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100253 const char *result;
254
Radek Krejcia5269642015-07-20 19:04:11 +0200255 if (!value) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200256 return NULL;
257 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100258
259 pthread_mutex_lock(&ctx->dict.lock);
260 result = dict_insert(ctx, value, strlen(value), 1);
261 pthread_mutex_unlock(&ctx->dict.lock);
262
263 return result;
Radek Krejci4ea08382015-04-21 09:41:40 +0200264}
Michal Vasko6c810702018-03-14 16:23:21 +0100265
Michal Vasko419fce02018-03-21 11:55:09 +0100266static struct ht_rec *
267lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
268{
269 return (struct ht_rec *)&recs[idx * rec_size];
270}
271
Michal Vasko6c810702018-03-14 16:23:21 +0100272struct hash_table *
Michal Vasko419fce02018-03-21 11:55:09 +0100273lyht_new(uint32_t size, uint16_t val_size, values_equal_cb val_equal, void *cb_data, int resize)
Michal Vasko6c810702018-03-14 16:23:21 +0100274{
275 struct hash_table *ht;
276
277 /* check that 2^x == size (power of 2) */
Michal Vaskod0f84c32018-03-16 12:13:15 +0100278 assert(size && !(size & (size - 1)));
Michal Vasko419fce02018-03-21 11:55:09 +0100279 assert(val_equal && val_size);
Michal Vasko04210d72018-03-16 12:07:47 +0100280 assert(resize == 0 || resize == 1);
Michal Vasko6c810702018-03-14 16:23:21 +0100281
Michal Vaskod0f84c32018-03-16 12:13:15 +0100282 if (size < LYHT_MIN_SIZE) {
283 size = LYHT_MIN_SIZE;
284 }
285
Michal Vasko6c810702018-03-14 16:23:21 +0100286 ht = malloc(sizeof *ht);
287 LY_CHECK_ERR_RETURN(!ht, LOGMEM(NULL), NULL);
288
Michal Vasko6c810702018-03-14 16:23:21 +0100289 ht->used = 0;
290 ht->size = size;
291 ht->val_equal = val_equal;
292 ht->cb_data = cb_data;
Michal Vasko419fce02018-03-21 11:55:09 +0100293 ht->resize = (uint16_t)resize;
294
295 ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
296 /* allocate the records correctly */
297 ht->recs = calloc(size, ht->rec_size);
298 LY_CHECK_ERR_RETURN(!ht->recs, free(ht); LOGMEM(NULL), NULL);
299
Michal Vasko6c810702018-03-14 16:23:21 +0100300 return ht;
301}
302
Michal Vasko8ac94532018-05-18 09:38:13 +0200303values_equal_cb
304lyht_set_cb(struct hash_table *ht, values_equal_cb new_val_equal)
305{
306 values_equal_cb prev;
307
308 prev = ht->val_equal;
309 ht->val_equal = new_val_equal;
310 return prev;
311}
312
313void *
314lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
315{
316 void *prev;
317
318 prev = ht->cb_data;
319 ht->cb_data = new_cb_data;
320 return prev;
321}
322
Michal Vasko8690d122018-04-26 14:28:58 +0200323struct hash_table *
324lyht_dup(const struct hash_table *orig)
325{
326 struct hash_table *ht;
327
328 if (!orig) {
329 return NULL;
330 }
331
332 ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
333 if (!ht) {
334 return NULL;
335 }
336
337 memcpy(ht->recs, orig->recs, orig->used * orig->rec_size);
338 ht->used = orig->used;
339 return ht;
340}
341
Michal Vasko6c810702018-03-14 16:23:21 +0100342void
343lyht_free(struct hash_table *ht)
344{
345 if (ht) {
346 free(ht->recs);
347 free(ht);
348 }
349}
350
Michal Vasko04210d72018-03-16 12:07:47 +0100351static int
352lyht_resize(struct hash_table *ht, int enlarge)
353{
Michal Vasko419fce02018-03-21 11:55:09 +0100354 struct ht_rec *rec;
355 unsigned char *old_recs;
Michal Vasko24affa02018-04-03 09:06:06 +0200356 uint32_t i, old_size;
Michal Vasko5a28e0e2018-08-07 11:29:06 +0200357 int ret;
Michal Vasko04210d72018-03-16 12:07:47 +0100358
359 old_recs = ht->recs;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200360 old_size = ht->size;
Michal Vasko04210d72018-03-16 12:07:47 +0100361
362 if (enlarge) {
363 /* double the size */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200364 ht->size <<= 1;
Michal Vasko04210d72018-03-16 12:07:47 +0100365 } else {
366 /* half the size */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200367 ht->size >>= 1;
Michal Vasko04210d72018-03-16 12:07:47 +0100368 }
369
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200370 ht->recs = calloc(ht->size, ht->rec_size);
371 LY_CHECK_ERR_RETURN(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, -1);
Michal Vasko04210d72018-03-16 12:07:47 +0100372
373 /* reset used, it will increase again */
374 ht->used = 0;
375
376 /* add all the old records into the new records array */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200377 for (i = 0; i < old_size; ++i) {
Michal Vasko419fce02018-03-21 11:55:09 +0100378 rec = lyht_get_rec(old_recs, ht->rec_size, i);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200379 if (rec->hits > 0) {
David Sedláka3969962018-08-13 10:28:32 +0200380 ret = lyht_insert(ht, rec->val, rec->hash, NULL);
Michal Vasko5a28e0e2018-08-07 11:29:06 +0200381 assert(!ret);
382 (void)ret;
Michal Vasko04210d72018-03-16 12:07:47 +0100383 }
384 }
385
386 /* final touches */
Michal Vasko04210d72018-03-16 12:07:47 +0100387 free(old_recs);
388 return 0;
389}
390
Michal Vasko24affa02018-04-03 09:06:06 +0200391/* return: 0 - hash found, returned its record,
392 * 1 - hash not found, returned the record where it would be inserted */
Michal Vasko419fce02018-03-21 11:55:09 +0100393static int
Michal Vasko24affa02018-04-03 09:06:06 +0200394lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
Michal Vasko6c810702018-03-14 16:23:21 +0100395{
Michal Vasko419fce02018-03-21 11:55:09 +0100396 struct ht_rec *rec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200397 uint32_t i, idx;
Michal Vasko6c810702018-03-14 16:23:21 +0100398
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200399 if (rec_p) {
400 *rec_p = NULL;
401 }
402
403 idx = i = hash & (ht->size - 1);
Michal Vasko419fce02018-03-21 11:55:09 +0100404 rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
Michal Vasko6c810702018-03-14 16:23:21 +0100405
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200406 /* skip through overflow and deleted records */
407 while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
408 if ((rec->hits == -1) && rec_p && !(*rec_p)) {
409 /* remember this record for return */
410 *rec_p = rec;
411 }
412 i = (i + 1) % ht->size;
Michal Vasko7f6f0f92018-05-21 13:57:30 +0200413 if (i == idx) {
414 /* we went through all the records (very unlikely, but possible when many records are invalid),
415 * just return not found */
416 assert(!rec_p || *rec_p);
417 return 1;
418 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200419 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
Michal Vasko419fce02018-03-21 11:55:09 +0100420 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200421 if (rec->hits == 0) {
Michal Vasko419fce02018-03-21 11:55:09 +0100422 /* we could not find the value */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200423 if (rec_p && !*rec_p) {
424 *rec_p = rec;
Michal Vasko6c810702018-03-14 16:23:21 +0100425 }
Michal Vasko419fce02018-03-21 11:55:09 +0100426 return 1;
Michal Vasko6c810702018-03-14 16:23:21 +0100427 }
428
Michal Vasko78548832018-06-04 12:07:52 +0200429 /* we have found a record with equal (shortened) hash */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200430 if (rec_p) {
431 *rec_p = rec;
432 }
Michal Vasko24affa02018-04-03 09:06:06 +0200433 return 0;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200434}
435
Michal Vasko1de7bd12018-05-21 14:27:02 +0200436/**
437 * @brief Search for the next collision.
438 *
439 * @param[in] ht Hash table to search in.
440 * @param[in,out] last Last returned collision record.
441 * @param[in] first First collision record (hits > 1).
442 * @return 0 when hash collision found, \p last points to this next collision,
443 * 1 when hash collision not found, \p last points to the record where it would be inserted.
444 */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200445static int
Michal Vasko1de7bd12018-05-21 14:27:02 +0200446lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200447{
448 struct ht_rec *empty = NULL;
Michal Vasko1de7bd12018-05-21 14:27:02 +0200449 uint32_t i, idx;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200450
451 assert(last && *last);
452
453 idx = (*last)->hash & (ht->size - 1);
Michal Vasko1de7bd12018-05-21 14:27:02 +0200454 i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200455
456 do {
457 i = (i + 1) % ht->size;
Michal Vasko1de7bd12018-05-21 14:27:02 +0200458 *last = lyht_get_rec(ht->recs, ht->rec_size, i);
459 if (*last == first) {
Michal Vasko1d89f902018-05-21 13:01:32 +0200460 /* we went through all the records (very unlikely, but possible when many records are invalid),
461 * just return an invalid record */
462 assert(empty);
463 *last = empty;
464 return 1;
465 }
Michal Vasko1de7bd12018-05-21 14:27:02 +0200466
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200467 if (((*last)->hits == -1) && !empty) {
468 empty = *last;
469 }
470 } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
471
472 if ((*last)->hits > 0) {
473 /* we found a collision */
474 assert((*last)->hits == 1);
475 return 0;
476 }
477
478 /* no next collision found, return the record where it would be inserted */
479 if (empty) {
480 *last = empty;
481 } /* else (*last)->hits == 0, it is already correct */
482 return 1;
483}
484
485int
Michal Vaskofd5581a2018-04-25 13:20:20 +0200486lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200487{
Michal Vasko1de7bd12018-05-21 14:27:02 +0200488 struct ht_rec *rec, *crec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200489 uint32_t i, c;
Michal Vasko24affa02018-04-03 09:06:06 +0200490 int r;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200491
Michal Vasko24affa02018-04-03 09:06:06 +0200492 if (lyht_find_first(ht, hash, &rec)) {
493 /* not found */
494 return 1;
495 }
Michal Vasko78548832018-06-04 12:07:52 +0200496 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200497 /* even the value matches */
Michal Vaskofd5581a2018-04-25 13:20:20 +0200498 if (match_p) {
499 *match_p = rec->val;
500 }
Michal Vasko24affa02018-04-03 09:06:06 +0200501 return 0;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200502 }
503
504 /* some collisions, we need to go through them, too */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200505 crec = rec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200506 c = rec->hits;
507 for (i = 1; i < c; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200508 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200509 assert(!r);
Michal Vasko24affa02018-04-03 09:06:06 +0200510 (void)r;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200511
512 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200513 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200514 if (match_p) {
515 *match_p = rec->val;
516 }
Michal Vasko419fce02018-03-21 11:55:09 +0100517 return 0;
518 }
Michal Vasko419fce02018-03-21 11:55:09 +0100519 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200520
521 /* not found even in collisions */
522 return 1;
Michal Vasko419fce02018-03-21 11:55:09 +0100523}
524
525int
Michal Vaskofd5581a2018-04-25 13:20:20 +0200526lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
527{
Michal Vasko1de7bd12018-05-21 14:27:02 +0200528 struct ht_rec *rec, *crec;
Michal Vaskofd5581a2018-04-25 13:20:20 +0200529 uint32_t i, c;
530 int r, found = 0;
531
532 if (lyht_find_first(ht, hash, &rec)) {
533 /* not found, cannot happen */
534 assert(0);
535 }
536
Michal Vasko78548832018-06-04 12:07:52 +0200537 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200538 /* previously returned value */
539 found = 1;
540 }
541
542 if (rec->hits == 1) {
543 /* there are no more similar values */
544 assert(rec->hash == hash);
545 assert(found);
546 return 1;
547 }
548
549 /* go through collisions and find next one after the previous one */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200550 crec = rec;
Michal Vaskofd5581a2018-04-25 13:20:20 +0200551 c = rec->hits;
552 for (i = 1; i < c; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200553 r = lyht_find_collision(ht, &rec, crec);
Michal Vaskofd5581a2018-04-25 13:20:20 +0200554 assert(!r);
555 (void)r;
556
557 if (rec->hash != hash) {
558 /* a normal collision, we are not interested in those */
559 continue;
560 }
561
562 if (found) {
563 /* next value with equal hash, found our value */
564 if (match_p) {
565 *match_p = rec->val;
566 }
567 return 0;
568 }
569
Michal Vasko66342942018-04-27 13:56:21 +0200570 if (!ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200571 /* already returned value, skip */
572 continue;
573 }
574
575 /* this one was returned previously, continue looking */
576 found = 1;
577 }
578
579 /* the last equal value was already returned */
580 assert(found);
581 return 1;
582}
583
584int
David Sedláka3969962018-08-13 10:28:32 +0200585lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash,
586 values_equal_cb resize_val_equal, void **match_p)
Michal Vasko419fce02018-03-21 11:55:09 +0100587{
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200588 struct ht_rec *rec, *crec = NULL;
589 int32_t i;
Michal Vasko24affa02018-04-03 09:06:06 +0200590 int r, ret;
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200591 values_equal_cb old_val_equal;
Michal Vasko419fce02018-03-21 11:55:09 +0100592
Michal Vasko24affa02018-04-03 09:06:06 +0200593 if (!lyht_find_first(ht, hash, &rec)) {
Michal Vasko78548832018-06-04 12:07:52 +0200594 /* we found matching shortened hash */
595 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200596 /* even the value matches */
David Sedlák9b357832018-08-09 10:39:27 +0200597 if (match_p) {
598 *match_p = (void *)&rec->val;
599 }
Michal Vasko24affa02018-04-03 09:06:06 +0200600 return 1;
601 }
Michal Vasko419fce02018-03-21 11:55:09 +0100602
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200603 /* some collisions, we need to go through them, too */
604 crec = rec;
605 for (i = 1; i < crec->hits; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200606 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200607 assert(!r);
608
609 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200610 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
David Sedlák9b357832018-08-09 10:39:27 +0200611 if (match_p) {
612 *match_p = (void *)&rec->val;
613 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200614 return 1;
615 }
Michal Vasko419fce02018-03-21 11:55:09 +0100616 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200617
618 /* value not found, get the record where it will be inserted */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200619 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200620 assert(r);
Michal Vasko419fce02018-03-21 11:55:09 +0100621 }
622
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200623 /* insert it into the returned record */
624 assert(rec->hits < 1);
Michal Vasko419fce02018-03-21 11:55:09 +0100625 rec->hash = hash;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200626 rec->hits = 1;
Michal Vasko419fce02018-03-21 11:55:09 +0100627 memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
David Sedlák9b357832018-08-09 10:39:27 +0200628 if (match_p) {
629 *match_p = (void *)&rec->val;
630 }
Michal Vasko419fce02018-03-21 11:55:09 +0100631
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200632 if (crec) {
633 /* there was a collision, increase hits */
Michal Vaskoe330d712018-04-25 13:20:43 +0200634 if (crec->hits == INT32_MAX) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200635 LOGINT(NULL);
636 }
637 ++crec->hits;
638 }
639
Michal Vasko419fce02018-03-21 11:55:09 +0100640 /* check size & enlarge if needed */
641 ret = 0;
Michal Vasko6c810702018-03-14 16:23:21 +0100642 ++ht->used;
Michal Vasko04210d72018-03-16 12:07:47 +0100643 if (ht->resize) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200644 r = (ht->used * 100) / ht->size;
645 if ((ht->resize == 1) && (r >= LYHT_FIRST_SHRINK_PERCENTAGE)) {
Michal Vasko04210d72018-03-16 12:07:47 +0100646 /* enable shrinking */
647 ht->resize = 2;
Michal Vasko419fce02018-03-21 11:55:09 +0100648 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200649 if ((ht->resize == 2) && (r >= LYHT_ENLARGE_PERCENTAGE)) {
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200650 if (resize_val_equal) {
651 old_val_equal = lyht_set_cb(ht, resize_val_equal);
652 }
653
Michal Vasko04210d72018-03-16 12:07:47 +0100654 /* enlarge */
655 ret = lyht_resize(ht, 1);
David Sedlák9b357832018-08-09 10:39:27 +0200656 /* if hash_table was resized, we need to find new matching value */
657 if (ret == 0 && match_p) {
658 lyht_find(ht, val_p, hash, match_p);
659 }
David Sedláka3969962018-08-13 10:28:32 +0200660
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200661 if (resize_val_equal) {
662 lyht_set_cb(ht, old_val_equal);
663 }
Michal Vasko04210d72018-03-16 12:07:47 +0100664 }
665 }
666 return ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100667}
668
669int
David Sedláka3969962018-08-13 10:28:32 +0200670lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200671{
David Sedláka3969962018-08-13 10:28:32 +0200672 return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200673}
674
675int
Michal Vasko419fce02018-03-21 11:55:09 +0100676lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash)
Michal Vasko6c810702018-03-14 16:23:21 +0100677{
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200678 struct ht_rec *rec, *crec;
679 int32_t i;
Michal Vasko24affa02018-04-03 09:06:06 +0200680 int first_matched = 0, r, ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100681
Michal Vasko24affa02018-04-03 09:06:06 +0200682 if (lyht_find_first(ht, hash, &rec)) {
683 /* hash not found */
Michal Vasko6c810702018-03-14 16:23:21 +0100684 return 1;
685 }
Michal Vasko78548832018-06-04 12:07:52 +0200686 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200687 /* even the value matches */
688 first_matched = 1;
689 }
Michal Vasko6c810702018-03-14 16:23:21 +0100690
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200691 /* we always need to go through collisions */
692 crec = rec;
693 for (i = 1; i < crec->hits; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200694 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200695 assert(!r);
Michal Vasko419fce02018-03-21 11:55:09 +0100696
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200697 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200698 if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200699 break;
700 }
Michal Vasko419fce02018-03-21 11:55:09 +0100701 }
702
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200703 if (i < crec->hits) {
704 /* one of collisions matched, reduce collision count, remove the record */
Michal Vasko24affa02018-04-03 09:06:06 +0200705 assert(!first_matched);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200706 --crec->hits;
707 rec->hits = -1;
Michal Vasko24affa02018-04-03 09:06:06 +0200708 } else if (first_matched) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200709 /* the first record matches */
710 if (crec != rec) {
711 /* ... so put the last collision in its place */
712 rec->hits = crec->hits - 1;
713 memcpy(crec, rec, ht->rec_size);
714 }
715 rec->hits = -1;
716 } else {
717 /* value not found even in collisions */
Michal Vasko24affa02018-04-03 09:06:06 +0200718 assert(!first_matched);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200719 return 1;
720 }
721
Michal Vasko419fce02018-03-21 11:55:09 +0100722 /* check size & shrink if needed */
723 ret = 0;
724 --ht->used;
725 if (ht->resize == 2) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200726 r = (ht->used * 100) / ht->size;
727 if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
Michal Vasko419fce02018-03-21 11:55:09 +0100728 /* shrink */
729 ret = lyht_resize(ht, 0);
Michal Vasko6c810702018-03-14 16:23:21 +0100730 }
Michal Vasko419fce02018-03-21 11:55:09 +0100731 }
Michal Vasko6c810702018-03-14 16:23:21 +0100732
Michal Vasko419fce02018-03-21 11:55:09 +0100733 return ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100734}