00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
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
00118
00119
00120
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
00159
00160
00161
00162
00163
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
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 }