Logo Search packages:      
Sourcecode: tucnak2 version File versions  Download package

ghash.c

/* 
 * mofified for Tucnak Jun 22, 2006
 *
 * */

/* 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
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.      See the GNU
 * 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.
 */

/*
 * 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/. 
 */

/* 
 * MT safe
 */

#include "header.h"


#define HASH_TABLE_MIN_SIZE 11
#define HASH_TABLE_MAX_SIZE 13845163



static void       t_hash_table_resize      (THashTable      *hash_table);
static THashNode**      t_hash_table_lookup_node (THashTable      *hash_table, gconstpointer key);
static THashNode* t_hash_node_new          (gpointer   key, gpointer value);
static void       t_hash_node_destroy      (THashNode *hash_node);
static void       t_hash_nodes_destroy     (THashNode *hash_node);

G_LOCK_DEFINE_STATIC (t_hash_global);

static GMemChunk *node_mem_chunk = NULL;
static THashNode *node_free_list = NULL;


THashTable*
t_hash_table_new (GHashFunc    hash_func,
              GCompareFunc key_compare_func)
{
  THashTable *hash_table;
  guint i;
  
  hash_table = g_new (THashTable, 1);
  hash_table->size = HASH_TABLE_MIN_SIZE;
  hash_table->nnodes = 0;
  hash_table->frozen = FALSE;
  hash_table->hash_func = hash_func ? hash_func : g_direct_hash;
  hash_table->key_compare_func = key_compare_func;
  hash_table->nodes = g_new (THashNode*, hash_table->size);
  
  for (i = 0; i < hash_table->size; i++)
    hash_table->nodes[i] = NULL;
  
  return hash_table;
}

void
t_hash_table_destroy (THashTable *hash_table)
{
  guint i;
  
  g_return_if_fail (hash_table != NULL);
  
  for (i = 0; i < hash_table->size; i++)
    t_hash_nodes_destroy (hash_table->nodes[i]);
  
  g_free (hash_table->nodes);
  g_free (hash_table);
}

static inline THashNode**
t_hash_table_lookup_node (THashTable      *hash_table,
                    gconstpointer    key)
{
  THashNode **node;
  
  node = &hash_table->nodes
    [(* hash_table->hash_func) (key) % hash_table->size];
  
  /* 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)
    while (*node && !(*hash_table->key_compare_func) ((*node)->key, key))
      node = &(*node)->next;
  else
    while (*node && (*node)->key != key)
      node = &(*node)->next;
  
  return node;
}

gpointer
t_hash_table_lookup (THashTable       *hash_table,
                 gconstpointer key)
{
  THashNode *node;
  
  g_return_val_if_fail (hash_table != NULL, NULL);
  
  node = *t_hash_table_lookup_node (hash_table, key);
  
  return node ? node->value : NULL;
}

void
t_hash_table_insert (THashTable *hash_table,
                 gpointer      key,
                 gpointer      value)
{
  THashNode **node;
  
  g_return_if_fail (hash_table != NULL);
  
  node = t_hash_table_lookup_node (hash_table, key);
  
  if (*node)
    {
      /* do not reset node->key in this place, keeping
       * the old key might be intended.
       * a t_hash_table_remove/t_hash_table_insert pair
       * can be used otherwise.
       *
       * node->key = key; */
      (*node)->value = value;
    }
  else
    {
      *node = t_hash_node_new (key, value);
      hash_table->nnodes++;
      if (!hash_table->frozen)
      t_hash_table_resize (hash_table);
    }
}

void
t_hash_table_remove (THashTable          *hash_table,
                 gconstpointer    key)
{
  THashNode **node, *dest;
  
  g_return_if_fail (hash_table != NULL);
  
  node = t_hash_table_lookup_node (hash_table, key);

  if (*node)
    {
      dest = *node;
      (*node) = dest->next;
      t_hash_node_destroy (dest);
      hash_table->nnodes--;
  
      if (!hash_table->frozen)
        t_hash_table_resize (hash_table);
    }
}

gboolean
t_hash_table_lookup_extended (THashTable  *hash_table,
                        gconstpointer      lookup_key,
                        gpointer          *orig_key,
                        gpointer          *value)
{
  THashNode *node;
  
  g_return_val_if_fail (hash_table != NULL, FALSE);
  
  node = *t_hash_table_lookup_node (hash_table, lookup_key);
  
  if (node)
    {
      if (orig_key)
      *orig_key = node->key;
      if (value)
      *value = node->value;
      return TRUE;
    }
  else
    return FALSE;
}

void
t_hash_table_freeze (THashTable *hash_table)
{
  g_return_if_fail (hash_table != NULL);
  
  hash_table->frozen++;
}

void
t_hash_table_thaw (THashTable *hash_table)
{
  g_return_if_fail (hash_table != NULL);
  
  if (hash_table->frozen)
    if (!(--hash_table->frozen))
      t_hash_table_resize (hash_table);
}

guint
t_hash_table_foreach_remove (THashTable   *hash_table,
                       GHRFunc       func,
                       gpointer      user_data)
{
  THashNode *node, *prev;
  guint i;
  guint deleted = 0;
  
  g_return_val_if_fail (hash_table != NULL, 0);
  g_return_val_if_fail (func != NULL, 0);
  
  for (i = 0; i < hash_table->size; i++)
    {
    restart:
      
      prev = NULL;
      
      for (node = hash_table->nodes[i]; node; prev = node, node = node->next)
      {
        if ((* func) (node->key, node->value, user_data))
          {
            deleted += 1;
            
            hash_table->nnodes -= 1;
            
            if (prev)
            {
              prev->next = node->next;
              t_hash_node_destroy (node);
              node = prev;
            }
            else
            {
              hash_table->nodes[i] = node->next;
              t_hash_node_destroy (node);
              goto restart;
            }
          }
      }
    }
  
  if (!hash_table->frozen)
    t_hash_table_resize (hash_table);
  
  return deleted;
}

void
t_hash_table_foreach (THashTable *hash_table,
                  GHFunc        func,
                  gpointer      user_data)
{
  THashNode *node;
  gint i;
  
  g_return_if_fail (hash_table != NULL);
  g_return_if_fail (func != NULL);
  
  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);
}

/* Returns the number of elements contained in the hash table. */
guint
t_hash_table_size (THashTable *hash_table)
{
  g_return_val_if_fail (hash_table != NULL, 0);
  
  return hash_table->nnodes;
}

static void
t_hash_table_resize (THashTable *hash_table)
{
  THashNode **new_nodes;
  THashNode *node;
  THashNode *next;
  gfloat nodes_per_list;
  guint hash_val;
  gint new_size;
  gint i;
  
  nodes_per_list = (gfloat) hash_table->nnodes / (gfloat) hash_table->size;
  
  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;
  
  new_size = CLAMP(g_spaced_primes_closest (hash_table->nnodes),
               HASH_TABLE_MIN_SIZE,
               HASH_TABLE_MAX_SIZE);
  new_nodes = g_new0 (THashNode*, new_size);
  
  for (i = 0; i < hash_table->size; i++)
    for (node = hash_table->nodes[i]; node; node = next)
      {
      next = node->next;

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

      node->next = new_nodes[hash_val];
      new_nodes[hash_val] = node;
      }
  
  g_free (hash_table->nodes);
  hash_table->nodes = new_nodes;
  hash_table->size = new_size;
}

static THashNode*
t_hash_node_new (gpointer key,
             gpointer value)
{
  THashNode *hash_node;
  
  G_LOCK (t_hash_global);
  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 (THashNode),
                                1024, G_ALLOC_ONLY);
      
      hash_node = g_chunk_new (THashNode, node_mem_chunk);
    }
  G_UNLOCK (t_hash_global);
  
  hash_node->key = key;
  hash_node->value = value;
  hash_node->next = NULL;
  
  return hash_node;
}

static void
t_hash_node_destroy (THashNode *hash_node)
{
  G_LOCK (t_hash_global);
  hash_node->next = node_free_list;
  node_free_list = hash_node;
  G_UNLOCK (t_hash_global);
}

static void
t_hash_nodes_destroy (THashNode *hash_node)
{
  if (hash_node)
    {
      THashNode *node = hash_node;
  
      while (node->next)
        node = node->next;
  
      G_LOCK (t_hash_global);
      node->next = node_free_list;
      node_free_list = hash_node;
      G_UNLOCK (t_hash_global);
    }
}

Generated by  Doxygen 1.6.0   Back to index