blob: 77448545e140d42b32fc86e9cb4f52623b344a52 [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;
David Sedlák69ed4492018-08-21 10:09:19 +020065 struct dict_rec *dict_rec = NULL;
66 struct ht_rec *rec = NULL;
Radek Krejcida04f4a2015-05-21 12:54:09 +020067
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020068 if (!dict) {
Michal Vasko53b7da02018-02-13 15:28:42 +010069 LOGARG;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020070 return;
71 }
Radek Krejcida04f4a2015-05-21 12:54:09 +020072
David Sedlákb0915842018-08-21 10:19:37 +020073 for (i = 0; i < dict->hash_tab->size; i++) {
David Sedlák1c151f22018-08-08 11:01:22 +020074 /* get ith record */
David Sedlák69ed4492018-08-21 10:09:19 +020075 rec = (struct ht_rec *)&dict->hash_tab->recs[i * dict->hash_tab->rec_size];
David Sedlák1c151f22018-08-08 11:01:22 +020076 if (rec->hits == 1) {
David Sedlákee0ffc72018-08-08 14:08:56 +020077 /*
78 * this should not happen, all records inserted into
David Sedlák1c151f22018-08-08 11:01:22 +020079 * dictionary are supposed to be removed using lydict_remove()
80 * before calling lydict_clean()
81 */
David Sedlák69ed4492018-08-21 10:09:19 +020082 dict_rec = (struct dict_rec *)rec->val;
83 LOGWRN(NULL, "String \"%s\" not freed from the dictionary, refcount %d", dict_rec->value, dict_rec->refcount);
David Sedlákfd4235c2018-08-09 10:09:18 +020084 /* if record wasn't removed before free string allocated for that record */
David Sedlák9460ece2018-08-21 10:59:58 +020085#ifdef NDEBUG
David Sedlák69ed4492018-08-21 10:09:19 +020086 free(dict_rec->value);
David Sedlák9460ece2018-08-21 10:59:58 +020087#endif
David Sedlák1c151f22018-08-08 11:01:22 +020088 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +020089 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +010090
David Sedlákee0ffc72018-08-08 14:08:56 +020091 /* free table and destroy mutex */
David Sedlákc6ecb892018-08-05 23:16:50 +020092 lyht_free(dict->hash_tab);
Radek Krejcifebcbfb2016-02-04 14:58:42 +010093 pthread_mutex_destroy(&dict->lock);
Radek Krejcida04f4a2015-05-21 12:54:09 +020094}
Radek Krejciee627582015-04-20 17:39:59 +020095
96/*
97 * Bob Jenkin's one-at-a-time hash
98 * http://www.burtleburtle.net/bob/hash/doobs.html
99 *
100 * Spooky hash is faster, but it works only for little endian architectures.
101 */
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200102static uint32_t
103dict_hash(const char *key, size_t len)
Radek Krejciee627582015-04-20 17:39:59 +0200104{
105 uint32_t hash, i;
106
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200107 for (hash = i = 0; i < len; ++i) {
Radek Krejciee627582015-04-20 17:39:59 +0200108 hash += key[i];
109 hash += (hash << 10);
110 hash ^= (hash >> 6);
111 }
112 hash += (hash << 3);
113 hash ^= (hash >> 11);
114 hash += (hash << 15);
115 return hash;
116}
117
Radek Krejci63b79c82016-08-10 10:09:33 +0200118/*
119 * Usage:
120 * - init hash to 0
121 * - repeatedly call dict_hash_multi(), provide hash from the last call
122 * - call dict_hash_multi() with key_part = NULL to finish the hash
123 */
124uint32_t
125dict_hash_multi(uint32_t hash, const char *key_part, size_t len)
126{
127 uint32_t i;
128
129 if (key_part) {
130 for (i = 0; i < len; ++i) {
131 hash += key_part[i];
132 hash += (hash << 10);
133 hash ^= (hash >> 6);
134 }
135 } else {
136 hash += (hash << 3);
137 hash ^= (hash >> 11);
138 hash += (hash << 15);
139 }
140
141 return hash;
142}
143
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200144API void
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200145lydict_remove(struct ly_ctx *ctx, const char *value)
Radek Krejciee627582015-04-20 17:39:59 +0200146{
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200147 size_t len;
David Sedlákc6ecb892018-08-05 23:16:50 +0200148 int ret;
149 uint32_t hash;
150 struct dict_rec rec, *match = NULL;
David Sedlák2aabdea2018-08-08 10:10:36 +0200151 char *val_p;
Radek Krejciee627582015-04-20 17:39:59 +0200152
Michal Vasko5c328e72016-10-13 12:17:37 +0200153 if (!value || !ctx) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200154 return;
155 }
Radek Krejciee627582015-04-20 17:39:59 +0200156
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200157 len = strlen(value);
David Sedlákc6ecb892018-08-05 23:16:50 +0200158 hash = dict_hash(value, len);
David Sedlák2aabdea2018-08-08 10:10:36 +0200159
David Sedlák0f860492018-08-07 16:24:24 +0200160 /* create record for lyht_find call */
David Sedlák2aabdea2018-08-08 10:10:36 +0200161 rec.value = (char *)value;
David Sedlákc6ecb892018-08-05 23:16:50 +0200162 rec.refcount = 0;
Radek Krejciee627582015-04-20 17:39:59 +0200163
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100164 pthread_mutex_lock(&ctx->dict.lock);
David Sedlák3468d3c2018-08-08 13:38:36 +0200165 /* set len as data for compare callback */
David Sedlák9af9ed52018-08-08 12:54:20 +0200166 lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
David Sedlákee0ffc72018-08-08 14:08:56 +0200167 /* check if value is already inserted */
David Sedlákc6ecb892018-08-05 23:16:50 +0200168 ret = lyht_find(ctx->dict.hash_tab, &rec, hash, (void **)&match);
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100169
David Sedlák3468d3c2018-08-08 13:38:36 +0200170 if (ret == 0) {
Michal Vaskodf2778e2018-08-17 10:28:11 +0200171 LY_CHECK_ERR_GOTO(!match, LOGINT(ctx), finish);
172
David Sedlákb6ea11c2018-08-06 15:53:42 +0200173 /* if value is already in dictionary, decrement reference counter */
David Sedlákcaac88b2018-08-09 11:15:26 +0200174 match->refcount--;
David Sedlákb6ea11c2018-08-06 15:53:42 +0200175 if (match->refcount == 0) {
David Sedlákee0ffc72018-08-08 14:08:56 +0200176 /*
177 * remove record
David Sedlák2aabdea2018-08-08 10:10:36 +0200178 * save pointer to stored string before lyht_remove to
David Sedlák3468d3c2018-08-08 13:38:36 +0200179 * free it after it is removed from hash table
180 */
David Sedlák2aabdea2018-08-08 10:10:36 +0200181 val_p = match->value;
182 ret = lyht_remove(ctx->dict.hash_tab, &rec, hash);
183 free(val_p);
Michal Vaskodf2778e2018-08-17 10:28:11 +0200184 LY_CHECK_ERR_GOTO(ret, LOGINT(ctx), finish);
David Sedlákc6ecb892018-08-05 23:16:50 +0200185 }
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200186 }
Michal Vaskodf2778e2018-08-17 10:28:11 +0200187
188finish:
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100189 pthread_mutex_unlock(&ctx->dict.lock);
Radek Krejciee627582015-04-20 17:39:59 +0200190}
191
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200192static char *
193dict_insert(struct ly_ctx *ctx, char *value, size_t len, int zerocopy)
Radek Krejciee627582015-04-20 17:39:59 +0200194{
David Sedlákc6ecb892018-08-05 23:16:50 +0200195 struct dict_rec *match = NULL, rec;
196 int ret = 0;
197 uint32_t hash;
Radek Krejciee627582015-04-20 17:39:59 +0200198
David Sedlákc6ecb892018-08-05 23:16:50 +0200199 hash = dict_hash(value, len);
David Sedlák3468d3c2018-08-08 13:38:36 +0200200 /* set len as data for compare callback */
David Sedlák2aabdea2018-08-08 10:10:36 +0200201 lyht_set_cb_data(ctx->dict.hash_tab, (void *)&len);
David Sedlákcaac88b2018-08-09 11:15:26 +0200202 /* create record for lyht_insert */
David Sedlák2aabdea2018-08-08 10:10:36 +0200203 rec.value = value;
David Sedlákc6ecb892018-08-05 23:16:50 +0200204 rec.refcount = 1;
205
David Sedlákcaac88b2018-08-09 11:15:26 +0200206 LOGDBG(LY_LDGDICT, "inserting \"%s\"", rec.value);
207 ret = lyht_insert(ctx->dict.hash_tab, (void *)&rec, hash, (void **)&match);
208 if (ret == 1) {
209 match->refcount++;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200210 if (zerocopy) {
David Sedlákc6ecb892018-08-05 23:16:50 +0200211 free(value);
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200212 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200213 } else if (ret == 0) {
David Sedlák2aabdea2018-08-08 10:10:36 +0200214 if (!zerocopy) {
Michal Vasko88025b42018-08-15 11:23:55 +0200215 /*
216 * allocate string for new record
David Sedlákcaac88b2018-08-09 11:15:26 +0200217 * record is already inserted in hash table
218 */
219 match->value = malloc(sizeof *match->value * (len + 1));
220 LY_CHECK_ERR_RETURN(!match->value, LOGMEM(ctx), NULL);
221 memcpy(match->value, value, len);
222 match->value[len] = '\0';
Radek Krejci592e00d2016-05-19 12:38:08 +0200223 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200224 } else {
225 /* lyht_insert returned error */
226 LOGINT(ctx);
227 return NULL;
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200228 }
David Sedlákcaac88b2018-08-09 11:15:26 +0200229
230 return match->value;
Radek Krejciee627582015-04-20 17:39:59 +0200231}
Radek Krejci4ea08382015-04-21 09:41:40 +0200232
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200233API const char *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200234lydict_insert(struct ly_ctx *ctx, const char *value, size_t len)
Radek Krejci4ea08382015-04-21 09:41:40 +0200235{
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100236 const char *result;
237
Radek Krejcia5269642015-07-20 19:04:11 +0200238 if (!value) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200239 return NULL;
240 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100241
David Sedlák885c78a2018-08-08 14:11:06 +0200242 if (!len) {
243 len = strlen(value);
244 }
245
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100246 pthread_mutex_lock(&ctx->dict.lock);
247 result = dict_insert(ctx, (char *)value, len, 0);
248 pthread_mutex_unlock(&ctx->dict.lock);
249
250 return result;
Radek Krejci4ea08382015-04-21 09:41:40 +0200251}
252
Radek Krejcifcf37bd2015-10-22 10:27:53 +0200253API const char *
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200254lydict_insert_zc(struct ly_ctx *ctx, char *value)
Radek Krejci4ea08382015-04-21 09:41:40 +0200255{
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100256 const char *result;
257
Radek Krejcia5269642015-07-20 19:04:11 +0200258 if (!value) {
Radek Krejci6e4ffbb2015-06-16 10:34:41 +0200259 return NULL;
260 }
Radek Krejcifebcbfb2016-02-04 14:58:42 +0100261
262 pthread_mutex_lock(&ctx->dict.lock);
263 result = dict_insert(ctx, value, strlen(value), 1);
264 pthread_mutex_unlock(&ctx->dict.lock);
265
266 return result;
Radek Krejci4ea08382015-04-21 09:41:40 +0200267}
Michal Vasko6c810702018-03-14 16:23:21 +0100268
Michal Vaskoe41dfe62018-11-08 13:28:07 +0100269struct ht_rec *
Michal Vasko419fce02018-03-21 11:55:09 +0100270lyht_get_rec(unsigned char *recs, uint16_t rec_size, uint32_t idx)
271{
272 return (struct ht_rec *)&recs[idx * rec_size];
273}
274
Michal Vasko6c810702018-03-14 16:23:21 +0100275struct hash_table *
Michal Vasko419fce02018-03-21 11:55:09 +0100276lyht_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 +0100277{
278 struct hash_table *ht;
279
280 /* check that 2^x == size (power of 2) */
Michal Vaskod0f84c32018-03-16 12:13:15 +0100281 assert(size && !(size & (size - 1)));
Michal Vasko419fce02018-03-21 11:55:09 +0100282 assert(val_equal && val_size);
Michal Vasko04210d72018-03-16 12:07:47 +0100283 assert(resize == 0 || resize == 1);
Michal Vasko6c810702018-03-14 16:23:21 +0100284
Michal Vaskod0f84c32018-03-16 12:13:15 +0100285 if (size < LYHT_MIN_SIZE) {
286 size = LYHT_MIN_SIZE;
287 }
288
Michal Vasko6c810702018-03-14 16:23:21 +0100289 ht = malloc(sizeof *ht);
290 LY_CHECK_ERR_RETURN(!ht, LOGMEM(NULL), NULL);
291
Michal Vasko6c810702018-03-14 16:23:21 +0100292 ht->used = 0;
293 ht->size = size;
294 ht->val_equal = val_equal;
295 ht->cb_data = cb_data;
Michal Vasko419fce02018-03-21 11:55:09 +0100296 ht->resize = (uint16_t)resize;
297
298 ht->rec_size = (sizeof(struct ht_rec) - 1) + val_size;
299 /* allocate the records correctly */
300 ht->recs = calloc(size, ht->rec_size);
301 LY_CHECK_ERR_RETURN(!ht->recs, free(ht); LOGMEM(NULL), NULL);
302
Michal Vasko6c810702018-03-14 16:23:21 +0100303 return ht;
304}
305
Michal Vasko8ac94532018-05-18 09:38:13 +0200306values_equal_cb
307lyht_set_cb(struct hash_table *ht, values_equal_cb new_val_equal)
308{
309 values_equal_cb prev;
310
311 prev = ht->val_equal;
312 ht->val_equal = new_val_equal;
313 return prev;
314}
315
316void *
317lyht_set_cb_data(struct hash_table *ht, void *new_cb_data)
318{
319 void *prev;
320
321 prev = ht->cb_data;
322 ht->cb_data = new_cb_data;
323 return prev;
324}
325
Michal Vasko8690d122018-04-26 14:28:58 +0200326struct hash_table *
327lyht_dup(const struct hash_table *orig)
328{
329 struct hash_table *ht;
330
331 if (!orig) {
332 return NULL;
333 }
334
335 ht = lyht_new(orig->size, orig->rec_size - (sizeof(struct ht_rec) - 1), orig->val_equal, orig->cb_data, orig->resize ? 1 : 0);
336 if (!ht) {
337 return NULL;
338 }
339
340 memcpy(ht->recs, orig->recs, orig->used * orig->rec_size);
341 ht->used = orig->used;
342 return ht;
343}
344
Michal Vasko6c810702018-03-14 16:23:21 +0100345void
346lyht_free(struct hash_table *ht)
347{
348 if (ht) {
349 free(ht->recs);
350 free(ht);
351 }
352}
353
Michal Vasko04210d72018-03-16 12:07:47 +0100354static int
355lyht_resize(struct hash_table *ht, int enlarge)
356{
Michal Vasko419fce02018-03-21 11:55:09 +0100357 struct ht_rec *rec;
358 unsigned char *old_recs;
Michal Vasko24affa02018-04-03 09:06:06 +0200359 uint32_t i, old_size;
Michal Vasko5a28e0e2018-08-07 11:29:06 +0200360 int ret;
Michal Vasko04210d72018-03-16 12:07:47 +0100361
362 old_recs = ht->recs;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200363 old_size = ht->size;
Michal Vasko04210d72018-03-16 12:07:47 +0100364
365 if (enlarge) {
366 /* double the size */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200367 ht->size <<= 1;
Michal Vasko04210d72018-03-16 12:07:47 +0100368 } else {
369 /* half the size */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200370 ht->size >>= 1;
Michal Vasko04210d72018-03-16 12:07:47 +0100371 }
372
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200373 ht->recs = calloc(ht->size, ht->rec_size);
374 LY_CHECK_ERR_RETURN(!ht->recs, LOGMEM(NULL); ht->recs = old_recs; ht->size = old_size, -1);
Michal Vasko04210d72018-03-16 12:07:47 +0100375
376 /* reset used, it will increase again */
377 ht->used = 0;
378
379 /* add all the old records into the new records array */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200380 for (i = 0; i < old_size; ++i) {
Michal Vasko419fce02018-03-21 11:55:09 +0100381 rec = lyht_get_rec(old_recs, ht->rec_size, i);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200382 if (rec->hits > 0) {
David Sedláka3969962018-08-13 10:28:32 +0200383 ret = lyht_insert(ht, rec->val, rec->hash, NULL);
Michal Vasko5a28e0e2018-08-07 11:29:06 +0200384 assert(!ret);
385 (void)ret;
Michal Vasko04210d72018-03-16 12:07:47 +0100386 }
387 }
388
389 /* final touches */
Michal Vasko04210d72018-03-16 12:07:47 +0100390 free(old_recs);
391 return 0;
392}
393
Michal Vasko24affa02018-04-03 09:06:06 +0200394/* return: 0 - hash found, returned its record,
395 * 1 - hash not found, returned the record where it would be inserted */
Michal Vasko419fce02018-03-21 11:55:09 +0100396static int
Michal Vasko24affa02018-04-03 09:06:06 +0200397lyht_find_first(struct hash_table *ht, uint32_t hash, struct ht_rec **rec_p)
Michal Vasko6c810702018-03-14 16:23:21 +0100398{
Michal Vasko419fce02018-03-21 11:55:09 +0100399 struct ht_rec *rec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200400 uint32_t i, idx;
Michal Vasko6c810702018-03-14 16:23:21 +0100401
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200402 if (rec_p) {
403 *rec_p = NULL;
404 }
405
406 idx = i = hash & (ht->size - 1);
Michal Vasko419fce02018-03-21 11:55:09 +0100407 rec = lyht_get_rec(ht->recs, ht->rec_size, idx);
Michal Vasko6c810702018-03-14 16:23:21 +0100408
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200409 /* skip through overflow and deleted records */
410 while ((rec->hits != 0) && ((rec->hits == -1) || ((rec->hash & (ht->size - 1)) != idx))) {
411 if ((rec->hits == -1) && rec_p && !(*rec_p)) {
412 /* remember this record for return */
413 *rec_p = rec;
414 }
415 i = (i + 1) % ht->size;
Michal Vasko7f6f0f92018-05-21 13:57:30 +0200416 if (i == idx) {
417 /* we went through all the records (very unlikely, but possible when many records are invalid),
418 * just return not found */
419 assert(!rec_p || *rec_p);
420 return 1;
421 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200422 rec = lyht_get_rec(ht->recs, ht->rec_size, i);
Michal Vasko419fce02018-03-21 11:55:09 +0100423 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200424 if (rec->hits == 0) {
Michal Vasko419fce02018-03-21 11:55:09 +0100425 /* we could not find the value */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200426 if (rec_p && !*rec_p) {
427 *rec_p = rec;
Michal Vasko6c810702018-03-14 16:23:21 +0100428 }
Michal Vasko419fce02018-03-21 11:55:09 +0100429 return 1;
Michal Vasko6c810702018-03-14 16:23:21 +0100430 }
431
Michal Vasko78548832018-06-04 12:07:52 +0200432 /* we have found a record with equal (shortened) hash */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200433 if (rec_p) {
434 *rec_p = rec;
435 }
Michal Vasko24affa02018-04-03 09:06:06 +0200436 return 0;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200437}
438
Michal Vasko1de7bd12018-05-21 14:27:02 +0200439/**
440 * @brief Search for the next collision.
441 *
442 * @param[in] ht Hash table to search in.
443 * @param[in,out] last Last returned collision record.
444 * @param[in] first First collision record (hits > 1).
445 * @return 0 when hash collision found, \p last points to this next collision,
446 * 1 when hash collision not found, \p last points to the record where it would be inserted.
447 */
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200448static int
Michal Vasko1de7bd12018-05-21 14:27:02 +0200449lyht_find_collision(struct hash_table *ht, struct ht_rec **last, struct ht_rec *first)
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200450{
451 struct ht_rec *empty = NULL;
Michal Vasko1de7bd12018-05-21 14:27:02 +0200452 uint32_t i, idx;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200453
454 assert(last && *last);
455
456 idx = (*last)->hash & (ht->size - 1);
Michal Vasko1de7bd12018-05-21 14:27:02 +0200457 i = (((unsigned char *)*last) - ht->recs) / ht->rec_size;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200458
459 do {
460 i = (i + 1) % ht->size;
Michal Vasko1de7bd12018-05-21 14:27:02 +0200461 *last = lyht_get_rec(ht->recs, ht->rec_size, i);
462 if (*last == first) {
Michal Vasko1d89f902018-05-21 13:01:32 +0200463 /* we went through all the records (very unlikely, but possible when many records are invalid),
464 * just return an invalid record */
465 assert(empty);
466 *last = empty;
467 return 1;
468 }
Michal Vasko1de7bd12018-05-21 14:27:02 +0200469
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200470 if (((*last)->hits == -1) && !empty) {
471 empty = *last;
472 }
473 } while (((*last)->hits != 0) && (((*last)->hits == -1) || (((*last)->hash & (ht->size - 1)) != idx)));
474
475 if ((*last)->hits > 0) {
476 /* we found a collision */
477 assert((*last)->hits == 1);
478 return 0;
479 }
480
481 /* no next collision found, return the record where it would be inserted */
482 if (empty) {
483 *last = empty;
484 } /* else (*last)->hits == 0, it is already correct */
485 return 1;
486}
487
488int
Michal Vaskofd5581a2018-04-25 13:20:20 +0200489lyht_find(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200490{
Michal Vasko1de7bd12018-05-21 14:27:02 +0200491 struct ht_rec *rec, *crec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200492 uint32_t i, c;
Michal Vasko24affa02018-04-03 09:06:06 +0200493 int r;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200494
Michal Vasko24affa02018-04-03 09:06:06 +0200495 if (lyht_find_first(ht, hash, &rec)) {
496 /* not found */
497 return 1;
498 }
Michal Vasko78548832018-06-04 12:07:52 +0200499 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200500 /* even the value matches */
Michal Vaskofd5581a2018-04-25 13:20:20 +0200501 if (match_p) {
502 *match_p = rec->val;
503 }
Michal Vasko24affa02018-04-03 09:06:06 +0200504 return 0;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200505 }
506
507 /* some collisions, we need to go through them, too */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200508 crec = rec;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200509 c = rec->hits;
510 for (i = 1; i < c; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200511 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200512 assert(!r);
Michal Vasko24affa02018-04-03 09:06:06 +0200513 (void)r;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200514
515 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200516 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 0, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200517 if (match_p) {
518 *match_p = rec->val;
519 }
Michal Vasko419fce02018-03-21 11:55:09 +0100520 return 0;
521 }
Michal Vasko419fce02018-03-21 11:55:09 +0100522 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200523
524 /* not found even in collisions */
525 return 1;
Michal Vasko419fce02018-03-21 11:55:09 +0100526}
527
528int
Michal Vaskofd5581a2018-04-25 13:20:20 +0200529lyht_find_next(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
530{
Michal Vasko1de7bd12018-05-21 14:27:02 +0200531 struct ht_rec *rec, *crec;
Michal Vaskofd5581a2018-04-25 13:20:20 +0200532 uint32_t i, c;
533 int r, found = 0;
534
535 if (lyht_find_first(ht, hash, &rec)) {
536 /* not found, cannot happen */
537 assert(0);
538 }
539
Michal Vasko78548832018-06-04 12:07:52 +0200540 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200541 /* previously returned value */
542 found = 1;
543 }
544
545 if (rec->hits == 1) {
546 /* there are no more similar values */
547 assert(rec->hash == hash);
548 assert(found);
549 return 1;
550 }
551
552 /* go through collisions and find next one after the previous one */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200553 crec = rec;
Michal Vaskofd5581a2018-04-25 13:20:20 +0200554 c = rec->hits;
555 for (i = 1; i < c; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200556 r = lyht_find_collision(ht, &rec, crec);
Michal Vaskofd5581a2018-04-25 13:20:20 +0200557 assert(!r);
558 (void)r;
559
560 if (rec->hash != hash) {
561 /* a normal collision, we are not interested in those */
562 continue;
563 }
564
565 if (found) {
566 /* next value with equal hash, found our value */
567 if (match_p) {
568 *match_p = rec->val;
569 }
570 return 0;
571 }
572
Michal Vasko66342942018-04-27 13:56:21 +0200573 if (!ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vaskofd5581a2018-04-25 13:20:20 +0200574 /* already returned value, skip */
575 continue;
576 }
577
578 /* this one was returned previously, continue looking */
579 found = 1;
580 }
581
582 /* the last equal value was already returned */
583 assert(found);
584 return 1;
585}
586
587int
David Sedláka3969962018-08-13 10:28:32 +0200588lyht_insert_with_resize_cb(struct hash_table *ht, void *val_p, uint32_t hash,
589 values_equal_cb resize_val_equal, void **match_p)
Michal Vasko419fce02018-03-21 11:55:09 +0100590{
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200591 struct ht_rec *rec, *crec = NULL;
592 int32_t i;
Michal Vasko24affa02018-04-03 09:06:06 +0200593 int r, ret;
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200594 values_equal_cb old_val_equal;
Michal Vasko419fce02018-03-21 11:55:09 +0100595
Michal Vasko24affa02018-04-03 09:06:06 +0200596 if (!lyht_find_first(ht, hash, &rec)) {
Michal Vasko78548832018-06-04 12:07:52 +0200597 /* we found matching shortened hash */
598 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200599 /* even the value matches */
David Sedlák9b357832018-08-09 10:39:27 +0200600 if (match_p) {
601 *match_p = (void *)&rec->val;
602 }
Michal Vasko24affa02018-04-03 09:06:06 +0200603 return 1;
604 }
Michal Vasko419fce02018-03-21 11:55:09 +0100605
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200606 /* some collisions, we need to go through them, too */
607 crec = rec;
608 for (i = 1; i < crec->hits; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200609 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200610 assert(!r);
611
612 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200613 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
David Sedlák9b357832018-08-09 10:39:27 +0200614 if (match_p) {
615 *match_p = (void *)&rec->val;
616 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200617 return 1;
618 }
Michal Vasko419fce02018-03-21 11:55:09 +0100619 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200620
621 /* value not found, get the record where it will be inserted */
Michal Vasko1de7bd12018-05-21 14:27:02 +0200622 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200623 assert(r);
Michal Vasko419fce02018-03-21 11:55:09 +0100624 }
625
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200626 /* insert it into the returned record */
627 assert(rec->hits < 1);
Michal Vasko419fce02018-03-21 11:55:09 +0100628 rec->hash = hash;
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200629 rec->hits = 1;
Michal Vasko419fce02018-03-21 11:55:09 +0100630 memcpy(&rec->val, val_p, ht->rec_size - (sizeof(struct ht_rec) - 1));
David Sedlák9b357832018-08-09 10:39:27 +0200631 if (match_p) {
632 *match_p = (void *)&rec->val;
633 }
Michal Vasko419fce02018-03-21 11:55:09 +0100634
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200635 if (crec) {
636 /* there was a collision, increase hits */
Michal Vaskoe330d712018-04-25 13:20:43 +0200637 if (crec->hits == INT32_MAX) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200638 LOGINT(NULL);
639 }
640 ++crec->hits;
641 }
642
Michal Vasko419fce02018-03-21 11:55:09 +0100643 /* check size & enlarge if needed */
644 ret = 0;
Michal Vasko6c810702018-03-14 16:23:21 +0100645 ++ht->used;
Michal Vasko04210d72018-03-16 12:07:47 +0100646 if (ht->resize) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200647 r = (ht->used * 100) / ht->size;
648 if ((ht->resize == 1) && (r >= LYHT_FIRST_SHRINK_PERCENTAGE)) {
Michal Vasko04210d72018-03-16 12:07:47 +0100649 /* enable shrinking */
650 ht->resize = 2;
Michal Vasko419fce02018-03-21 11:55:09 +0100651 }
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200652 if ((ht->resize == 2) && (r >= LYHT_ENLARGE_PERCENTAGE)) {
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200653 if (resize_val_equal) {
654 old_val_equal = lyht_set_cb(ht, resize_val_equal);
655 }
656
Michal Vasko04210d72018-03-16 12:07:47 +0100657 /* enlarge */
658 ret = lyht_resize(ht, 1);
David Sedlák9b357832018-08-09 10:39:27 +0200659 /* if hash_table was resized, we need to find new matching value */
660 if (ret == 0 && match_p) {
661 lyht_find(ht, val_p, hash, match_p);
662 }
Michal Vasko88025b42018-08-15 11:23:55 +0200663
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200664 if (resize_val_equal) {
665 lyht_set_cb(ht, old_val_equal);
666 }
Michal Vasko04210d72018-03-16 12:07:47 +0100667 }
668 }
669 return ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100670}
671
672int
David Sedláka3969962018-08-13 10:28:32 +0200673lyht_insert(struct hash_table *ht, void *val_p, uint32_t hash, void **match_p)
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200674{
David Sedláka3969962018-08-13 10:28:32 +0200675 return lyht_insert_with_resize_cb(ht, val_p, hash, NULL, match_p);
Michal Vaskoa1db4be2018-08-07 11:30:43 +0200676}
677
678int
Michal Vasko419fce02018-03-21 11:55:09 +0100679lyht_remove(struct hash_table *ht, void *val_p, uint32_t hash)
Michal Vasko6c810702018-03-14 16:23:21 +0100680{
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200681 struct ht_rec *rec, *crec;
682 int32_t i;
Michal Vasko24affa02018-04-03 09:06:06 +0200683 int first_matched = 0, r, ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100684
Michal Vasko24affa02018-04-03 09:06:06 +0200685 if (lyht_find_first(ht, hash, &rec)) {
686 /* hash not found */
Michal Vasko6c810702018-03-14 16:23:21 +0100687 return 1;
688 }
Michal Vasko78548832018-06-04 12:07:52 +0200689 if ((rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko24affa02018-04-03 09:06:06 +0200690 /* even the value matches */
691 first_matched = 1;
692 }
Michal Vasko6c810702018-03-14 16:23:21 +0100693
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200694 /* we always need to go through collisions */
695 crec = rec;
696 for (i = 1; i < crec->hits; ++i) {
Michal Vasko1de7bd12018-05-21 14:27:02 +0200697 r = lyht_find_collision(ht, &rec, crec);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200698 assert(!r);
Michal Vasko419fce02018-03-21 11:55:09 +0100699
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200700 /* compare values */
Michal Vasko78548832018-06-04 12:07:52 +0200701 if (!first_matched && (rec->hash == hash) && ht->val_equal(val_p, &rec->val, 1, ht->cb_data)) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200702 break;
703 }
Michal Vasko419fce02018-03-21 11:55:09 +0100704 }
705
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200706 if (i < crec->hits) {
707 /* one of collisions matched, reduce collision count, remove the record */
Michal Vasko24affa02018-04-03 09:06:06 +0200708 assert(!first_matched);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200709 --crec->hits;
710 rec->hits = -1;
Michal Vasko24affa02018-04-03 09:06:06 +0200711 } else if (first_matched) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200712 /* the first record matches */
713 if (crec != rec) {
714 /* ... so put the last collision in its place */
715 rec->hits = crec->hits - 1;
716 memcpy(crec, rec, ht->rec_size);
717 }
718 rec->hits = -1;
719 } else {
720 /* value not found even in collisions */
Michal Vasko24affa02018-04-03 09:06:06 +0200721 assert(!first_matched);
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200722 return 1;
723 }
724
Michal Vasko419fce02018-03-21 11:55:09 +0100725 /* check size & shrink if needed */
726 ret = 0;
727 --ht->used;
728 if (ht->resize == 2) {
Michal Vasko7d3ad5a2018-03-27 14:22:19 +0200729 r = (ht->used * 100) / ht->size;
730 if ((r < LYHT_SHRINK_PERCENTAGE) && (ht->size > LYHT_MIN_SIZE)) {
Michal Vasko419fce02018-03-21 11:55:09 +0100731 /* shrink */
732 ret = lyht_resize(ht, 0);
Michal Vasko6c810702018-03-14 16:23:21 +0100733 }
Michal Vasko419fce02018-03-21 11:55:09 +0100734 }
Michal Vasko6c810702018-03-14 16:23:21 +0100735
Michal Vasko419fce02018-03-21 11:55:09 +0100736 return ret;
Michal Vasko6c810702018-03-14 16:23:21 +0100737}