questzdoom/Projects/Android/jni/SupportLibs/fluidsynth/fluid_hash.c
2021-04-20 21:09:02 +01:00

388 lines
10 KiB
C

/* GLIB - Library of useful routines for C programming
* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
/*
* Modified by the GLib Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GLib Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GLib at ftp://ftp.gtk.org/pub/gtk/.
*/
/*
* MT safe
*/
#include "fluidsynth_priv.h"
#include "fluid_hash.h"
#define HASH_TABLE_MIN_SIZE 7
#define HASH_TABLE_MAX_SIZE 13845163
typedef struct _fluid_hashnode_t fluid_hashnode_t;
struct _fluid_hashnode_t {
char* key;
void* value;
int type;
fluid_hashnode_t *next;
};
static fluid_hashnode_t* new_fluid_hashnode(char* key, void* value, int type);
static void delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
static void delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del);
struct _fluid_hashtable_t {
unsigned int size;
unsigned int nnodes;
fluid_hashnode_t **nodes;
fluid_hash_delete_t del;
};
#define FLUID_HASHTABLE_RESIZE(hash_table) \
if ((3 * hash_table->size <= hash_table->nnodes) \
&& (hash_table->size < HASH_TABLE_MAX_SIZE)) { \
fluid_hashtable_resize(hash_table); \
}
static void fluid_hashtable_resize(fluid_hashtable_t *hash_table);
static fluid_hashnode_t** fluid_hashtable_lookup_node(fluid_hashtable_t *hash_table, char* key);
/**
* new_fluid_hashtable:
*
* Creates a new #fluid_hashtable_t.
*
* Return value: a new #fluid_hashtable_t.
**/
fluid_hashtable_t*
new_fluid_hashtable(fluid_hash_delete_t del)
{
fluid_hashtable_t *hash_table;
unsigned int i;
hash_table = FLUID_NEW(fluid_hashtable_t);
hash_table->size = HASH_TABLE_MIN_SIZE;
hash_table->nnodes = 0;
hash_table->nodes = FLUID_ARRAY(fluid_hashnode_t*, hash_table->size);
hash_table->del = del;
for (i = 0; i < hash_table->size; i++) {
hash_table->nodes[i] = NULL;
}
return hash_table;
}
/**
* delete_fluid_hashtable:
* @hash_table: a #fluid_hashtable_t.
*
* Destroys the #fluid_hashtable_t. If keys and/or values are dynamically
* allocated, you should either free them first or create the #fluid_hashtable_t
* using fluid_hashtable_new_full(). In the latter case the destroy functions
* you supplied will be called on all keys and values before destroying
* the #fluid_hashtable_t.
**/
void
delete_fluid_hashtable(fluid_hashtable_t *hash_table)
{
unsigned int i;
if (hash_table == NULL) {
return;
}
for (i = 0; i < hash_table->size; i++) {
delete_fluid_hashnodes(hash_table->nodes[i], hash_table->del);
}
FLUID_FREE(hash_table->nodes);
FLUID_FREE(hash_table);
}
static /*inline*/ fluid_hashnode_t**
fluid_hashtable_lookup_node (fluid_hashtable_t* hash_table, char* key)
{
fluid_hashnode_t **node;
node = &hash_table->nodes[fluid_str_hash(key) % hash_table->size];
while (*node && (FLUID_STRCMP((*node)->key, key) != 0)) {
node = &(*node)->next;
}
return node;
}
/**
* fluid_hashtable_lookup:
* @hash_table: a #fluid_hashtable_t.
* @key: the key to look up.
*
* Looks up a key in a #fluid_hashtable_t.
*
* Return value: the associated value, or %NULL if the key is not found.
**/
int
fluid_hashtable_lookup(fluid_hashtable_t *hash_table, char* key, void** value, int* type)
{
fluid_hashnode_t *node;
node = *fluid_hashtable_lookup_node(hash_table, key);
if (node) {
if (value) {
*value = node->value;
}
if (type) {
*type = node->type;
}
return 1;
} else {
return 0;
}
}
/**
* fluid_hashtable_insert:
* @hash_table: a #fluid_hashtable_t.
* @key: a key to insert.
* @value: the value to associate with the key.
*
* Inserts a new key and value into a #fluid_hashtable_t.
*
* If the key already exists in the #fluid_hashtable_t its current value is replaced
* with the new value. If you supplied a @value_destroy_func when creating the
* #fluid_hashtable_t, the old value is freed using that function. If you supplied
* a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed
* using that function.
**/
void
fluid_hashtable_insert(fluid_hashtable_t *hash_table, char* key, void* value, int type)
{
fluid_hashnode_t **node;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
(*node)->value = value;
(*node)->type = type;
} else {
*node = new_fluid_hashnode(key, value, type);
hash_table->nnodes++;
FLUID_HASHTABLE_RESIZE(hash_table);
}
}
/**
* fluid_hashtable_replace:
* @hash_table: a #GHashTable.
* @key: a key to insert.
* @value: the value to associate with the key.
*
* Inserts a new key and value into a #GHashTable similar to
* fluid_hashtable_insert(). The difference is that if the key already exists
* in the #GHashTable, it gets replaced by the new key. If you supplied a
* @value_destroy_func when creating the #GHashTable, the old value is freed
* using that function. If you supplied a @key_destroy_func when creating the
* #GHashTable, the old key is freed using that function.
**/
void
fluid_hashtable_replace(fluid_hashtable_t *hash_table, char* key, void* value, int type)
{
fluid_hashnode_t **node;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
if (hash_table->del) {
hash_table->del((*node)->value, (*node)->type);
}
(*node)->value = value;
} else {
*node = new_fluid_hashnode(key, value, type);
hash_table->nnodes++;
FLUID_HASHTABLE_RESIZE(hash_table);
}
}
/**
* fluid_hashtable_remove:
* @hash_table: a #fluid_hashtable_t.
* @key: the key to remove.
*
* Removes a key and its associated value from a #fluid_hashtable_t.
*
* If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the
* key and value are freed using the supplied destroy functions, otherwise
* you have to make sure that any dynamically allocated values are freed
* yourself.
*
* Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.
**/
int
fluid_hashtable_remove (fluid_hashtable_t *hash_table, char* key)
{
fluid_hashnode_t **node, *dest;
node = fluid_hashtable_lookup_node(hash_table, key);
if (*node) {
dest = *node;
(*node) = dest->next;
delete_fluid_hashnode(dest, hash_table->del);
hash_table->nnodes--;
FLUID_HASHTABLE_RESIZE (hash_table);
return 1;
}
return 0;
}
/**
* fluid_hashtable_foreach:
* @hash_table: a #fluid_hashtable_t.
* @func: the function to call for each key/value pair.
* @user_data: user data to pass to the function.
*
* Calls the given function for each of the key/value pairs in the
* #fluid_hashtable_t. The function is passed the key and value of each
* pair, and the given @user_data parameter. The hash table may not
* be modified while iterating over it (you can't add/remove
* items). To remove all items matching a predicate, use
* fluid_hashtable_remove().
**/
void
fluid_hashtable_foreach(fluid_hashtable_t *hash_table, fluid_hash_iter_t func, void* data)
{
fluid_hashnode_t *node = NULL;
unsigned int i;
for (i = 0; i < hash_table->size; i++) {
for (node = hash_table->nodes[i]; node != NULL; node = node->next) {
(*func)(node->key, node->value, node->type, data);
}
}
}
/**
* fluid_hashtable_size:
* @hash_table: a #fluid_hashtable_t.
*
* Returns the number of elements contained in the #fluid_hashtable_t.
*
* Return value: the number of key/value pairs in the #fluid_hashtable_t.
**/
unsigned int
fluid_hashtable_size(fluid_hashtable_t *hash_table)
{
return hash_table->nnodes;
}
static void
fluid_hashtable_resize(fluid_hashtable_t *hash_table)
{
fluid_hashnode_t **new_nodes;
fluid_hashnode_t *node;
fluid_hashnode_t *next;
unsigned int hash_val;
int new_size;
unsigned int i;
new_size = 3 * hash_table->size + 1;
new_size = (new_size > HASH_TABLE_MAX_SIZE)? HASH_TABLE_MAX_SIZE : new_size;
/* printf("%s: %d: resizing, new size = %d\n", __FILE__, __LINE__, new_size); */
new_nodes = FLUID_ARRAY(fluid_hashnode_t*, new_size);
FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t*));
for (i = 0; i < hash_table->size; i++) {
for (node = hash_table->nodes[i]; node; node = next) {
next = node->next;
hash_val = fluid_str_hash(node->key) % new_size;
node->next = new_nodes[hash_val];
new_nodes[hash_val] = node;
}
}
FLUID_FREE(hash_table->nodes);
hash_table->nodes = new_nodes;
hash_table->size = new_size;
}
static fluid_hashnode_t*
new_fluid_hashnode(char* key, void* value, int type)
{
fluid_hashnode_t *hash_node;
hash_node = FLUID_NEW(fluid_hashnode_t);
hash_node->key = FLUID_STRDUP(key);
hash_node->value = value;
hash_node->type = type;
hash_node->next = NULL;
return hash_node;
}
static void
delete_fluid_hashnode(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
{
if (del) {
(*del)(hash_node->value, hash_node->type);
}
if (hash_node->key) {
FLUID_FREE(hash_node->key);
}
FLUID_FREE(hash_node);
}
static void
delete_fluid_hashnodes(fluid_hashnode_t *hash_node, fluid_hash_delete_t del)
{
while (hash_node) {
fluid_hashnode_t *next = hash_node->next;
delete_fluid_hashnode(hash_node, del);
hash_node = next;
}
}
/* 31 bit hash function */
unsigned int
fluid_str_hash(char* key)
{
char *p = key;
unsigned int h = *p;
if (h) {
for (p += 1; *p != '\0'; p++) {
h = (h << 5) - h + *p;
}
}
return h;
}