/* * The MIT License (MIT) * * Copyright (c) 2015 Mikkel F. Jørgensen, dvide.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* * This file implements a generic hash interface such that different * instances have the same name, but hidden from each other. * The interface maps the local names to a public specific type. * * This implementations implements a hash table with linear or quadratic * probing. */ #ifdef HASH_TABLE_IMPL #error "cannot have multiple implementations in same compilation unit" #endif #define HASH_TABLE_IMPL /* Open Addressing */ #define HT_OA #if defined(_MSC_VER) #pragma warning(disable: 4127) /* conditional expression is constant */ #endif #include #include #ifndef HT_PROBE #ifdef HT_PROBE_QUADRATIC #define HT_PROBE(k, i, N) ((k + (i + i * i) / 2) & N) #else #define HT_PROBE(k, i, N) ((k + i) & N) #endif #endif static int ht_init(hash_table_t *ht, size_t count) { size_t buckets = 4; if ((HT_LOAD_FACTOR_FRAC) > 256 || (HT_LOAD_FACTOR_FRAC) < 1) { /* * 100% is bad but still the users choice. * 101% will never terminate insertion. */ HT_PANIC("hash table failed with impossible load factor"); return -1; } while (count > buckets * (HT_LOAD_FACTOR_FRAC) / 256) { buckets *= 2; } ht->table = calloc(buckets, sizeof(ht_item_t)); if (ht->table == 0) { return -1; } ht->offsets = 0; ht->buckets = buckets; ht->count = 0; return 0; } static int ht_resize(hash_table_t *ht, size_t count) { size_t i; hash_table_t ht2; ht_item_t *T = ht->table; void *item; if (count < ht->count) { count = ht->count; } if (ht_init(&ht2, count)) { return -1; } for (i = 0; i < ht->buckets; ++i) { item = T[i]; if ((item && item != HT_DELETED)) { ht_insert(&ht2, ht_key(item), ht_key_len(item), item, ht_multi); } } ht_clear(ht); memcpy(ht, &ht2, sizeof(*ht)); return 0; } static ht_item_t ht_insert(hash_table_t *ht, const void *key, size_t len, ht_item_t new_item, int mode) { ht_item_t *T; size_t N, i, j, k; ht_item_t item, *vacant = 0; assert(new_item != HT_MISSING); assert(new_item != HT_DELETED); assert(new_item != HT_NOMEM); if (ht->count >= ht->buckets * (HT_LOAD_FACTOR_FRAC) / 256) { if (ht_resize(ht, ht->count * 2)) { HT_PANIC("hash table failed to allocate memory during resize"); return HT_NOMEM; } } T = ht->table; N = ht->buckets - 1; k = HT_HASH_FUNCTION(key, len); i = 0; j = HT_PROBE(k, i, N); if (mode == ht_unique || mode == ht_multi) { ++ht->count; while (T[j] && T[j] != HT_DELETED) { ++i; j = HT_PROBE(k, i, N); } T[j] = new_item; return 0; } while ((item = T[j])) { if (item == HT_DELETED) { if (vacant == 0) { /* * If a tombstone was found, use the first available, * but continue search for possible match. */ vacant = &T[j]; } } else if (ht_match(key, len, item)) { if (mode == ht_replace) { T[j] = new_item; } return item; } ++i; j = HT_PROBE(k, i, N); } if (vacant == 0) { vacant = &T[j]; } ++ht->count; *vacant = new_item; return 0; } static ht_item_t ht_find(hash_table_t *ht, const void *key, size_t len) { ht_item_t *T = ht->table; size_t N, i, j, k; ht_item_t item; if (T == 0) { return 0; } N = ht->buckets - 1; k = HT_HASH_FUNCTION(key, len); i = 0; j = HT_PROBE(k, i, N); while ((item = T[j])) { if ((item != HT_DELETED) && ht_match(key, len, item)) { return item; } ++i; j = HT_PROBE(k, i, N); } return 0; } static ht_item_t ht_remove(hash_table_t *ht, const void *key, size_t len) { ht_item_t *T = ht->table; size_t N, i, j, k; ht_item_t item; if (T == 0) { return 0; } N = ht->buckets - 1; k = HT_HASH_FUNCTION(key, len); i = 0; j = HT_PROBE(k, i, N); while ((item = T[j])) { if (item != HT_DELETED && ht_match(key, len, item)) { T[j] = HT_DELETED; --ht->count; return item; } ++i; j = HT_PROBE(k, i, N); } return 0; } static void ht_visit(hash_table_t *ht, ht_visitor_f *visitor, void *context) { size_t i; ht_item_t *T = ht->table; ht_item_t item; for (i = 0; i < ht->buckets; ++i) { item = T[i]; if (item && item != HT_DELETED) { visitor(context, item); } } } static void ht_clear(hash_table_t *ht) { if (ht->table) { free(ht->table); } memset(ht, 0, sizeof(*ht)); }