Path: blob/master/libs/fluidsynth/src/utils/fluid_hash.c
4396 views
/* GLIB - Library of useful routines for C programming1* Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald2*3* This library is free software; you can redistribute it and/or4* modify it under the terms of the GNU Lesser General Public5* License as published by the Free Software Foundation; either6* version 2 of the License, or (at your option) any later version.7*8* This library is distributed in the hope that it will be useful,9* but WITHOUT ANY WARRANTY; without even the implied warranty of10* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU11* Lesser General Public License for more details.12*13* You should have received a copy of the GNU Lesser General Public14* License along with this library; if not, write to the15* Free Software Foundation, Inc., 59 Temple Place - Suite 330,16* Boston, MA 02110-1301, USA.17*/1819/*20* Modified by the GLib Team and others 1997-2000. See the AUTHORS21* file for a list of people on the GLib Team. See the ChangeLog22* files for a list of changes. These files are distributed with23* GLib at ftp://ftp.gtk.org/pub/gtk/.24*25* Adapted for FluidSynth use by Josh Green <[email protected]>26* September 8, 2009 from glib 2.18.427*/2829/*30* MT safe31*/3233#include "fluid_sys.h"34#include "fluid_hash.h"35#include "fluid_list.h"363738#define HASH_TABLE_MIN_SIZE 1139#define HASH_TABLE_MAX_SIZE 13845163404142typedef struct43{44fluid_hashtable_t *hashtable;45fluid_hashnode_t *prev_node;46fluid_hashnode_t *node;47int position;48int pre_advanced; // Boolean49int version;50} RealIter;515253/* Excerpt from glib gprimes.c */5455static const unsigned int primes[] =56{5711,5819,5937,6073,61109,62163,63251,64367,65557,66823,671237,681861,692777,704177,716247,729371,7314057,7421089,7531627,7647431,7771143,78106721,79160073,80240101,81360163,82540217,83810343,841215497,851823231,862734867,874102283,886153409,899230113,9013845163,91};9293static const unsigned int nprimes = FLUID_N_ELEMENTS(primes);9495static unsigned int96spaced_primes_closest(unsigned int num)97{98unsigned int i;99100for(i = 0; i < nprimes; i++)101{102if(primes[i] > num)103{104return primes[i];105}106}107108return primes[nprimes - 1];109}110111/* End excerpt from glib gprimes.c */112113114/*115* @hashtable: our #fluid_hashtable_t116* @key: the key to lookup against117* @hash_return: optional key hash return location118* Return value: a pointer to the described #fluid_hashnode_t pointer119*120* Performs a lookup in the hash table. Virtually all hash operations121* will use this function internally.122*123* This function first computes the hash value of the key using the124* user's hash function.125*126* If an entry in the table matching @key is found then this function127* returns a pointer to the pointer to that entry in the table. In128* the case that the entry is at the head of a chain, this pointer129* will be an item in the nodes[] array. In the case that the entry130* is not at the head of a chain, this pointer will be the ->next131* pointer on the node that precedes it.132*133* In the case that no matching entry exists in the table, a pointer134* to a %NULL pointer will be returned. To insert a item, this %NULL135* pointer should be updated to point to the new #fluid_hashnode_t.136*137* If @hash_return is a pass-by-reference parameter. If it is138* non-%NULL then the computed hash value is returned. This is to139* save insertions from having to compute the hash record again for140* the new record.141*/142static FLUID_INLINE fluid_hashnode_t **143fluid_hashtable_lookup_node(fluid_hashtable_t *hashtable, const void *key,144unsigned int *hash_return)145{146fluid_hashnode_t **node_ptr, *node;147unsigned int hash_value;148149hash_value = (* hashtable->hash_func)(key);150node_ptr = &hashtable->nodes[hash_value % hashtable->size];151152if(hash_return)153{154*hash_return = hash_value;155}156157/* Hash table lookup needs to be fast.158* We therefore remove the extra conditional of testing159* whether to call the key_equal_func or not from160* the inner loop.161*162* Additional optimisation: first check if our full hash163* values are equal so we can avoid calling the full-blown164* key equality function in most cases.165*/166if(hashtable->key_equal_func)167{168while((node = *node_ptr))169{170if(node->key_hash == hash_value &&171hashtable->key_equal_func(node->key, key))172{173break;174}175176node_ptr = &(*node_ptr)->next;177}178}179else180{181while((node = *node_ptr))182{183if(node->key == key)184{185break;186}187188node_ptr = &(*node_ptr)->next;189}190}191192return node_ptr;193}194195/*196* @hashtable: our #fluid_hashtable_t197* @node_ptr_ptr: a pointer to the return value from198* fluid_hashtable_lookup_node()199* @notify: %TRUE if the destroy notify handlers are to be called200*201* Removes a node from the hash table and updates the node count. The202* node is freed. No table resize is performed.203*204* If @notify is %TRUE then the destroy notify functions are called205* for the key and value of the hash node.206*207* @node_ptr_ptr is a pass-by-reference in/out parameter. When the208* function is called, it should point to the pointer to the node to209* remove. This level of indirection is required so that the pointer210* may be updated appropriately once the node has been removed.211*212* Before the function returns, the pointer at @node_ptr_ptr will be213* updated to point to the position in the table that contains the214* pointer to the "next" node in the chain. This makes this function215* convenient to use from functions that iterate over the entire216* table. If there is no further item in the chain then the217* #fluid_hashnode_t pointer will be %NULL (ie: **node_ptr_ptr == %NULL).218*219* Since the pointer in the table to the removed node is replaced with220* either a pointer to the next node or a %NULL pointer as221* appropriate, the pointer at the end of @node_ptr_ptr will never be222* modified at all. Stay tuned. :)223*/224static void225fluid_hashtable_remove_node(fluid_hashtable_t *hashtable,226fluid_hashnode_t ***node_ptr_ptr, int notify)227{228fluid_hashnode_t **node_ptr, *node;229230node_ptr = *node_ptr_ptr;231node = *node_ptr;232233*node_ptr = node->next;234235if(notify && hashtable->key_destroy_func)236{237hashtable->key_destroy_func(node->key);238}239240if(notify && hashtable->value_destroy_func)241{242hashtable->value_destroy_func(node->value);243}244245FLUID_FREE(node);246247hashtable->nnodes--;248}249250/*251* fluid_hashtable_remove_all_nodes:252* @hashtable: our #fluid_hashtable_t253* @notify: %TRUE if the destroy notify handlers are to be called254*255* Removes all nodes from the table. Since this may be a precursor to256* freeing the table entirely, no resize is performed.257*258* If @notify is %TRUE then the destroy notify functions are called259* for the key and value of the hash node.260*/261static void262fluid_hashtable_remove_all_nodes(fluid_hashtable_t *hashtable, int notify)263{264fluid_hashnode_t **node_ptr;265int i;266267for(i = 0; i < hashtable->size; i++)268{269for(node_ptr = &hashtable->nodes[i]; *node_ptr != NULL;)270{271fluid_hashtable_remove_node(hashtable, &node_ptr, notify);272}273}274275hashtable->nnodes = 0;276}277278/*279* fluid_hashtable_resize:280* @hashtable: our #fluid_hashtable_t281*282* Resizes the hash table to the optimal size based on the number of283* nodes currently held. If you call this function then a resize will284* occur, even if one does not need to occur. Use285* fluid_hashtable_maybe_resize() instead.286*/287static void288fluid_hashtable_resize(fluid_hashtable_t *hashtable)289{290fluid_hashnode_t **new_nodes;291fluid_hashnode_t *node;292fluid_hashnode_t *next;293unsigned int hash_val;294int new_size;295int i;296297new_size = spaced_primes_closest(hashtable->nnodes);298new_size = (new_size < HASH_TABLE_MIN_SIZE) ? HASH_TABLE_MIN_SIZE :299((new_size > HASH_TABLE_MAX_SIZE) ? HASH_TABLE_MAX_SIZE : new_size);300301new_nodes = FLUID_ARRAY(fluid_hashnode_t *, new_size);302303if(!new_nodes)304{305FLUID_LOG(FLUID_ERR, "Out of memory");306return;307}308309FLUID_MEMSET(new_nodes, 0, new_size * sizeof(fluid_hashnode_t *));310311for(i = 0; i < hashtable->size; i++)312{313for(node = hashtable->nodes[i]; node; node = next)314{315next = node->next;316317hash_val = node->key_hash % new_size;318319node->next = new_nodes[hash_val];320new_nodes[hash_val] = node;321}322}323324FLUID_FREE(hashtable->nodes);325hashtable->nodes = new_nodes;326hashtable->size = new_size;327}328329/*330* fluid_hashtable_maybe_resize:331* @hashtable: our #fluid_hashtable_t332*333* Resizes the hash table, if needed.334*335* Essentially, calls fluid_hashtable_resize() if the table has strayed336* too far from its ideal size for its number of nodes.337*/338static FLUID_INLINE void339fluid_hashtable_maybe_resize(fluid_hashtable_t *hashtable)340{341int nnodes = hashtable->nnodes;342int size = hashtable->size;343344if((size >= 3 * nnodes && size > HASH_TABLE_MIN_SIZE) ||345(3 * size <= nnodes && size < HASH_TABLE_MAX_SIZE))346{347fluid_hashtable_resize(hashtable);348}349}350351/**352* new_fluid_hashtable:353* @hash_func: a function to create a hash value from a key.354* Hash values are used to determine where keys are stored within the355* #fluid_hashtable_t data structure. The fluid_direct_hash(), fluid_int_hash() and356* fluid_str_hash() functions are provided for some common types of keys.357* If hash_func is %NULL, fluid_direct_hash() is used.358* @key_equal_func: a function to check two keys for equality. This is359* used when looking up keys in the #fluid_hashtable_t. The fluid_direct_equal(),360* fluid_int_equal() and fluid_str_equal() functions are provided for the most361* common types of keys. If @key_equal_func is %NULL, keys are compared362* directly in a similar fashion to fluid_direct_equal(), but without the363* overhead of a function call.364*365* Creates a new #fluid_hashtable_t with a reference count of 1.366*367* Return value: a new #fluid_hashtable_t.368**/369fluid_hashtable_t *370new_fluid_hashtable(fluid_hash_func_t hash_func, fluid_equal_func_t key_equal_func)371{372return new_fluid_hashtable_full(hash_func, key_equal_func, NULL, NULL);373}374375376/**377* new_fluid_hashtable_full:378* @hash_func: a function to create a hash value from a key.379* @key_equal_func: a function to check two keys for equality.380* @key_destroy_func: a function to free the memory allocated for the key381* used when removing the entry from the #fluid_hashtable_t or %NULL if you382* don't want to supply such a function.383* @value_destroy_func: a function to free the memory allocated for the384* value used when removing the entry from the #fluid_hashtable_t or %NULL if385* you don't want to supply such a function.386*387* Creates a new #fluid_hashtable_t like fluid_hashtable_new() with a reference count388* of 1 and allows to specify functions to free the memory allocated for the389* key and value that get called when removing the entry from the #fluid_hashtable_t.390*391* Return value: a new #fluid_hashtable_t.392**/393fluid_hashtable_t *394new_fluid_hashtable_full(fluid_hash_func_t hash_func,395fluid_equal_func_t key_equal_func,396fluid_destroy_notify_t key_destroy_func,397fluid_destroy_notify_t value_destroy_func)398{399fluid_hashtable_t *hashtable;400401hashtable = FLUID_NEW(fluid_hashtable_t);402403if(!hashtable)404{405FLUID_LOG(FLUID_ERR, "Out of memory");406return NULL;407}408409hashtable->size = HASH_TABLE_MIN_SIZE;410hashtable->nnodes = 0;411hashtable->hash_func = hash_func ? hash_func : fluid_direct_hash;412hashtable->key_equal_func = key_equal_func;413fluid_atomic_int_set(&hashtable->ref_count, 1);414hashtable->key_destroy_func = key_destroy_func;415hashtable->value_destroy_func = value_destroy_func;416hashtable->nodes = FLUID_ARRAY(fluid_hashnode_t *, hashtable->size);417if(hashtable->nodes == NULL)418{419delete_fluid_hashtable(hashtable);420FLUID_LOG(FLUID_ERR, "Out of memory");421return NULL;422}423FLUID_MEMSET(hashtable->nodes, 0, hashtable->size * sizeof(*hashtable->nodes));424425return hashtable;426}427428/**429* fluid_hashtable_iter_init:430* @iter: an uninitialized #fluid_hashtable_iter_t.431* @hashtable: a #fluid_hashtable_t.432*433* Initializes a key/value pair iterator and associates it with434* @hashtable. Modifying the hash table after calling this function435* invalidates the returned iterator.436* |[437* fluid_hashtable_iter_t iter;438* gpointer key, value;439*440* fluid_hashtable_iter_init (&iter, hashtable);441* while (fluid_hashtable_iter_next (&iter, &key, &value))442* {443* /* do something with key and value */444* }445* ]|446*447* Since: 2.16448**/449void450fluid_hashtable_iter_init(fluid_hashtable_iter_t *iter,451fluid_hashtable_t *hashtable)452{453RealIter *ri = (RealIter *) iter;454455fluid_return_if_fail(iter != NULL);456fluid_return_if_fail(hashtable != NULL);457458ri->hashtable = hashtable;459ri->prev_node = NULL;460ri->node = NULL;461ri->position = -1;462ri->pre_advanced = FALSE;463}464465/**466* fluid_hashtable_iter_next:467* @iter: an initialized #fluid_hashtable_iter_t.468* @key: a location to store the key, or %NULL.469* @value: a location to store the value, or %NULL.470*471* Advances @iter and retrieves the key and/or value that are now472* pointed to as a result of this advancement. If %FALSE is returned,473* @key and @value are not set, and the iterator becomes invalid.474*475* Return value: %FALSE if the end of the #fluid_hashtable_t has been reached.476*477* Since: 2.16478**/479int480fluid_hashtable_iter_next(fluid_hashtable_iter_t *iter, void **key,481void **value)482{483RealIter *ri = (RealIter *) iter;484485fluid_return_val_if_fail(iter != NULL, FALSE);486487if(ri->pre_advanced)488{489ri->pre_advanced = FALSE;490491if(ri->node == NULL)492{493return FALSE;494}495}496else497{498if(ri->node != NULL)499{500ri->prev_node = ri->node;501ri->node = ri->node->next;502}503504while(ri->node == NULL)505{506ri->position++;507508if(ri->position >= ri->hashtable->size)509{510return FALSE;511}512513ri->prev_node = NULL;514ri->node = ri->hashtable->nodes[ri->position];515}516}517518if(key != NULL)519{520*key = ri->node->key;521}522523if(value != NULL)524{525*value = ri->node->value;526}527528return TRUE;529}530531/**532* fluid_hashtable_iter_get_hash_table:533* @iter: an initialized #fluid_hashtable_iter_t.534*535* Returns the #fluid_hashtable_t associated with @iter.536*537* Return value: the #fluid_hashtable_t associated with @iter.538*539* Since: 2.16540**/541fluid_hashtable_t *542fluid_hashtable_iter_get_hash_table(fluid_hashtable_iter_t *iter)543{544fluid_return_val_if_fail(iter != NULL, NULL);545546return ((RealIter *) iter)->hashtable;547}548549static void550iter_remove_or_steal(RealIter *ri, int notify)551{552fluid_hashnode_t *prev;553fluid_hashnode_t *node;554int position;555556fluid_return_if_fail(ri != NULL);557fluid_return_if_fail(ri->node != NULL);558559prev = ri->prev_node;560node = ri->node;561position = ri->position;562563/* pre-advance the iterator since we will remove the node */564565ri->node = ri->node->next;566/* ri->prev_node is still the correct previous node */567568while(ri->node == NULL)569{570ri->position++;571572if(ri->position >= ri->hashtable->size)573{574break;575}576577ri->prev_node = NULL;578ri->node = ri->hashtable->nodes[ri->position];579}580581ri->pre_advanced = TRUE;582583/* remove the node */584585if(prev != NULL)586{587prev->next = node->next;588}589else590{591ri->hashtable->nodes[position] = node->next;592}593594if(notify)595{596if(ri->hashtable->key_destroy_func)597{598ri->hashtable->key_destroy_func(node->key);599}600601if(ri->hashtable->value_destroy_func)602{603ri->hashtable->value_destroy_func(node->value);604}605}606607FLUID_FREE(node);608609ri->hashtable->nnodes--;610}611612/**613* fluid_hashtable_iter_remove():614* @iter: an initialized #fluid_hashtable_iter_t.615*616* Removes the key/value pair currently pointed to by the iterator617* from its associated #fluid_hashtable_t. Can only be called after618* fluid_hashtable_iter_next() returned %TRUE, and cannot be called more619* than once for the same key/value pair.620*621* If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the622* key and value are freed using the supplied destroy functions, otherwise623* you have to make sure that any dynamically allocated values are freed624* yourself.625*626* Since: 2.16627**/628void629fluid_hashtable_iter_remove(fluid_hashtable_iter_t *iter)630{631iter_remove_or_steal((RealIter *) iter, TRUE);632}633634/**635* fluid_hashtable_iter_steal():636* @iter: an initialized #fluid_hashtable_iter_t.637*638* Removes the key/value pair currently pointed to by the iterator639* from its associated #fluid_hashtable_t, without calling the key and value640* destroy functions. Can only be called after641* fluid_hashtable_iter_next() returned %TRUE, and cannot be called more642* than once for the same key/value pair.643*644* Since: 2.16645**/646void647fluid_hashtable_iter_steal(fluid_hashtable_iter_t *iter)648{649iter_remove_or_steal((RealIter *) iter, FALSE);650}651652653/**654* fluid_hashtable_ref:655* @hashtable: a valid #fluid_hashtable_t.656*657* Atomically increments the reference count of @hashtable by one.658* This function is MT-safe and may be called from any thread.659*660* Return value: the passed in #fluid_hashtable_t.661*662* Since: 2.10663**/664fluid_hashtable_t *665fluid_hashtable_ref(fluid_hashtable_t *hashtable)666{667fluid_return_val_if_fail(hashtable != NULL, NULL);668fluid_return_val_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0, hashtable);669670fluid_atomic_int_add(&hashtable->ref_count, 1);671return hashtable;672}673674/**675* fluid_hashtable_unref:676* @hashtable: a valid #fluid_hashtable_t.677*678* Atomically decrements the reference count of @hashtable by one.679* If the reference count drops to 0, all keys and values will be680* destroyed, and all memory allocated by the hash table is released.681* This function is MT-safe and may be called from any thread.682*683* Since: 2.10684**/685void686fluid_hashtable_unref(fluid_hashtable_t *hashtable)687{688fluid_return_if_fail(hashtable != NULL);689fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);690691if(fluid_atomic_int_exchange_and_add(&hashtable->ref_count, -1) - 1 == 0)692{693fluid_hashtable_remove_all_nodes(hashtable, TRUE);694FLUID_FREE(hashtable->nodes);695FLUID_FREE(hashtable);696}697}698699/**700* delete_fluid_hashtable:701* @hashtable: a #fluid_hashtable_t.702*703* Destroys all keys and values in the #fluid_hashtable_t and decrements its704* reference count by 1. If keys and/or values are dynamically allocated,705* you should either free them first or create the #fluid_hashtable_t with destroy706* notifiers using fluid_hashtable_new_full(). In the latter case the destroy707* functions you supplied will be called on all keys and values during the708* destruction phase.709**/710void711delete_fluid_hashtable(fluid_hashtable_t *hashtable)712{713fluid_return_if_fail(hashtable != NULL);714fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);715716fluid_hashtable_remove_all(hashtable);717fluid_hashtable_unref(hashtable);718}719720/**721* fluid_hashtable_lookup:722* @hashtable: a #fluid_hashtable_t.723* @key: the key to look up.724*725* Looks up a key in a #fluid_hashtable_t. Note that this function cannot726* distinguish between a key that is not present and one which is present727* and has the value %NULL. If you need this distinction, use728* fluid_hashtable_lookup_extended().729*730* Return value: the associated value, or %NULL if the key is not found.731**/732void *733fluid_hashtable_lookup(fluid_hashtable_t *hashtable, const void *key)734{735fluid_hashnode_t *node;736737fluid_return_val_if_fail(hashtable != NULL, NULL);738739node = *fluid_hashtable_lookup_node(hashtable, key, NULL);740741return node ? node->value : NULL;742}743744/**745* fluid_hashtable_lookup_extended:746* @hashtable: a #fluid_hashtable_t.747* @lookup_key: the key to look up.748* @orig_key: returns the original key.749* @value: returns the value associated with the key.750*751* Looks up a key in the #fluid_hashtable_t, returning the original key and the752* associated value and a #gboolean which is %TRUE if the key was found. This753* is useful if you need to free the memory allocated for the original key,754* for example before calling fluid_hashtable_remove().755*756* Return value: %TRUE if the key was found in the #fluid_hashtable_t.757**/758int759fluid_hashtable_lookup_extended(fluid_hashtable_t *hashtable,760const void *lookup_key,761void **orig_key, void **value)762{763fluid_hashnode_t *node;764765fluid_return_val_if_fail(hashtable != NULL, FALSE);766767node = *fluid_hashtable_lookup_node(hashtable, lookup_key, NULL);768769if(node == NULL)770{771return FALSE;772}773774if(orig_key)775{776*orig_key = node->key;777}778779if(value)780{781*value = node->value;782}783784return TRUE;785}786787/*788* fluid_hashtable_insert_internal:789* @hashtable: our #fluid_hashtable_t790* @key: the key to insert791* @value: the value to insert792* @keep_new_key: if %TRUE and this key already exists in the table793* then call the destroy notify function on the old key. If %FALSE794* then call the destroy notify function on the new key.795*796* Implements the common logic for the fluid_hashtable_insert() and797* fluid_hashtable_replace() functions.798*799* Do a lookup of @key. If it is found, replace it with the new800* @value (and perhaps the new @key). If it is not found, create a801* new node.802*/803static void804fluid_hashtable_insert_internal(fluid_hashtable_t *hashtable, void *key,805void *value, int keep_new_key)806{807fluid_hashnode_t **node_ptr, *node;808unsigned int key_hash;809810fluid_return_if_fail(hashtable != NULL);811fluid_return_if_fail(fluid_atomic_int_get(&hashtable->ref_count) > 0);812813node_ptr = fluid_hashtable_lookup_node(hashtable, key, &key_hash);814815if((node = *node_ptr))816{817if(keep_new_key)818{819if(hashtable->key_destroy_func)820{821hashtable->key_destroy_func(node->key);822}823824node->key = key;825}826else827{828if(hashtable->key_destroy_func)829{830hashtable->key_destroy_func(key);831}832}833834if(hashtable->value_destroy_func)835{836hashtable->value_destroy_func(node->value);837}838839node->value = value;840}841else842{843node = FLUID_NEW(fluid_hashnode_t);844845if(!node)846{847FLUID_LOG(FLUID_ERR, "Out of memory");848return;849}850851node->key = key;852node->value = value;853node->key_hash = key_hash;854node->next = NULL;855856*node_ptr = node;857hashtable->nnodes++;858fluid_hashtable_maybe_resize(hashtable);859}860}861862/**863* fluid_hashtable_insert:864* @hashtable: a #fluid_hashtable_t.865* @key: a key to insert.866* @value: the value to associate with the key.867*868* Inserts a new key and value into a #fluid_hashtable_t.869*870* If the key already exists in the #fluid_hashtable_t its current value is replaced871* with the new value. If you supplied a @value_destroy_func when creating the872* #fluid_hashtable_t, the old value is freed using that function. If you supplied873* a @key_destroy_func when creating the #fluid_hashtable_t, the passed key is freed874* using that function.875**/876void877fluid_hashtable_insert(fluid_hashtable_t *hashtable, void *key, void *value)878{879fluid_hashtable_insert_internal(hashtable, key, value, FALSE);880}881882/**883* fluid_hashtable_replace:884* @hashtable: a #fluid_hashtable_t.885* @key: a key to insert.886* @value: the value to associate with the key.887*888* Inserts a new key and value into a #fluid_hashtable_t similar to889* fluid_hashtable_insert(). The difference is that if the key already exists890* in the #fluid_hashtable_t, it gets replaced by the new key. If you supplied a891* @value_destroy_func when creating the #fluid_hashtable_t, the old value is freed892* using that function. If you supplied a @key_destroy_func when creating the893* #fluid_hashtable_t, the old key is freed using that function.894**/895void896fluid_hashtable_replace(fluid_hashtable_t *hashtable, void *key, void *value)897{898fluid_hashtable_insert_internal(hashtable, key, value, TRUE);899}900901/*902* fluid_hashtable_remove_internal:903* @hashtable: our #fluid_hashtable_t904* @key: the key to remove905* @notify: %TRUE if the destroy notify handlers are to be called906* Return value: %TRUE if a node was found and removed, else %FALSE907*908* Implements the common logic for the fluid_hashtable_remove() and909* fluid_hashtable_steal() functions.910*911* Do a lookup of @key and remove it if it is found, calling the912* destroy notify handlers only if @notify is %TRUE.913*/914static int915fluid_hashtable_remove_internal(fluid_hashtable_t *hashtable, const void *key,916int notify)917{918fluid_hashnode_t **node_ptr;919920fluid_return_val_if_fail(hashtable != NULL, FALSE);921922node_ptr = fluid_hashtable_lookup_node(hashtable, key, NULL);923924if(*node_ptr == NULL)925{926return FALSE;927}928929fluid_hashtable_remove_node(hashtable, &node_ptr, notify);930fluid_hashtable_maybe_resize(hashtable);931932return TRUE;933}934935/**936* fluid_hashtable_remove:937* @hashtable: a #fluid_hashtable_t.938* @key: the key to remove.939*940* Removes a key and its associated value from a #fluid_hashtable_t.941*942* If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the943* key and value are freed using the supplied destroy functions, otherwise944* you have to make sure that any dynamically allocated values are freed945* yourself.946*947* Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.948**/949int950fluid_hashtable_remove(fluid_hashtable_t *hashtable, const void *key)951{952return fluid_hashtable_remove_internal(hashtable, key, TRUE);953}954955/**956* fluid_hashtable_steal:957* @hashtable: a #fluid_hashtable_t.958* @key: the key to remove.959*960* Removes a key and its associated value from a #fluid_hashtable_t without961* calling the key and value destroy functions.962*963* Return value: %TRUE if the key was found and removed from the #fluid_hashtable_t.964**/965int966fluid_hashtable_steal(fluid_hashtable_t *hashtable, const void *key)967{968return fluid_hashtable_remove_internal(hashtable, key, FALSE);969}970971/**972* fluid_hashtable_remove_all:973* @hashtable: a #fluid_hashtable_t974*975* Removes all keys and their associated values from a #fluid_hashtable_t.976*977* If the #fluid_hashtable_t was created using fluid_hashtable_new_full(), the keys978* and values are freed using the supplied destroy functions, otherwise you979* have to make sure that any dynamically allocated values are freed980* yourself.981*982* Since: 2.12983**/984void985fluid_hashtable_remove_all(fluid_hashtable_t *hashtable)986{987fluid_return_if_fail(hashtable != NULL);988989fluid_hashtable_remove_all_nodes(hashtable, TRUE);990fluid_hashtable_maybe_resize(hashtable);991}992993/**994* fluid_hashtable_steal_all:995* @hashtable: a #fluid_hashtable_t.996*997* Removes all keys and their associated values from a #fluid_hashtable_t998* without calling the key and value destroy functions.999*1000* Since: 2.121001**/1002void1003fluid_hashtable_steal_all(fluid_hashtable_t *hashtable)1004{1005fluid_return_if_fail(hashtable != NULL);10061007fluid_hashtable_remove_all_nodes(hashtable, FALSE);1008fluid_hashtable_maybe_resize(hashtable);1009}10101011/*1012* fluid_hashtable_foreach_remove_or_steal:1013* @hashtable: our #fluid_hashtable_t1014* @func: the user's callback function1015* @user_data: data for @func1016* @notify: %TRUE if the destroy notify handlers are to be called1017*1018* Implements the common logic for fluid_hashtable_foreach_remove() and1019* fluid_hashtable_foreach_steal().1020*1021* Iterates over every node in the table, calling @func with the key1022* and value of the node (and @user_data). If @func returns %TRUE the1023* node is removed from the table.1024*1025* If @notify is true then the destroy notify handlers will be called1026* for each removed node.1027*/1028static unsigned int1029fluid_hashtable_foreach_remove_or_steal(fluid_hashtable_t *hashtable,1030fluid_hr_func_t func, void *user_data,1031int notify)1032{1033fluid_hashnode_t *node, **node_ptr;1034unsigned int deleted = 0;1035int i;10361037for(i = 0; i < hashtable->size; i++)1038{1039for(node_ptr = &hashtable->nodes[i]; (node = *node_ptr) != NULL;)1040{1041if((* func)(node->key, node->value, user_data))1042{1043fluid_hashtable_remove_node(hashtable, &node_ptr, notify);1044deleted++;1045}1046else1047{1048node_ptr = &node->next;1049}1050}1051}10521053fluid_hashtable_maybe_resize(hashtable);10541055return deleted;1056}10571058#if 01059/**1060* fluid_hashtable_foreach_remove:1061* @hashtable: a #fluid_hashtable_t.1062* @func: the function to call for each key/value pair.1063* @user_data: user data to pass to the function.1064*1065* Calls the given function for each key/value pair in the #fluid_hashtable_t.1066* If the function returns %TRUE, then the key/value pair is removed from the1067* #fluid_hashtable_t. If you supplied key or value destroy functions when creating1068* the #fluid_hashtable_t, they are used to free the memory allocated for the removed1069* keys and values.1070*1071* See #fluid_hashtable_iter_t for an alternative way to loop over the1072* key/value pairs in the hash table.1073*1074* Return value: the number of key/value pairs removed.1075**/1076static unsigned int1077fluid_hashtable_foreach_remove(fluid_hashtable_t *hashtable,1078fluid_hr_func_t func, void *user_data)1079{1080fluid_return_val_if_fail(hashtable != NULL, 0);1081fluid_return_val_if_fail(func != NULL, 0);10821083return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, TRUE);1084}1085#endif10861087/**1088* fluid_hashtable_foreach_steal:1089* @hashtable: a #fluid_hashtable_t.1090* @func: the function to call for each key/value pair.1091* @user_data: user data to pass to the function.1092*1093* Calls the given function for each key/value pair in the #fluid_hashtable_t.1094* If the function returns %TRUE, then the key/value pair is removed from the1095* #fluid_hashtable_t, but no key or value destroy functions are called.1096*1097* See #fluid_hashtable_iter_t for an alternative way to loop over the1098* key/value pairs in the hash table.1099*1100* Return value: the number of key/value pairs removed.1101**/1102unsigned int1103fluid_hashtable_foreach_steal(fluid_hashtable_t *hashtable,1104fluid_hr_func_t func, void *user_data)1105{1106fluid_return_val_if_fail(hashtable != NULL, 0);1107fluid_return_val_if_fail(func != NULL, 0);11081109return fluid_hashtable_foreach_remove_or_steal(hashtable, func, user_data, FALSE);1110}11111112/**1113* fluid_hashtable_foreach:1114* @hashtable: a #fluid_hashtable_t.1115* @func: the function to call for each key/value pair.1116* @user_data: user data to pass to the function.1117*1118* Calls the given function for each of the key/value pairs in the1119* #fluid_hashtable_t. The function is passed the key and value of each1120* pair, and the given @user_data parameter. The hash table may not1121* be modified while iterating over it (you can't add/remove1122* items). To remove all items matching a predicate, use1123* fluid_hashtable_foreach_remove().1124*1125* See fluid_hashtable_find() for performance caveats for linear1126* order searches in contrast to fluid_hashtable_lookup().1127**/1128void1129fluid_hashtable_foreach(fluid_hashtable_t *hashtable, fluid_hr_func_t func,1130void *user_data)1131{1132fluid_hashnode_t *node;1133int i;11341135fluid_return_if_fail(hashtable != NULL);1136fluid_return_if_fail(func != NULL);11371138for(i = 0; i < hashtable->size; i++)1139{1140for(node = hashtable->nodes[i]; node; node = node->next)1141{1142(* func)(node->key, node->value, user_data);1143}1144}1145}11461147/**1148* fluid_hashtable_find:1149* @hashtable: a #fluid_hashtable_t.1150* @predicate: function to test the key/value pairs for a certain property.1151* @user_data: user data to pass to the function.1152*1153* Calls the given function for key/value pairs in the #fluid_hashtable_t until1154* @predicate returns %TRUE. The function is passed the key and value of1155* each pair, and the given @user_data parameter. The hash table may not1156* be modified while iterating over it (you can't add/remove items).1157*1158* Note, that hash tables are really only optimized for forward lookups,1159* i.e. fluid_hashtable_lookup().1160* So code that frequently issues fluid_hashtable_find() or1161* fluid_hashtable_foreach() (e.g. in the order of once per every entry in a1162* hash table) should probably be reworked to use additional or different1163* data structures for reverse lookups (keep in mind that an O(n) find/foreach1164* operation issued for all n values in a hash table ends up needing O(n*n)1165* operations).1166*1167* Return value: The value of the first key/value pair is returned, for which1168* func evaluates to %TRUE. If no pair with the requested property is found,1169* %NULL is returned.1170*1171* Since: 2.41172**/1173void *1174fluid_hashtable_find(fluid_hashtable_t *hashtable, fluid_hr_func_t predicate,1175void *user_data)1176{1177fluid_hashnode_t *node;1178int i;11791180fluid_return_val_if_fail(hashtable != NULL, NULL);1181fluid_return_val_if_fail(predicate != NULL, NULL);11821183for(i = 0; i < hashtable->size; i++)1184{1185for(node = hashtable->nodes[i]; node; node = node->next)1186{1187if(predicate(node->key, node->value, user_data))1188{1189return node->value;1190}1191}1192}11931194return NULL;1195}11961197/**1198* fluid_hashtable_size:1199* @hashtable: a #fluid_hashtable_t.1200*1201* Returns the number of elements contained in the #fluid_hashtable_t.1202*1203* Return value: the number of key/value pairs in the #fluid_hashtable_t.1204**/1205unsigned int1206fluid_hashtable_size(fluid_hashtable_t *hashtable)1207{1208fluid_return_val_if_fail(hashtable != NULL, 0);12091210return hashtable->nnodes;1211}12121213/**1214* fluid_hashtable_get_keys:1215* @hashtable: a #fluid_hashtable_t1216*1217* Retrieves every key inside @hashtable. The returned data is valid1218* until @hashtable is modified.1219*1220* Return value: a #GList containing all the keys inside the hash1221* table. The content of the list is owned by the hash table and1222* should not be modified or freed. Use delete_fluid_list() when done1223* using the list.1224*1225* Since: 2.141226*/1227fluid_list_t *1228fluid_hashtable_get_keys(fluid_hashtable_t *hashtable)1229{1230fluid_hashnode_t *node;1231int i;1232fluid_list_t *retval;12331234fluid_return_val_if_fail(hashtable != NULL, NULL);12351236retval = NULL;12371238for(i = 0; i < hashtable->size; i++)1239{1240for(node = hashtable->nodes[i]; node; node = node->next)1241{1242retval = fluid_list_prepend(retval, node->key);1243}1244}12451246return retval;1247}12481249/**1250* fluid_hashtable_get_values:1251* @hashtable: a #fluid_hashtable_t1252*1253* Retrieves every value inside @hashtable. The returned data is1254* valid until @hashtable is modified.1255*1256* Return value: a #GList containing all the values inside the hash1257* table. The content of the list is owned by the hash table and1258* should not be modified or freed. Use delete_fluid_list() when done1259* using the list.1260*1261* Since: 2.141262*/1263fluid_list_t *1264fluid_hashtable_get_values(fluid_hashtable_t *hashtable)1265{1266fluid_hashnode_t *node;1267int i;1268fluid_list_t *retval;12691270fluid_return_val_if_fail(hashtable != NULL, NULL);12711272retval = NULL;12731274for(i = 0; i < hashtable->size; i++)1275{1276for(node = hashtable->nodes[i]; node; node = node->next)1277{1278retval = fluid_list_prepend(retval, node->value);1279}1280}12811282return retval;1283}128412851286/* Extracted from glib/gstring.c */128712881289/**1290* fluid_str_equal:1291* @v1: a key1292* @v2: a key to compare with @v11293*1294* Compares two strings for byte-by-byte equality and returns %TRUE1295* if they are equal. It can be passed to new_fluid_hashtable() as the1296* @key_equal_func parameter, when using strings as keys in a #Ghashtable.1297*1298* Returns: %TRUE if the two keys match1299*/1300int1301fluid_str_equal(const void *v1, const void *v2)1302{1303const char *string1 = v1;1304const char *string2 = v2;13051306return FLUID_STRCMP(string1, string2) == 0;1307}13081309/**1310* fluid_str_hash:1311* @v: a string key1312*1313* Converts a string to a hash value.1314* It can be passed to new_fluid_hashtable() as the @hash_func1315* parameter, when using strings as keys in a #fluid_hashtable_t.1316*1317* Returns: a hash value corresponding to the key1318*/1319unsigned int1320fluid_str_hash(const void *v)1321{1322/* 31 bit hash function */1323const signed char *p = v;1324uint32_t h = *p;13251326if(h)1327{1328for(p += 1; *p != '\0'; p++)1329{1330h = (h << 5) - h + *p;1331}1332}13331334return h;1335}133613371338/* Extracted from glib/gutils.c */133913401341/**1342* fluid_direct_equal:1343* @v1: a key.1344* @v2: a key to compare with @v1.1345*1346* Compares two #gpointer arguments and returns %TRUE if they are equal.1347* It can be passed to new_fluid_hashtable() as the @key_equal_func1348* parameter, when using pointers as keys in a #fluid_hashtable_t.1349*1350* Returns: %TRUE if the two keys match.1351*/1352int1353fluid_direct_equal(const void *v1, const void *v2)1354{1355return v1 == v2;1356}13571358/**1359* fluid_direct_hash:1360* @v: a void * key1361*1362* Converts a gpointer to a hash value.1363* It can be passed to g_hashtable_new() as the @hash_func parameter,1364* when using pointers as keys in a #fluid_hashtable_t.1365*1366* Returns: a hash value corresponding to the key.1367*/1368unsigned int1369fluid_direct_hash(const void *v)1370{1371return FLUID_POINTER_TO_UINT(v);1372}13731374/**1375* fluid_int_equal:1376* @v1: a pointer to a int key.1377* @v2: a pointer to a int key to compare with @v1.1378*1379* Compares the two #gint values being pointed to and returns1380* %TRUE if they are equal.1381* It can be passed to g_hashtable_new() as the @key_equal_func1382* parameter, when using pointers to integers as keys in a #fluid_hashtable_t.1383*1384* Returns: %TRUE if the two keys match.1385*/1386int1387fluid_int_equal(const void *v1, const void *v2)1388{1389return *((const int *) v1) == *((const int *) v2);1390}13911392/**1393* fluid_int_hash:1394* @v: a pointer to a int key1395*1396* Converts a pointer to a #gint to a hash value.1397* It can be passed to g_hashtable_new() as the @hash_func parameter,1398* when using pointers to integers values as keys in a #fluid_hashtable_t.1399*1400* Returns: a hash value corresponding to the key.1401*/1402unsigned int1403fluid_int_hash(const void *v)1404{1405return *(const int *) v;1406}140714081409