mirror of
https://github.com/DrBeef/QuestZDoom.git
synced 2025-03-05 17:10:55 +00:00
388 lines
10 KiB
C
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;
|
|
}
|