Main Page | Class List | Directories | File List | Class Members | File Members

ghash.c

Go to the documentation of this file.
00001 /* GLIB - Library of useful routines for C programming
00002  * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Library General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Library General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Library General Public
00015  * License along with this library; if not, write to the
00016  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
00017  * Boston, MA 02111-1307, USA.
00018  */
00019 
00020 /*
00021  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
00022  * file for a list of people on the GLib Team.  See the ChangeLog
00023  * files for a list of changes.  These files are distributed with
00024  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
00025  */
00026 
00027 /* 
00028  * MT safe
00029  */
00030 
00031 #include "glib.h"
00032 
00033 
00034 #define HASH_TABLE_MIN_SIZE 11
00035 #define HASH_TABLE_MAX_SIZE 13845163
00036 
00037 
00038 typedef struct _GHashNode      GHashNode;
00039 
00040 struct _GHashNode
00041 {
00042   gpointer key;
00043   gpointer value;
00044   GHashNode *next;
00045 };
00046 
00047 struct _GHashTable
00048 {
00049   gint size;
00050   gint nnodes;
00051   guint frozen;
00052   GHashNode **nodes;
00053   GHashFunc hash_func;
00054   GCompareFunc key_compare_func;
00055 };
00056 
00057 
00058 static void             g_hash_table_resize      (GHashTable    *hash_table);
00059 static GHashNode**      g_hash_table_lookup_node (GHashTable    *hash_table,
00060                                                   gconstpointer  key);
00061 static GHashNode*       g_hash_node_new          (gpointer       key,
00062                                                   gpointer       value);
00063 static void             g_hash_node_destroy      (GHashNode     *hash_node);
00064 static void             g_hash_nodes_destroy     (GHashNode     *hash_node);
00065 
00066 
00067 G_LOCK_DEFINE_STATIC (g_hash_global);
00068 
00069 static GMemChunk *node_mem_chunk = NULL;
00070 static GHashNode *node_free_list = NULL;
00071 
00072 
00073 GHashTable*
00074 g_hash_table_new (GHashFunc    hash_func,
00075                   GCompareFunc key_compare_func)
00076 {
00077   GHashTable *hash_table;
00078   guint i;
00079   
00080   hash_table = g_new (GHashTable, 1);
00081   hash_table->size = HASH_TABLE_MIN_SIZE;
00082   hash_table->nnodes = 0;
00083   hash_table->frozen = FALSE;
00084   hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
00085   hash_table->key_compare_func = key_compare_func;
00086   hash_table->nodes = g_new (GHashNode*, hash_table->size);
00087   
00088   for (i = 0; i < hash_table->size; i++)
00089     hash_table->nodes[i] = NULL;
00090   
00091   return hash_table;
00092 }
00093 
00094 void
00095 g_hash_table_destroy (GHashTable *hash_table)
00096 {
00097   guint i;
00098   
00099   g_return_if_fail (hash_table != NULL);
00100   
00101   for (i = 0; i < hash_table->size; i++)
00102     g_hash_nodes_destroy (hash_table->nodes[i]);
00103   
00104   g_free (hash_table->nodes);
00105   g_free (hash_table);
00106 }
00107 
00108 static inline GHashNode**
00109 g_hash_table_lookup_node (GHashTable    *hash_table,
00110                           gconstpointer  key)
00111 {
00112   GHashNode **node;
00113   
00114   node = &hash_table->nodes
00115     [(* hash_table->hash_func) (key) % hash_table->size];
00116   
00117   /* Hash table lookup needs to be fast.
00118    *  We therefore remove the extra conditional of testing
00119    *  whether to call the key_compare_func or not from
00120    *  the inner loop.
00121    */
00122   if (hash_table->key_compare_func)
00123     while (*node && !(*hash_table->key_compare_func) ((*node)->key, key))
00124       node = &(*node)->next;
00125   else
00126     while (*node && (*node)->key != key)
00127       node = &(*node)->next;
00128   
00129   return node;
00130 }
00131 
00132 gpointer
00133 g_hash_table_lookup (GHashTable   *hash_table,
00134                      gconstpointer key)
00135 {
00136   GHashNode *node;
00137   
00138   g_return_val_if_fail (hash_table != NULL, NULL);
00139   
00140   node = *g_hash_table_lookup_node (hash_table, key);
00141   
00142   return node ? node->value : NULL;
00143 }
00144 
00145 void
00146 g_hash_table_insert (GHashTable *hash_table,
00147                      gpointer    key,
00148                      gpointer    value)
00149 {
00150   GHashNode **node;
00151   
00152   g_return_if_fail (hash_table != NULL);
00153   
00154   node = g_hash_table_lookup_node (hash_table, key);
00155   
00156   if (*node)
00157     {
00158       /* do not reset node->key in this place, keeping
00159        * the old key might be intended.
00160        * a g_hash_table_remove/g_hash_table_insert pair
00161        * can be used otherwise.
00162        *
00163        * node->key = key; */
00164       (*node)->value = value;
00165     }
00166   else
00167     {
00168       *node = g_hash_node_new (key, value);
00169       hash_table->nnodes++;
00170       if (!hash_table->frozen)
00171         g_hash_table_resize (hash_table);
00172     }
00173 }
00174 
00175 void
00176 g_hash_table_remove (GHashTable      *hash_table,
00177                      gconstpointer    key)
00178 {
00179   GHashNode **node, *dest;
00180   
00181   g_return_if_fail (hash_table != NULL);
00182   
00183   node = g_hash_table_lookup_node (hash_table, key);
00184 
00185   if (*node)
00186     {
00187       dest = *node;
00188       (*node) = dest->next;
00189       g_hash_node_destroy (dest);
00190       hash_table->nnodes--;
00191   
00192       if (!hash_table->frozen)
00193         g_hash_table_resize (hash_table);
00194     }
00195 }
00196 
00197 gboolean
00198 g_hash_table_lookup_extended (GHashTable        *hash_table,
00199                               gconstpointer      lookup_key,
00200                               gpointer          *orig_key,
00201                               gpointer          *value)
00202 {
00203   GHashNode *node;
00204   
00205   g_return_val_if_fail (hash_table != NULL, FALSE);
00206   
00207   node = *g_hash_table_lookup_node (hash_table, lookup_key);
00208   
00209   if (node)
00210     {
00211       if (orig_key)
00212         *orig_key = node->key;
00213       if (value)
00214         *value = node->value;
00215       return TRUE;
00216     }
00217   else
00218     return FALSE;
00219 }
00220 
00221 void
00222 g_hash_table_freeze (GHashTable *hash_table)
00223 {
00224   g_return_if_fail (hash_table != NULL);
00225   
00226   hash_table->frozen++;
00227 }
00228 
00229 void
00230 g_hash_table_thaw (GHashTable *hash_table)
00231 {
00232   g_return_if_fail (hash_table != NULL);
00233   
00234   if (hash_table->frozen)
00235     if (!(--hash_table->frozen))
00236       g_hash_table_resize (hash_table);
00237 }
00238 
00239 guint
00240 g_hash_table_foreach_remove (GHashTable *hash_table,
00241                              GHRFunc     func,
00242                              gpointer    user_data)
00243 {
00244   GHashNode *node, *prev;
00245   guint i;
00246   guint deleted = 0;
00247   
00248   g_return_val_if_fail (hash_table != NULL, 0);
00249   g_return_val_if_fail (func != NULL, 0);
00250   
00251   for (i = 0; i < hash_table->size; i++)
00252     {
00253     restart:
00254       
00255       prev = NULL;
00256       
00257       for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
00258         {
00259           if ((* func) (node->key, node->value, user_data))
00260             {
00261               deleted += 1;
00262               
00263               hash_table->nnodes -= 1;
00264               
00265               if (prev)
00266                 {
00267                   prev->next = node->next;
00268                   g_hash_node_destroy (node);
00269                   node = prev;
00270                 }
00271               else
00272                 {
00273                   hash_table->nodes[i] = node->next;
00274                   g_hash_node_destroy (node);
00275                   goto restart;
00276                 }
00277             }
00278         }
00279     }
00280   
00281   if (!hash_table->frozen)
00282     g_hash_table_resize (hash_table);
00283   
00284   return deleted;
00285 }
00286 
00287 void
00288 g_hash_table_foreach (GHashTable *hash_table,
00289                       GHFunc      func,
00290                       gpointer    user_data)
00291 {
00292   GHashNode *node;
00293   gint i;
00294   
00295   g_return_if_fail (hash_table != NULL);
00296   g_return_if_fail (func != NULL);
00297   
00298   for (i = 0; i < hash_table->size; i++)
00299     for (node = hash_table->nodes[i]; node; node = node->next)
00300       (* func) (node->key, node->value, user_data);
00301 }
00302 
00303 /* Returns the number of elements contained in the hash table. */
00304 guint
00305 g_hash_table_size (GHashTable *hash_table)
00306 {
00307   g_return_val_if_fail (hash_table != NULL, 0);
00308   
00309   return hash_table->nnodes;
00310 }
00311 
00312 static void
00313 g_hash_table_resize (GHashTable *hash_table)
00314 {
00315   GHashNode **new_nodes;
00316   GHashNode *node;
00317   GHashNode *next;
00318   gfloat nodes_per_list;
00319   guint hash_val;
00320   gint new_size;
00321   gint i;
00322   
00323   nodes_per_list = (gfloat) hash_table->nnodes / (gfloat) hash_table->size;
00324   
00325   if ((nodes_per_list > 0.3 || hash_table->size <= HASH_TABLE_MIN_SIZE) &&
00326       (nodes_per_list < 3.0 || hash_table->size >= HASH_TABLE_MAX_SIZE))
00327     return;
00328   
00329   new_size = CLAMP(g_spaced_primes_closest (hash_table->nnodes),
00330                    HASH_TABLE_MIN_SIZE,
00331                    HASH_TABLE_MAX_SIZE);
00332   new_nodes = g_new0 (GHashNode*, new_size);
00333   
00334   for (i = 0; i < hash_table->size; i++)
00335     for (node = hash_table->nodes[i]; node; node = next)
00336       {
00337         next = node->next;
00338 
00339         hash_val = (* hash_table->hash_func) (node->key) % new_size;
00340 
00341         node->next = new_nodes[hash_val];
00342         new_nodes[hash_val] = node;
00343       }
00344   
00345   g_free (hash_table->nodes);
00346   hash_table->nodes = new_nodes;
00347   hash_table->size = new_size;
00348 }
00349 
00350 static GHashNode*
00351 g_hash_node_new (gpointer key,
00352                  gpointer value)
00353 {
00354   GHashNode *hash_node;
00355   
00356   G_LOCK (g_hash_global);
00357   if (node_free_list)
00358     {
00359       hash_node = node_free_list;
00360       node_free_list = node_free_list->next;
00361     }
00362   else
00363     {
00364       if (!node_mem_chunk)
00365         node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
00366                                           sizeof (GHashNode),
00367                                           1024, G_ALLOC_ONLY);
00368       
00369       hash_node = g_chunk_new (GHashNode, node_mem_chunk);
00370     }
00371   G_UNLOCK (g_hash_global);
00372   
00373   hash_node->key = key;
00374   hash_node->value = value;
00375   hash_node->next = NULL;
00376   
00377   return hash_node;
00378 }
00379 
00380 static void
00381 g_hash_node_destroy (GHashNode *hash_node)
00382 {
00383   G_LOCK (g_hash_global);
00384   hash_node->next = node_free_list;
00385   node_free_list = hash_node;
00386   G_UNLOCK (g_hash_global);
00387 }
00388 
00389 static void
00390 g_hash_nodes_destroy (GHashNode *hash_node)
00391 {
00392   if (hash_node)
00393     {
00394       GHashNode *node = hash_node;
00395   
00396       while (node->next)
00397         node = node->next;
00398   
00399       G_LOCK (g_hash_global);
00400       node->next = node_free_list;
00401       node_free_list = hash_node;
00402       G_UNLOCK (g_hash_global);
00403     }
00404 }

© sourcejam.com 2005-2008