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

gdataset.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  * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
00005  * Copyright (C) 1998 Tim Janik
00006  *
00007  * This library is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Library General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2 of the License, or (at your option) any later version.
00011  *
00012  * This library is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Library General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Library General Public
00018  * License along with this library; if not, write to the Free Software
00019  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
00020  */
00021 
00022 /*
00023  * Modified by the GLib Team and others 1997-1999.  See the AUTHORS
00024  * file for a list of people on the GLib Team.  See the ChangeLog
00025  * files for a list of changes.  These files are distributed with
00026  * GLib at ftp://ftp.gtk.org/pub/gtk/. 
00027  */
00028 
00029 /* 
00030  * MT safe ; FIXME: might still freeze, watch out, not thoroughly
00031  * looked at yet.  
00032  */
00033 
00034 #include        <string.h>
00035 #include        "glib.h"
00036 
00037 
00038 
00039 /* --- defines --- */
00040 #define G_QUARK_BLOCK_SIZE                      (512)
00041 #define G_DATA_MEM_CHUNK_PREALLOC               (128)
00042 #define G_DATA_CACHE_MAX                        (512)
00043 #define G_DATASET_MEM_CHUNK_PREALLOC            (32)
00044 
00045 
00046 /* --- structures --- */
00047 typedef struct _GDataset GDataset;
00048 struct _GData
00049 {
00050   GData *next;
00051   GQuark id;
00052   gpointer data;
00053   GDestroyNotify destroy_func;
00054 };
00055 
00056 struct _GDataset
00057 {
00058   gconstpointer location;
00059   GData        *datalist;
00060 };
00061 
00062 
00063 /* --- prototypes --- */
00064 static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
00065 static inline void      g_datalist_clear_i              (GData          **datalist);
00066 static void             g_dataset_destroy_internal      (GDataset        *dataset);
00067 static inline void      g_data_set_internal             (GData          **datalist,
00068                                                          GQuark           key_id,
00069                                                          gpointer         data,
00070                                                          GDestroyNotify   destroy_func,
00071                                                          GDataset        *dataset);
00072 static void             g_data_initialize               (void);
00073 static inline GQuark    g_quark_new                     (gchar          *string);
00074 
00075 
00076 /* --- variables --- */
00077 G_LOCK_DEFINE_STATIC (g_dataset_global);
00078 static GHashTable   *g_dataset_location_ht = NULL;
00079 static GDataset     *g_dataset_cached = NULL; /* should this be
00080                                                  threadspecific? */
00081 static GMemChunk    *g_dataset_mem_chunk = NULL;
00082 static GMemChunk    *g_data_mem_chunk = NULL;
00083 static GData        *g_data_cache = NULL;
00084 static guint         g_data_cache_length = 0;
00085 
00086 G_LOCK_DEFINE_STATIC (g_quark_global);
00087 static GHashTable   *g_quark_ht = NULL;
00088 static gchar       **g_quarks = NULL;
00089 static GQuark        g_quark_seq_id = 0;
00090 
00091 
00092 /* --- functions --- */
00093 
00094 /* HOLDS: g_dataset_global_lock */
00095 static inline void
00096 g_datalist_clear_i (GData **datalist)
00097 {
00098   register GData *list;
00099   
00100   /* unlink *all* items before walking their destructors
00101    */
00102   list = *datalist;
00103   *datalist = NULL;
00104   
00105   while (list)
00106     {
00107       register GData *prev;
00108       
00109       prev = list;
00110       list = prev->next;
00111       
00112       if (prev->destroy_func)
00113         {
00114           G_UNLOCK (g_dataset_global);
00115           prev->destroy_func (prev->data);
00116           G_LOCK (g_dataset_global);
00117         }
00118       
00119       if (g_data_cache_length < G_DATA_CACHE_MAX)
00120         {
00121           prev->next = g_data_cache;
00122           g_data_cache = prev;
00123           g_data_cache_length++;
00124         }
00125       else
00126         g_mem_chunk_free (g_data_mem_chunk, prev);
00127     }
00128 }
00129 
00130 void
00131 g_datalist_clear (GData **datalist)
00132 {
00133   g_return_if_fail (datalist != NULL);
00134   
00135   G_LOCK (g_dataset_global);
00136   if (!g_dataset_location_ht)
00137     g_data_initialize ();
00138 
00139   while (*datalist)
00140     g_datalist_clear_i (datalist);
00141   G_UNLOCK (g_dataset_global);
00142 }
00143 
00144 /* HOLDS: g_dataset_global_lock */
00145 static inline GDataset*
00146 g_dataset_lookup (gconstpointer dataset_location)
00147 {
00148   register GDataset *dataset;
00149   
00150   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
00151     return g_dataset_cached;
00152   
00153   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
00154   if (dataset)
00155     g_dataset_cached = dataset;
00156   
00157   return dataset;
00158 }
00159 
00160 /* HOLDS: g_dataset_global_lock */
00161 static void
00162 g_dataset_destroy_internal (GDataset *dataset)
00163 {
00164   register gconstpointer dataset_location;
00165   
00166   dataset_location = dataset->location;
00167   while (dataset)
00168     {
00169       if (!dataset->datalist)
00170         {
00171           if (dataset == g_dataset_cached)
00172             g_dataset_cached = NULL;
00173           g_hash_table_remove (g_dataset_location_ht, dataset_location);
00174           g_mem_chunk_free (g_dataset_mem_chunk, dataset);
00175           break;
00176         }
00177       
00178       g_datalist_clear_i (&dataset->datalist);
00179       dataset = g_dataset_lookup (dataset_location);
00180     }
00181 }
00182 
00183 void
00184 g_dataset_destroy (gconstpointer  dataset_location)
00185 {
00186   g_return_if_fail (dataset_location != NULL);
00187   
00188   G_LOCK (g_dataset_global);
00189   if (g_dataset_location_ht)
00190     {
00191       register GDataset *dataset;
00192 
00193       dataset = g_dataset_lookup (dataset_location);
00194       if (dataset)
00195         g_dataset_destroy_internal (dataset);
00196     }
00197   G_UNLOCK (g_dataset_global);
00198 }
00199 
00200 /* HOLDS: g_dataset_global_lock */
00201 static inline void
00202 g_data_set_internal (GData        **datalist,
00203                      GQuark         key_id,
00204                      gpointer       data,
00205                      GDestroyNotify destroy_func,
00206                      GDataset      *dataset)
00207 {
00208   register GData *list;
00209   
00210   list = *datalist;
00211   if (!data)
00212     {
00213       register GData *prev;
00214       
00215       prev = NULL;
00216       while (list)
00217         {
00218           if (list->id == key_id)
00219             {
00220               if (prev)
00221                 prev->next = list->next;
00222               else
00223                 {
00224                   *datalist = list->next;
00225                   
00226                   /* the dataset destruction *must* be done
00227                    * prior to invokation of the data destroy function
00228                    */
00229                   if (!*datalist && dataset)
00230                     g_dataset_destroy_internal (dataset);
00231                 }
00232               
00233               /* the GData struct *must* already be unlinked
00234                * when invoking the destroy function.
00235                * we use (data==NULL && destroy_func!=NULL) as
00236                * a special hint combination to "steal"
00237                * data without destroy notification
00238                */
00239               if (list->destroy_func && !destroy_func)
00240                 {
00241                   G_UNLOCK (g_dataset_global);
00242                   list->destroy_func (list->data);
00243                   G_LOCK (g_dataset_global);
00244                 }
00245               
00246               if (g_data_cache_length < G_DATA_CACHE_MAX)
00247                 {
00248                   list->next = g_data_cache;
00249                   g_data_cache = list;
00250                   g_data_cache_length++;
00251                 }
00252               else
00253                 g_mem_chunk_free (g_data_mem_chunk, list);
00254               
00255               return;
00256             }
00257           
00258           prev = list;
00259           list = list->next;
00260         }
00261     }
00262   else
00263     {
00264       while (list)
00265         {
00266           if (list->id == key_id)
00267             {
00268               if (!list->destroy_func)
00269                 {
00270                   list->data = data;
00271                   list->destroy_func = destroy_func;
00272                 }
00273               else
00274                 {
00275                   register GDestroyNotify dfunc;
00276                   register gpointer ddata;
00277                   
00278                   dfunc = list->destroy_func;
00279                   ddata = list->data;
00280                   list->data = data;
00281                   list->destroy_func = destroy_func;
00282                   
00283                   /* we need to have updated all structures prior to
00284                    * invokation of the destroy function
00285                    */
00286                   G_UNLOCK (g_dataset_global);
00287                   dfunc (ddata);
00288                   G_LOCK (g_dataset_global);
00289                 }
00290               
00291               return;
00292             }
00293           
00294           list = list->next;
00295         }
00296       
00297       if (g_data_cache)
00298         {
00299           list = g_data_cache;
00300           g_data_cache = list->next;
00301           g_data_cache_length--;
00302         }
00303       else
00304         list = g_chunk_new (GData, g_data_mem_chunk);
00305       list->next = *datalist;
00306       list->id = key_id;
00307       list->data = data;
00308       list->destroy_func = destroy_func;
00309       *datalist = list;
00310     }
00311 }
00312 
00313 void
00314 g_dataset_id_set_data_full (gconstpointer  dataset_location,
00315                             GQuark         key_id,
00316                             gpointer       data,
00317                             GDestroyNotify destroy_func)
00318 {
00319   register GDataset *dataset;
00320   
00321   g_return_if_fail (dataset_location != NULL);
00322   if (!data)
00323     g_return_if_fail (destroy_func == NULL);
00324   if (!key_id)
00325     {
00326       if (data)
00327         g_return_if_fail (key_id > 0);
00328       else
00329         return;
00330     }
00331   
00332   G_LOCK (g_dataset_global);
00333   if (!g_dataset_location_ht)
00334     g_data_initialize ();
00335  
00336   dataset = g_dataset_lookup (dataset_location);
00337   if (!dataset)
00338     {
00339       dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
00340       dataset->location = dataset_location;
00341       g_datalist_init (&dataset->datalist);
00342       g_hash_table_insert (g_dataset_location_ht, 
00343                            (gpointer) dataset->location,
00344                            dataset);
00345     }
00346   
00347   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
00348   G_UNLOCK (g_dataset_global);
00349 }
00350 
00351 void
00352 g_datalist_id_set_data_full (GData        **datalist,
00353                              GQuark         key_id,
00354                              gpointer       data,
00355                              GDestroyNotify destroy_func)
00356 {
00357   g_return_if_fail (datalist != NULL);
00358   if (!data)
00359     g_return_if_fail (destroy_func == NULL);
00360   if (!key_id)
00361     {
00362       if (data)
00363         g_return_if_fail (key_id > 0);
00364       else
00365         return;
00366     }
00367 
00368   G_LOCK (g_dataset_global);
00369   if (!g_dataset_location_ht)
00370     g_data_initialize ();
00371   
00372   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
00373   G_UNLOCK (g_dataset_global);
00374 }
00375 
00376 void
00377 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
00378                                GQuark         key_id)
00379 {
00380   g_return_if_fail (dataset_location != NULL);
00381   
00382   G_LOCK (g_dataset_global);
00383   if (key_id && g_dataset_location_ht)
00384     {
00385       GDataset *dataset;
00386   
00387       dataset = g_dataset_lookup (dataset_location);
00388       if (dataset)
00389         g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
00390     } 
00391   G_UNLOCK (g_dataset_global);
00392 }
00393 
00394 void
00395 g_datalist_id_remove_no_notify (GData   **datalist,
00396                                 GQuark    key_id)
00397 {
00398   g_return_if_fail (datalist != NULL);
00399 
00400   G_LOCK (g_dataset_global);
00401   if (key_id && g_dataset_location_ht)
00402     g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
00403   G_UNLOCK (g_dataset_global);
00404 }
00405 
00406 gpointer
00407 g_dataset_id_get_data (gconstpointer  dataset_location,
00408                        GQuark         key_id)
00409 {
00410   g_return_val_if_fail (dataset_location != NULL, NULL);
00411   
00412   G_LOCK (g_dataset_global);
00413   if (key_id && g_dataset_location_ht)
00414     {
00415       register GDataset *dataset;
00416       
00417       dataset = g_dataset_lookup (dataset_location);
00418       if (dataset)
00419         {
00420           register GData *list;
00421           
00422           for (list = dataset->datalist; list; list = list->next)
00423             if (list->id == key_id)
00424               {
00425                 G_UNLOCK (g_dataset_global);
00426                 return list->data;
00427               }
00428         }
00429     }
00430   G_UNLOCK (g_dataset_global);
00431  
00432   return NULL;
00433 }
00434 
00435 gpointer
00436 g_datalist_id_get_data (GData    **datalist,
00437                         GQuark     key_id)
00438 {
00439   g_return_val_if_fail (datalist != NULL, NULL);
00440   
00441   if (key_id)
00442     {
00443       register GData *list;
00444       
00445       for (list = *datalist; list; list = list->next)
00446         if (list->id == key_id)
00447           return list->data;
00448     }
00449   
00450   return NULL;
00451 }
00452 
00453 void
00454 g_dataset_foreach (gconstpointer    dataset_location,
00455                    GDataForeachFunc func,
00456                    gpointer         user_data)
00457 {
00458   register GDataset *dataset;
00459   
00460   g_return_if_fail (dataset_location != NULL);
00461   g_return_if_fail (func != NULL);
00462 
00463   G_LOCK (g_dataset_global);
00464   if (g_dataset_location_ht)
00465     {
00466       dataset = g_dataset_lookup (dataset_location);
00467       G_UNLOCK (g_dataset_global);
00468       if (dataset)
00469         {
00470           register GData *list;
00471           
00472           for (list = dataset->datalist; list; list = list->next)
00473               func (list->id, list->data, user_data);
00474         }
00475     }
00476   else
00477     {
00478       G_UNLOCK (g_dataset_global);
00479     }
00480 }
00481 
00482 void
00483 g_datalist_foreach (GData          **datalist,
00484                     GDataForeachFunc func,
00485                     gpointer         user_data)
00486 {
00487   register GData *list;
00488 
00489   g_return_if_fail (datalist != NULL);
00490   g_return_if_fail (func != NULL);
00491   
00492   for (list = *datalist; list; list = list->next)
00493     func (list->id, list->data, user_data);
00494 }
00495 
00496 void
00497 g_datalist_init (GData **datalist)
00498 {
00499   g_return_if_fail (datalist != NULL);
00500   
00501   *datalist = NULL;
00502 }
00503 
00504 /* HOLDS: g_dataset_global_lock */
00505 static void
00506 g_data_initialize (void)
00507 {
00508   g_return_if_fail (g_dataset_location_ht == NULL);
00509 
00510   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
00511   g_dataset_cached = NULL;
00512   g_dataset_mem_chunk =
00513     g_mem_chunk_new ("GDataset MemChunk",
00514                      sizeof (GDataset),
00515                      sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
00516                      G_ALLOC_AND_FREE);
00517   g_data_mem_chunk =
00518     g_mem_chunk_new ("GData MemChunk",
00519                      sizeof (GData),
00520                      sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
00521                      G_ALLOC_AND_FREE);
00522 }
00523 
00524 GQuark
00525 g_quark_try_string (const gchar *string)
00526 {
00527   GQuark quark = 0;
00528   g_return_val_if_fail (string != NULL, 0);
00529   
00530   G_LOCK (g_quark_global);
00531   if (g_quark_ht)
00532     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
00533   G_UNLOCK (g_quark_global);
00534   
00535   return quark;
00536 }
00537 
00538 GQuark
00539 g_quark_from_string (const gchar *string)
00540 {
00541   GQuark quark;
00542   
00543   g_return_val_if_fail (string != NULL, 0);
00544   
00545   G_LOCK (g_quark_global);
00546   if (g_quark_ht)
00547     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
00548   else
00549     {
00550       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
00551       quark = 0;
00552     }
00553   
00554   if (!quark)
00555     quark = g_quark_new (g_strdup (string));
00556   G_UNLOCK (g_quark_global);
00557   
00558   return quark;
00559 }
00560 
00561 GQuark
00562 g_quark_from_static_string (const gchar *string)
00563 {
00564   GQuark quark;
00565   
00566   g_return_val_if_fail (string != NULL, 0);
00567   
00568   G_LOCK (g_quark_global);
00569   if (g_quark_ht)
00570     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
00571   else
00572     {
00573       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
00574       quark = 0;
00575     }
00576 
00577   if (!quark)
00578     quark = g_quark_new ((gchar*) string);
00579   G_UNLOCK (g_quark_global);
00580  
00581   return quark;
00582 }
00583 
00584 gchar*
00585 g_quark_to_string (GQuark quark)
00586 {
00587   gchar* result = NULL;
00588   G_LOCK (g_quark_global);
00589   if (quark > 0 && quark <= g_quark_seq_id)
00590     result = g_quarks[quark - 1];
00591   G_UNLOCK (g_quark_global);
00592 
00593   return result;
00594 }
00595 
00596 /* HOLDS: g_quark_global_lock */
00597 static inline GQuark
00598 g_quark_new (gchar *string)
00599 {
00600   GQuark quark;
00601   
00602   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
00603     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
00604   
00605   g_quarks[g_quark_seq_id] = string;
00606   g_quark_seq_id++;
00607   quark = g_quark_seq_id;
00608   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
00609   
00610   return quark;
00611 }

© sourcejam.com 2005-2008