mirror of
				https://github.com/espressif/esp-idf.git
				synced 2025-11-03 22:08:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			429 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*
 | 
						|
 * 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;
 | 
						|
}
 |