diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-06-27 16:12:18 +1000 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-06-27 16:12:18 +1000 |
| commit | 1036f1b00efe2bbd2467cbfa3a4a97ab7f56591e (patch) | |
| tree | cc3aa8683daa30e36f3777699e32aaf93e211f95 /lib/console/argtable3/arg_hashtable.c | |
| parent | 1daa1e9b0fe737ea5dac99f42fdf3c217873443f (diff) | |
| download | tangara-fw-1036f1b00efe2bbd2467cbfa3a4a97ab7f56591e.tar.gz | |
fork esp-idf's dev console, in order to add some hooks
Diffstat (limited to 'lib/console/argtable3/arg_hashtable.c')
| -rw-r--r-- | lib/console/argtable3/arg_hashtable.c | 428 |
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; +} |
