ghash.c 9.04 KB
Newer Older
Owen Taylor's avatar
Owen Taylor committed
1 2 3 4 5 6 7 8 9 10
/* 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 Library 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
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the GNU
Owen Taylor's avatar
Owen Taylor committed
12 13 14 15 16 17 18
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library 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.
 */
19

20 21 22 23 24 25 26
/*
 * Modified by the GLib Team and others 1997-1999.  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/. 
 */

27 28 29 30
/* 
 * MT safe
 */

Owen Taylor's avatar
Owen Taylor committed
31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include "glib.h"


#define HASH_TABLE_MIN_SIZE 11
#define HASH_TABLE_MAX_SIZE 13845163


typedef struct _GHashNode      GHashNode;

struct _GHashNode
{
  gpointer key;
  gpointer value;
  GHashNode *next;
};

47
struct _GHashTable
Owen Taylor's avatar
Owen Taylor committed
48 49 50 51 52 53 54 55 56
{
  gint size;
  gint nnodes;
  GHashNode **nodes;
  GHashFunc hash_func;
  GCompareFunc key_compare_func;
};


57
static void		g_hash_table_resize	 (GHashTable	*hash_table);
58 59
static GHashNode**	g_hash_table_lookup_node (GHashTable	*hash_table,
						  gconstpointer	 key);
60 61
static GHashNode*	g_hash_node_new		 (gpointer	 key,
						  gpointer	 value);
62 63
static void		g_hash_node_destroy	 (GHashNode	*hash_node);
static void		g_hash_nodes_destroy	 (GHashNode	*hash_node);
Owen Taylor's avatar
Owen Taylor committed
64 65


66
G_LOCK_DEFINE_STATIC (g_hash_global);
67

Owen Taylor's avatar
Owen Taylor committed
68 69 70 71 72 73 74 75
static GMemChunk *node_mem_chunk = NULL;
static GHashNode *node_free_list = NULL;


GHashTable*
g_hash_table_new (GHashFunc    hash_func,
		  GCompareFunc key_compare_func)
{
76
  GHashTable *hash_table;
77 78
  guint i;
  
79 80
  hash_table = g_new (GHashTable, 1);
  hash_table->size = HASH_TABLE_MIN_SIZE;
Owen Taylor's avatar
Owen Taylor committed
81
  hash_table->nnodes = 0;
82
  hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
Owen Taylor's avatar
Owen Taylor committed
83
  hash_table->key_compare_func = key_compare_func;
84
  hash_table->nodes = g_new (GHashNode*, hash_table->size);
85
  
86 87
  for (i = 0; i < hash_table->size; i++)
    hash_table->nodes[i] = NULL;
88
  
89
  return hash_table;
Owen Taylor's avatar
Owen Taylor committed
90 91 92 93 94
}

void
g_hash_table_destroy (GHashTable *hash_table)
{
95 96 97 98
  guint i;
  
  g_return_if_fail (hash_table != NULL);
  
99
  for (i = 0; i < hash_table->size; i++)
100
    g_hash_nodes_destroy (hash_table->nodes[i]);
101
  
102 103
  g_free (hash_table->nodes);
  g_free (hash_table);
Owen Taylor's avatar
Owen Taylor committed
104 105
}

106 107 108
static inline GHashNode**
g_hash_table_lookup_node (GHashTable	*hash_table,
			  gconstpointer	 key)
Owen Taylor's avatar
Owen Taylor committed
109
{
110
  GHashNode **node;
111
  
112 113
  node = &hash_table->nodes
    [(* hash_table->hash_func) (key) % hash_table->size];
114 115 116 117 118 119 120
  
  /* Hash table lookup needs to be fast.
   *  We therefore remove the extra conditional of testing
   *  whether to call the key_compare_func or not from
   *  the inner loop.
   */
  if (hash_table->key_compare_func)
121 122
    while (*node && !(*hash_table->key_compare_func) ((*node)->key, key))
      node = &(*node)->next;
123
  else
124 125
    while (*node && (*node)->key != key)
      node = &(*node)->next;
126 127 128
  
  return node;
}
Owen Taylor's avatar
Owen Taylor committed
129

130 131 132 133 134 135 136 137
gpointer
g_hash_table_lookup (GHashTable	  *hash_table,
		     gconstpointer key)
{
  GHashNode *node;
  
  g_return_val_if_fail (hash_table != NULL, NULL);
  
138
  node = *g_hash_table_lookup_node (hash_table, key);
139 140 141
  
  return node ? node->value : NULL;
}
142

143 144 145 146 147
void
g_hash_table_insert (GHashTable *hash_table,
		     gpointer	 key,
		     gpointer	 value)
{
148
  GHashNode **node;
149 150 151
  
  g_return_if_fail (hash_table != NULL);
  
152 153 154
  node = g_hash_table_lookup_node (hash_table, key);
  
  if (*node)
155 156 157 158 159 160 161
    {
      /* do not reset node->key in this place, keeping
       * the old key might be intended.
       * a g_hash_table_remove/g_hash_table_insert pair
       * can be used otherwise.
       *
       * node->key = key; */
162 163 164 165 166 167
      (*node)->value = value;
    }
  else
    {
      *node = g_hash_node_new (key, value);
      hash_table->nnodes++;
168
      g_hash_table_resize (hash_table);
Owen Taylor's avatar
Owen Taylor committed
169 170 171 172
    }
}

void
173
g_hash_table_remove (GHashTable	     *hash_table,
Owen Taylor's avatar
Owen Taylor committed
174 175
		     gconstpointer    key)
{
176
  GHashNode **node, *dest;
177 178 179
  
  g_return_if_fail (hash_table != NULL);
  
180
  node = g_hash_table_lookup_node (hash_table, key);
181

182
  if (*node)
Owen Taylor's avatar
Owen Taylor committed
183
    {
184 185 186
      dest = *node;
      (*node) = dest->next;
      g_hash_node_destroy (dest);
187
      hash_table->nnodes--;
188
  
189
      g_hash_table_resize (hash_table);
190
    }
Owen Taylor's avatar
Owen Taylor committed
191 192
}

193
gboolean
194 195 196 197
g_hash_table_lookup_extended (GHashTable	*hash_table,
			      gconstpointer	 lookup_key,
			      gpointer		*orig_key,
			      gpointer		*value)
198 199
{
  GHashNode *node;
200 201 202
  
  g_return_val_if_fail (hash_table != NULL, FALSE);
  
203
  node = *g_hash_table_lookup_node (hash_table, lookup_key);
204
  
205
  if (node)
Owen Taylor's avatar
Owen Taylor committed
206
    {
207 208 209 210 211
      if (orig_key)
	*orig_key = node->key;
      if (value)
	*value = node->value;
      return TRUE;
Owen Taylor's avatar
Owen Taylor committed
212
    }
213 214
  else
    return FALSE;
Owen Taylor's avatar
Owen Taylor committed
215 216 217 218 219
}

void
g_hash_table_freeze (GHashTable *hash_table)
{
220 221 222 223 224 225 226 227 228
#ifdef G_ENABLE_DEBUG
  static gboolean first_call = TRUE;

  if (first_call)
    {
      g_warning("g_hash_table_freeze and g_hash_table_thaw are deprecated.");
      first_call = FALSE;
    }
#endif /* G_ENABLE_DEBUG */
Owen Taylor's avatar
Owen Taylor committed
229 230 231 232 233 234 235
}

void
g_hash_table_thaw (GHashTable *hash_table)
{
}

236
guint
237 238 239
g_hash_table_foreach_remove (GHashTable	*hash_table,
			     GHRFunc	 func,
			     gpointer	 user_data)
240 241
{
  GHashNode *node, *prev;
242
  guint i;
243
  guint deleted = 0;
244 245 246 247
  
  g_return_val_if_fail (hash_table != NULL, 0);
  g_return_val_if_fail (func != NULL, 0);
  
248 249 250
  for (i = 0; i < hash_table->size; i++)
    {
    restart:
251
      
252
      prev = NULL;
253
      
254 255 256 257 258
      for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
	{
	  if ((* func) (node->key, node->value, user_data))
	    {
	      deleted += 1;
259
	      
260
	      hash_table->nnodes -= 1;
261
	      
262 263 264
	      if (prev)
		{
		  prev->next = node->next;
265
		  g_hash_node_destroy (node);
266 267 268 269 270
		  node = prev;
		}
	      else
		{
		  hash_table->nodes[i] = node->next;
271
		  g_hash_node_destroy (node);
272 273 274 275 276
		  goto restart;
		}
	    }
	}
    }
277
  
278
  g_hash_table_resize (hash_table);
279
  
280 281 282
  return deleted;
}

Owen Taylor's avatar
Owen Taylor committed
283 284
void
g_hash_table_foreach (GHashTable *hash_table,
285 286
		      GHFunc	  func,
		      gpointer	  user_data)
Owen Taylor's avatar
Owen Taylor committed
287 288 289
{
  GHashNode *node;
  gint i;
290 291 292 293
  
  g_return_if_fail (hash_table != NULL);
  g_return_if_fail (func != NULL);
  
294 295 296
  for (i = 0; i < hash_table->size; i++)
    for (node = hash_table->nodes[i]; node; node = node->next)
      (* func) (node->key, node->value, user_data);
Owen Taylor's avatar
Owen Taylor committed
297 298
}

299
/* Returns the number of elements contained in the hash table. */
300
guint
301
g_hash_table_size (GHashTable *hash_table)
302
{
303 304
  g_return_val_if_fail (hash_table != NULL, 0);
  
305 306
  return hash_table->nnodes;
}
Owen Taylor's avatar
Owen Taylor committed
307 308 309 310 311 312 313 314 315 316 317

static void
g_hash_table_resize (GHashTable *hash_table)
{
  GHashNode **new_nodes;
  GHashNode *node;
  GHashNode *next;
  gfloat nodes_per_list;
  guint hash_val;
  gint new_size;
  gint i;
318
  
319
  nodes_per_list = (gfloat) hash_table->nnodes / (gfloat) hash_table->size;
320
  
321 322 323
  if ((nodes_per_list > 0.3 || hash_table->size <= HASH_TABLE_MIN_SIZE) &&
      (nodes_per_list < 3.0 || hash_table->size >= HASH_TABLE_MAX_SIZE))
    return;
324
  
325
  new_size = CLAMP(g_spaced_primes_closest (hash_table->nnodes),
326 327
		   HASH_TABLE_MIN_SIZE,
		   HASH_TABLE_MAX_SIZE);
328
  new_nodes = g_new0 (GHashNode*, new_size);
329
  
330 331 332 333
  for (i = 0; i < hash_table->size; i++)
    for (node = hash_table->nodes[i]; node; node = next)
      {
	next = node->next;
334

335
	hash_val = (* hash_table->hash_func) (node->key) % new_size;
336

337 338 339
	node->next = new_nodes[hash_val];
	new_nodes[hash_val] = node;
      }
340
  
341 342 343 344 345
  g_free (hash_table->nodes);
  hash_table->nodes = new_nodes;
  hash_table->size = new_size;
}

Owen Taylor's avatar
Owen Taylor committed
346 347 348 349 350
static GHashNode*
g_hash_node_new (gpointer key,
		 gpointer value)
{
  GHashNode *hash_node;
351
  
352
  G_LOCK (g_hash_global);
Owen Taylor's avatar
Owen Taylor committed
353 354 355 356 357 358 359 360 361 362 363
  if (node_free_list)
    {
      hash_node = node_free_list;
      node_free_list = node_free_list->next;
    }
  else
    {
      if (!node_mem_chunk)
	node_mem_chunk = g_mem_chunk_new ("hash node mem chunk",
					  sizeof (GHashNode),
					  1024, G_ALLOC_ONLY);
364
      
Owen Taylor's avatar
Owen Taylor committed
365 366
      hash_node = g_chunk_new (GHashNode, node_mem_chunk);
    }
367
  G_UNLOCK (g_hash_global);
368
  
Owen Taylor's avatar
Owen Taylor committed
369 370 371
  hash_node->key = key;
  hash_node->value = value;
  hash_node->next = NULL;
372
  
Owen Taylor's avatar
Owen Taylor committed
373 374 375 376
  return hash_node;
}

static void
377
g_hash_node_destroy (GHashNode *hash_node)
Owen Taylor's avatar
Owen Taylor committed
378
{
379 380 381 382 383 384

#ifdef ENABLE_GC_FRIENDLY
  hash_node->key = NULL;
  hash_node->value = NULL;
#endif /* ENABLE_GC_FRIENDLY */

385
  G_LOCK (g_hash_global);
386 387
  hash_node->next = node_free_list;
  node_free_list = hash_node;
388
  G_UNLOCK (g_hash_global);
Owen Taylor's avatar
Owen Taylor committed
389 390 391
}

static void
392
g_hash_nodes_destroy (GHashNode *hash_node)
Owen Taylor's avatar
Owen Taylor committed
393
{
394 395 396
  if (hash_node)
    {
      GHashNode *node = hash_node;
397
  
398
      while (node->next)
399 400 401 402 403 404 405 406 407 408 409 410 411
	{
#ifdef ENABLE_GC_FRIENDLY
	  node->key = NULL;
	  node->value = NULL;
#endif /* ENABLE_GC_FRIENDLY */
	  node = node->next;
	}

#ifdef ENABLE_GC_FRIENDLY
      node->key = NULL;
      node->value = NULL;
#endif /* ENABLE_GC_FRIENDLY */
 
412 413 414 415 416
      G_LOCK (g_hash_global);
      node->next = node_free_list;
      node_free_list = hash_node;
      G_UNLOCK (g_hash_global);
    }
417
}