diff options
Diffstat (limited to 'lib/isc/ht.c')
-rw-r--r-- | lib/isc/ht.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/lib/isc/ht.c b/lib/isc/ht.c new file mode 100644 index 0000000..07a36b4 --- /dev/null +++ b/lib/isc/ht.c @@ -0,0 +1,342 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * SPDX-License-Identifier: MPL-2.0 + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include <inttypes.h> +#include <string.h> + +#include <isc/hash.h> +#include <isc/ht.h> +#include <isc/magic.h> +#include <isc/mem.h> +#include <isc/result.h> +#include <isc/types.h> +#include <isc/util.h> + +typedef struct isc_ht_node isc_ht_node_t; + +#define ISC_HT_MAGIC ISC_MAGIC('H', 'T', 'a', 'b') +#define ISC_HT_VALID(ht) ISC_MAGIC_VALID(ht, ISC_HT_MAGIC) + +struct isc_ht_node { + void *value; + isc_ht_node_t *next; + size_t keysize; + unsigned char key[FLEXIBLE_ARRAY_MEMBER]; +}; + +struct isc_ht { + unsigned int magic; + isc_mem_t *mctx; + size_t size; + size_t mask; + unsigned int count; + isc_ht_node_t **table; +}; + +struct isc_ht_iter { + isc_ht_t *ht; + size_t i; + isc_ht_node_t *cur; +}; + +void +isc_ht_init(isc_ht_t **htp, isc_mem_t *mctx, uint8_t bits) { + isc_ht_t *ht = NULL; + size_t i; + + REQUIRE(htp != NULL && *htp == NULL); + REQUIRE(mctx != NULL); + REQUIRE(bits >= 1 && bits <= (sizeof(size_t) * 8 - 1)); + + ht = isc_mem_get(mctx, sizeof(struct isc_ht)); + + ht->mctx = NULL; + isc_mem_attach(mctx, &ht->mctx); + + ht->size = ((size_t)1 << bits); + ht->mask = ((size_t)1 << bits) - 1; + ht->count = 0; + + ht->table = isc_mem_get(ht->mctx, ht->size * sizeof(isc_ht_node_t *)); + + for (i = 0; i < ht->size; i++) { + ht->table[i] = NULL; + } + + ht->magic = ISC_HT_MAGIC; + + *htp = ht; +} + +void +isc_ht_destroy(isc_ht_t **htp) { + isc_ht_t *ht; + size_t i; + + REQUIRE(htp != NULL); + + ht = *htp; + *htp = NULL; + + REQUIRE(ISC_HT_VALID(ht)); + + ht->magic = 0; + + for (i = 0; i < ht->size; i++) { + isc_ht_node_t *node = ht->table[i]; + while (node != NULL) { + isc_ht_node_t *next = node->next; + ht->count--; + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + + node->keysize); + node = next; + } + } + + INSIST(ht->count == 0); + + isc_mem_put(ht->mctx, ht->table, ht->size * sizeof(isc_ht_node_t *)); + isc_mem_putanddetach(&ht->mctx, ht, sizeof(struct isc_ht)); +} + +isc_result_t +isc_ht_add(isc_ht_t *ht, const unsigned char *key, uint32_t keysize, + void *value) { + isc_ht_node_t *node; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + + hash = isc_hash_function(key, keysize, true); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) + { + return (ISC_R_EXISTS); + } + node = node->next; + } + + node = isc_mem_get(ht->mctx, offsetof(isc_ht_node_t, key) + keysize); + + memmove(node->key, key, keysize); + node->keysize = keysize; + node->next = ht->table[hash & ht->mask]; + node->value = value; + + ht->count++; + ht->table[hash & ht->mask] = node; + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_find(const isc_ht_t *ht, const unsigned char *key, uint32_t keysize, + void **valuep) { + isc_ht_node_t *node; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + REQUIRE(valuep == NULL || *valuep == NULL); + + hash = isc_hash_function(key, keysize, true); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) + { + if (valuep != NULL) { + *valuep = node->value; + } + return (ISC_R_SUCCESS); + } + node = node->next; + } + + return (ISC_R_NOTFOUND); +} + +isc_result_t +isc_ht_delete(isc_ht_t *ht, const unsigned char *key, uint32_t keysize) { + isc_ht_node_t *node, *prev; + uint32_t hash; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(key != NULL && keysize > 0); + + prev = NULL; + hash = isc_hash_function(key, keysize, true); + node = ht->table[hash & ht->mask]; + while (node != NULL) { + if (keysize == node->keysize && + memcmp(key, node->key, keysize) == 0) + { + if (prev == NULL) { + ht->table[hash & ht->mask] = node->next; + } else { + prev->next = node->next; + } + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + + node->keysize); + ht->count--; + + return (ISC_R_SUCCESS); + } + + prev = node; + node = node->next; + } + return (ISC_R_NOTFOUND); +} + +void +isc_ht_iter_create(isc_ht_t *ht, isc_ht_iter_t **itp) { + isc_ht_iter_t *it; + + REQUIRE(ISC_HT_VALID(ht)); + REQUIRE(itp != NULL && *itp == NULL); + + it = isc_mem_get(ht->mctx, sizeof(isc_ht_iter_t)); + + it->ht = ht; + it->i = 0; + it->cur = NULL; + + *itp = it; +} + +void +isc_ht_iter_destroy(isc_ht_iter_t **itp) { + isc_ht_iter_t *it; + isc_ht_t *ht; + + REQUIRE(itp != NULL && *itp != NULL); + + it = *itp; + *itp = NULL; + ht = it->ht; + isc_mem_put(ht->mctx, it, sizeof(isc_ht_iter_t)); +} + +isc_result_t +isc_ht_iter_first(isc_ht_iter_t *it) { + REQUIRE(it != NULL); + + it->i = 0; + while (it->i < it->ht->size && it->ht->table[it->i] == NULL) { + it->i++; + } + + if (it->i == it->ht->size) { + return (ISC_R_NOMORE); + } + + it->cur = it->ht->table[it->i]; + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_iter_next(isc_ht_iter_t *it) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + + it->cur = it->cur->next; + if (it->cur == NULL) { + do { + it->i++; + } while (it->i < it->ht->size && it->ht->table[it->i] == NULL); + if (it->i >= it->ht->size) { + return (ISC_R_NOMORE); + } + it->cur = it->ht->table[it->i]; + } + + return (ISC_R_SUCCESS); +} + +isc_result_t +isc_ht_iter_delcurrent_next(isc_ht_iter_t *it) { + isc_result_t result = ISC_R_SUCCESS; + isc_ht_node_t *to_delete = NULL; + isc_ht_node_t *prev = NULL; + isc_ht_node_t *node = NULL; + uint32_t hash; + isc_ht_t *ht; + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + to_delete = it->cur; + ht = it->ht; + + it->cur = it->cur->next; + if (it->cur == NULL) { + do { + it->i++; + } while (it->i < ht->size && ht->table[it->i] == NULL); + if (it->i >= ht->size) { + result = ISC_R_NOMORE; + } else { + it->cur = ht->table[it->i]; + } + } + + hash = isc_hash_function(to_delete->key, to_delete->keysize, true); + node = ht->table[hash & ht->mask]; + while (node != to_delete) { + prev = node; + node = node->next; + INSIST(node != NULL); + } + + if (prev == NULL) { + ht->table[hash & ht->mask] = node->next; + } else { + prev->next = node->next; + } + isc_mem_put(ht->mctx, node, + offsetof(isc_ht_node_t, key) + node->keysize); + ht->count--; + + return (result); +} + +void +isc_ht_iter_current(isc_ht_iter_t *it, void **valuep) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + REQUIRE(valuep != NULL && *valuep == NULL); + + *valuep = it->cur->value; +} + +void +isc_ht_iter_currentkey(isc_ht_iter_t *it, unsigned char **key, + size_t *keysize) { + REQUIRE(it != NULL); + REQUIRE(it->cur != NULL); + REQUIRE(key != NULL && *key == NULL); + + *key = it->cur->key; + *keysize = it->cur->keysize; +} + +unsigned int +isc_ht_count(isc_ht_t *ht) { + REQUIRE(ISC_HT_VALID(ht)); + + return (ht->count); +} |