summaryrefslogtreecommitdiff
path: root/lib/console/argtable3/arg_hashtable.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/console/argtable3/arg_hashtable.c')
-rw-r--r--lib/console/argtable3/arg_hashtable.c428
1 files changed, 428 insertions, 0 deletions
diff --git a/lib/console/argtable3/arg_hashtable.c b/lib/console/argtable3/arg_hashtable.c
new file mode 100644
index 00000000..1e8a2d38
--- /dev/null
+++ b/lib/console/argtable3/arg_hashtable.c
@@ -0,0 +1,428 @@
+/*
+ * SPDX-FileCopyrightText: 2013-2019 Tom G. Huang
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+/*******************************************************************************
+ * arg_hashtable: Implements the hash table utilities
+ *
+ * This file is part of the argtable3 library.
+ *
+ * Copyright (C) 2013-2019 Tom G. Huang
+ * <tomghuang@gmail.com>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of STEWART HEITMANN nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL STEWART HEITMANN BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ ******************************************************************************/
+
+#ifndef ARG_AMALGAMATION
+#include "argtable3_private.h"
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * This hash table module is adapted from the C hash table implementation by
+ * Christopher Clark. Here is the copyright notice from the library:
+ *
+ * Copyright (c) 2002, Christopher Clark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of the original author; nor the names of any contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+ * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Credit for primes table: Aaron Krowne
+ * http://br.endernet.org/~akrowne/
+ * http://planetmath.org/encyclopedia/GoodHashTablePrimes.html
+ */
+static const unsigned int primes[] = {53, 97, 193, 389, 769, 1543, 3079, 6151, 12289,
+ 24593, 49157, 98317, 196613, 393241, 786433, 1572869, 3145739, 6291469,
+ 12582917, 25165843, 50331653, 100663319, 201326611, 402653189, 805306457, 1610612741};
+const unsigned int prime_table_length = sizeof(primes) / sizeof(primes[0]);
+const float max_load_factor = (float)0.65;
+
+static unsigned int enhanced_hash(arg_hashtable_t* h, const void* k) {
+ /*
+ * Aim to protect against poor hash functions by adding logic here.
+ * The logic is taken from Java 1.4 hash table source.
+ */
+ unsigned int i = h->hashfn(k);
+ i += ~(i << 9);
+ i ^= ((i >> 14) | (i << 18)); /* >>> */
+ i += (i << 4);
+ i ^= ((i >> 10) | (i << 22)); /* >>> */
+ return i;
+}
+
+static unsigned int index_for(unsigned int tablelength, unsigned int hashvalue) {
+ return (hashvalue % tablelength);
+}
+
+arg_hashtable_t* arg_hashtable_create(unsigned int minsize, unsigned int (*hashfn)(const void*), int (*eqfn)(const void*, const void*)) {
+ arg_hashtable_t* h;
+ unsigned int pindex;
+ unsigned int size = primes[0];
+
+ /* Check requested hash table isn't too large */
+ if (minsize > (1u << 30))
+ return NULL;
+
+ /*
+ * Enforce size as prime. The reason is to avoid clustering of values
+ * into a small number of buckets (yes, distribution). A more even
+ * distributed hash table will perform more consistently.
+ */
+ for (pindex = 0; pindex < prime_table_length; pindex++) {
+ if (primes[pindex] > minsize) {
+ size = primes[pindex];
+ break;
+ }
+ }
+
+ h = (arg_hashtable_t*)xmalloc(sizeof(arg_hashtable_t));
+ h->table = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * size);
+ memset(h->table, 0, size * sizeof(struct arg_hashtable_entry*));
+ h->tablelength = size;
+ h->primeindex = pindex;
+ h->entrycount = 0;
+ h->hashfn = hashfn;
+ h->eqfn = eqfn;
+ h->loadlimit = (unsigned int)ceil(size * (double)max_load_factor);
+ return h;
+}
+
+static int arg_hashtable_expand(arg_hashtable_t* h) {
+ /* Double the size of the table to accommodate more entries */
+ struct arg_hashtable_entry** newtable;
+ struct arg_hashtable_entry* e;
+ unsigned int newsize;
+ unsigned int i;
+ unsigned int index;
+
+ /* Check we're not hitting max capacity */
+ if (h->primeindex == (prime_table_length - 1))
+ return 0;
+ newsize = primes[++(h->primeindex)];
+
+ newtable = (struct arg_hashtable_entry**)xmalloc(sizeof(struct arg_hashtable_entry*) * newsize);
+ memset(newtable, 0, newsize * sizeof(struct arg_hashtable_entry*));
+ /*
+ * This algorithm is not 'stable': it reverses the list
+ * when it transfers entries between the tables
+ */
+ for (i = 0; i < h->tablelength; i++) {
+ while (NULL != (e = h->table[i])) {
+ h->table[i] = e->next;
+ index = index_for(newsize, e->h);
+ e->next = newtable[index];
+ newtable[index] = e;
+ }
+ }
+
+ xfree(h->table);
+ h->table = newtable;
+ h->tablelength = newsize;
+ h->loadlimit = (unsigned int)ceil(newsize * (double)max_load_factor);
+ return -1;
+}
+
+unsigned int arg_hashtable_count(arg_hashtable_t* h) {
+ return h->entrycount;
+}
+
+void arg_hashtable_insert(arg_hashtable_t* h, void* k, void* v) {
+ /* This method allows duplicate keys - but they shouldn't be used */
+ unsigned int index;
+ struct arg_hashtable_entry* e;
+ if ((h->entrycount + 1) > h->loadlimit) {
+ /*
+ * Ignore the return value. If expand fails, we should
+ * still try cramming just this value into the existing table
+ * -- we may not have memory for a larger table, but one more
+ * element may be ok. Next time we insert, we'll try expanding again.
+ */
+ arg_hashtable_expand(h);
+ }
+ e = (struct arg_hashtable_entry*)xmalloc(sizeof(struct arg_hashtable_entry));
+ e->h = enhanced_hash(h, k);
+ index = index_for(h->tablelength, e->h);
+ e->k = k;
+ e->v = v;
+ e->next = h->table[index];
+ h->table[index] = e;
+ h->entrycount++;
+}
+
+void* arg_hashtable_search(arg_hashtable_t* h, const void* k) {
+ struct arg_hashtable_entry* e;
+ unsigned int hashvalue;
+ unsigned int index;
+
+ hashvalue = enhanced_hash(h, k);
+ index = index_for(h->tablelength, hashvalue);
+ e = h->table[index];
+ while (e != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k)))
+ return e->v;
+ e = e->next;
+ }
+ return NULL;
+}
+
+void arg_hashtable_remove(arg_hashtable_t* h, const void* k) {
+ /*
+ * TODO: consider compacting the table when the load factor drops enough,
+ * or provide a 'compact' method.
+ */
+
+ struct arg_hashtable_entry* e;
+ struct arg_hashtable_entry** pE;
+ unsigned int hashvalue;
+ unsigned int index;
+
+ hashvalue = enhanced_hash(h, k);
+ index = index_for(h->tablelength, hashvalue);
+ pE = &(h->table[index]);
+ e = *pE;
+ while (NULL != e) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) {
+ *pE = e->next;
+ h->entrycount--;
+ xfree(e->k);
+ xfree(e->v);
+ xfree(e);
+ return;
+ }
+ pE = &(e->next);
+ e = e->next;
+ }
+}
+
+void arg_hashtable_destroy(arg_hashtable_t* h, int free_values) {
+ unsigned int i;
+ struct arg_hashtable_entry *e, *f;
+ struct arg_hashtable_entry** table = h->table;
+ if (free_values) {
+ for (i = 0; i < h->tablelength; i++) {
+ e = table[i];
+ while (NULL != e) {
+ f = e;
+ e = e->next;
+ xfree(f->k);
+ xfree(f->v);
+ xfree(f);
+ }
+ }
+ } else {
+ for (i = 0; i < h->tablelength; i++) {
+ e = table[i];
+ while (NULL != e) {
+ f = e;
+ e = e->next;
+ xfree(f->k);
+ xfree(f);
+ }
+ }
+ }
+ xfree(h->table);
+ xfree(h);
+}
+
+arg_hashtable_itr_t* arg_hashtable_itr_create(arg_hashtable_t* h) {
+ unsigned int i;
+ unsigned int tablelength;
+
+ arg_hashtable_itr_t* itr = (arg_hashtable_itr_t*)xmalloc(sizeof(arg_hashtable_itr_t));
+ itr->h = h;
+ itr->e = NULL;
+ itr->parent = NULL;
+ tablelength = h->tablelength;
+ itr->index = tablelength;
+ if (0 == h->entrycount)
+ return itr;
+
+ for (i = 0; i < tablelength; i++) {
+ if (h->table[i] != NULL) {
+ itr->e = h->table[i];
+ itr->index = i;
+ break;
+ }
+ }
+ return itr;
+}
+
+void arg_hashtable_itr_destroy(arg_hashtable_itr_t* itr) {
+ xfree(itr);
+}
+
+void* arg_hashtable_itr_key(arg_hashtable_itr_t* i) {
+ return i->e->k;
+}
+
+void* arg_hashtable_itr_value(arg_hashtable_itr_t* i) {
+ return i->e->v;
+}
+
+int arg_hashtable_itr_advance(arg_hashtable_itr_t* itr) {
+ unsigned int j;
+ unsigned int tablelength;
+ struct arg_hashtable_entry** table;
+ struct arg_hashtable_entry* next;
+
+ if (itr->e == NULL)
+ return 0; /* stupidity check */
+
+ next = itr->e->next;
+ if (NULL != next) {
+ itr->parent = itr->e;
+ itr->e = next;
+ return -1;
+ }
+
+ tablelength = itr->h->tablelength;
+ itr->parent = NULL;
+ if (tablelength <= (j = ++(itr->index))) {
+ itr->e = NULL;
+ return 0;
+ }
+
+ table = itr->h->table;
+ while (NULL == (next = table[j])) {
+ if (++j >= tablelength) {
+ itr->index = tablelength;
+ itr->e = NULL;
+ return 0;
+ }
+ }
+
+ itr->index = j;
+ itr->e = next;
+ return -1;
+}
+
+int arg_hashtable_itr_remove(arg_hashtable_itr_t* itr) {
+ struct arg_hashtable_entry* remember_e;
+ struct arg_hashtable_entry* remember_parent;
+ int ret;
+
+ /* Do the removal */
+ if ((itr->parent) == NULL) {
+ /* element is head of a chain */
+ itr->h->table[itr->index] = itr->e->next;
+ } else {
+ /* element is mid-chain */
+ itr->parent->next = itr->e->next;
+ }
+ /* itr->e is now outside the hashtable */
+ remember_e = itr->e;
+ itr->h->entrycount--;
+ xfree(remember_e->k);
+ xfree(remember_e->v);
+
+ /* Advance the iterator, correcting the parent */
+ remember_parent = itr->parent;
+ ret = arg_hashtable_itr_advance(itr);
+ if (itr->parent == remember_e) {
+ itr->parent = remember_parent;
+ }
+ xfree(remember_e);
+ return ret;
+}
+
+int arg_hashtable_itr_search(arg_hashtable_itr_t* itr, arg_hashtable_t* h, void* k) {
+ struct arg_hashtable_entry* e;
+ struct arg_hashtable_entry* parent;
+ unsigned int hashvalue;
+ unsigned int index;
+
+ hashvalue = enhanced_hash(h, k);
+ index = index_for(h->tablelength, hashvalue);
+
+ e = h->table[index];
+ parent = NULL;
+ while (e != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) {
+ itr->index = index;
+ itr->e = e;
+ itr->parent = parent;
+ itr->h = h;
+ return -1;
+ }
+ parent = e;
+ e = e->next;
+ }
+ return 0;
+}
+
+int arg_hashtable_change(arg_hashtable_t* h, void* k, void* v) {
+ struct arg_hashtable_entry* e;
+ unsigned int hashvalue;
+ unsigned int index;
+
+ hashvalue = enhanced_hash(h, k);
+ index = index_for(h->tablelength, hashvalue);
+ e = h->table[index];
+ while (e != NULL) {
+ /* Check hash value to short circuit heavier comparison */
+ if ((hashvalue == e->h) && (h->eqfn(k, e->k))) {
+ xfree(e->v);
+ e->v = v;
+ return -1;
+ }
+ e = e->next;
+ }
+ return 0;
+}