source: trunk/third/glib2/glib/gdataset.c @ 18159

Revision 18159, 14.0 KB checked in by ghudson, 22 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18158, which included commits to RCS files with non-trunk default branches.
Line 
1/* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997  Peter Mattis, Spencer Kimball and Josh MacDonald
3 *
4 * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
5 * Copyright (C) 1998 Tim Janik
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22/*
23 * Modified by the GLib Team and others 1997-2000.  See the AUTHORS
24 * file for a list of people on the GLib Team.  See the ChangeLog
25 * files for a list of changes.  These files are distributed with
26 * GLib at ftp://ftp.gtk.org/pub/gtk/.
27 */
28
29/*
30 * MT safe ; FIXME: might still freeze, watch out, not thoroughly
31 * looked at yet. 
32 */
33
34#include "config.h"
35
36#include <string.h>
37
38#include "glib.h"
39
40
41/* --- defines --- */
42#define G_QUARK_BLOCK_SIZE                      (512)
43#define G_DATA_MEM_CHUNK_PREALLOC               (128)
44#define G_DATA_CACHE_MAX                        (512)
45#define G_DATASET_MEM_CHUNK_PREALLOC            (32)
46
47
48/* --- structures --- */
49typedef struct _GDataset GDataset;
50struct _GData
51{
52  GData *next;
53  GQuark id;
54  gpointer data;
55  GDestroyNotify destroy_func;
56};
57
58struct _GDataset
59{
60  gconstpointer location;
61  GData        *datalist;
62};
63
64
65/* --- prototypes --- */
66static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
67static inline void      g_datalist_clear_i              (GData          **datalist);
68static void             g_dataset_destroy_internal      (GDataset        *dataset);
69static inline gpointer  g_data_set_internal             (GData          **datalist,
70                                                         GQuark           key_id,
71                                                         gpointer         data,
72                                                         GDestroyNotify   destroy_func,
73                                                         GDataset        *dataset);
74static void             g_data_initialize               (void);
75static inline GQuark    g_quark_new                     (gchar          *string);
76
77
78/* --- variables --- */
79G_LOCK_DEFINE_STATIC (g_dataset_global);
80static GHashTable   *g_dataset_location_ht = NULL;
81static GDataset     *g_dataset_cached = NULL; /* should this be
82                                                 threadspecific? */
83static GMemChunk    *g_dataset_mem_chunk = NULL;
84static GMemChunk    *g_data_mem_chunk = NULL;
85static GData        *g_data_cache = NULL;
86static guint         g_data_cache_length = 0;
87
88G_LOCK_DEFINE_STATIC (g_quark_global);
89static GHashTable   *g_quark_ht = NULL;
90static gchar       **g_quarks = NULL;
91static GQuark        g_quark_seq_id = 0;
92
93
94/* --- functions --- */
95
96/* HOLDS: g_dataset_global_lock */
97static inline void
98g_datalist_clear_i (GData **datalist)
99{
100  register GData *list;
101 
102  /* unlink *all* items before walking their destructors
103   */
104  list = *datalist;
105  *datalist = NULL;
106 
107  while (list)
108    {
109      register GData *prev;
110     
111      prev = list;
112      list = prev->next;
113     
114      if (prev->destroy_func)
115        {
116          G_UNLOCK (g_dataset_global);
117          prev->destroy_func (prev->data);
118          G_LOCK (g_dataset_global);
119        }
120     
121      if (g_data_cache_length < G_DATA_CACHE_MAX)
122        {
123          prev->next = g_data_cache;
124          g_data_cache = prev;
125          g_data_cache_length++;
126        }
127      else
128        g_mem_chunk_free (g_data_mem_chunk, prev);
129    }
130}
131
132void
133g_datalist_clear (GData **datalist)
134{
135  g_return_if_fail (datalist != NULL);
136 
137  G_LOCK (g_dataset_global);
138  if (!g_dataset_location_ht)
139    g_data_initialize ();
140
141  while (*datalist)
142    g_datalist_clear_i (datalist);
143  G_UNLOCK (g_dataset_global);
144}
145
146/* HOLDS: g_dataset_global_lock */
147static inline GDataset*
148g_dataset_lookup (gconstpointer dataset_location)
149{
150  register GDataset *dataset;
151 
152  if (g_dataset_cached && g_dataset_cached->location == dataset_location)
153    return g_dataset_cached;
154 
155  dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
156  if (dataset)
157    g_dataset_cached = dataset;
158 
159  return dataset;
160}
161
162/* HOLDS: g_dataset_global_lock */
163static void
164g_dataset_destroy_internal (GDataset *dataset)
165{
166  register gconstpointer dataset_location;
167 
168  dataset_location = dataset->location;
169  while (dataset)
170    {
171      if (!dataset->datalist)
172        {
173          if (dataset == g_dataset_cached)
174            g_dataset_cached = NULL;
175          g_hash_table_remove (g_dataset_location_ht, dataset_location);
176          g_mem_chunk_free (g_dataset_mem_chunk, dataset);
177          break;
178        }
179     
180      g_datalist_clear_i (&dataset->datalist);
181      dataset = g_dataset_lookup (dataset_location);
182    }
183}
184
185void
186g_dataset_destroy (gconstpointer  dataset_location)
187{
188  g_return_if_fail (dataset_location != NULL);
189 
190  G_LOCK (g_dataset_global);
191  if (g_dataset_location_ht)
192    {
193      register GDataset *dataset;
194
195      dataset = g_dataset_lookup (dataset_location);
196      if (dataset)
197        g_dataset_destroy_internal (dataset);
198    }
199  G_UNLOCK (g_dataset_global);
200}
201
202/* HOLDS: g_dataset_global_lock */
203static inline gpointer
204g_data_set_internal (GData        **datalist,
205                     GQuark         key_id,
206                     gpointer       data,
207                     GDestroyNotify destroy_func,
208                     GDataset      *dataset)
209{
210  register GData *list;
211 
212  list = *datalist;
213  if (!data)
214    {
215      register GData *prev;
216     
217      prev = NULL;
218      while (list)
219        {
220          if (list->id == key_id)
221            {
222              gpointer ret_data = NULL;
223
224              if (prev)
225                prev->next = list->next;
226              else
227                {
228                  *datalist = list->next;
229                 
230                  /* the dataset destruction *must* be done
231                   * prior to invokation of the data destroy function
232                   */
233                  if (!*datalist && dataset)
234                    g_dataset_destroy_internal (dataset);
235                }
236             
237              /* the GData struct *must* already be unlinked
238               * when invoking the destroy function.
239               * we use (data==NULL && destroy_func!=NULL) as
240               * a special hint combination to "steal"
241               * data without destroy notification
242               */
243              if (list->destroy_func && !destroy_func)
244                {
245                  G_UNLOCK (g_dataset_global);
246                  list->destroy_func (list->data);
247                  G_LOCK (g_dataset_global);
248                }
249              else
250                ret_data = list->data;
251             
252              if (g_data_cache_length < G_DATA_CACHE_MAX)
253                {
254                  list->next = g_data_cache;
255                  g_data_cache = list;
256                  g_data_cache_length++;
257                }
258              else
259                g_mem_chunk_free (g_data_mem_chunk, list);
260             
261              return ret_data;
262            }
263         
264          prev = list;
265          list = list->next;
266        }
267    }
268  else
269    {
270      while (list)
271        {
272          if (list->id == key_id)
273            {
274              if (!list->destroy_func)
275                {
276                  list->data = data;
277                  list->destroy_func = destroy_func;
278                }
279              else
280                {
281                  register GDestroyNotify dfunc;
282                  register gpointer ddata;
283                 
284                  dfunc = list->destroy_func;
285                  ddata = list->data;
286                  list->data = data;
287                  list->destroy_func = destroy_func;
288                 
289                  /* we need to have updated all structures prior to
290                   * invokation of the destroy function
291                   */
292                  G_UNLOCK (g_dataset_global);
293                  dfunc (ddata);
294                  G_LOCK (g_dataset_global);
295                }
296             
297              return NULL;
298            }
299         
300          list = list->next;
301        }
302     
303      if (g_data_cache)
304        {
305          list = g_data_cache;
306          g_data_cache = list->next;
307          g_data_cache_length--;
308        }
309      else
310        list = g_chunk_new (GData, g_data_mem_chunk);
311      list->next = *datalist;
312      list->id = key_id;
313      list->data = data;
314      list->destroy_func = destroy_func;
315      *datalist = list;
316    }
317
318  return NULL;
319}
320
321void
322g_dataset_id_set_data_full (gconstpointer  dataset_location,
323                            GQuark         key_id,
324                            gpointer       data,
325                            GDestroyNotify destroy_func)
326{
327  register GDataset *dataset;
328 
329  g_return_if_fail (dataset_location != NULL);
330  if (!data)
331    g_return_if_fail (destroy_func == NULL);
332  if (!key_id)
333    {
334      if (data)
335        g_return_if_fail (key_id > 0);
336      else
337        return;
338    }
339 
340  G_LOCK (g_dataset_global);
341  if (!g_dataset_location_ht)
342    g_data_initialize ();
343 
344  dataset = g_dataset_lookup (dataset_location);
345  if (!dataset)
346    {
347      dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
348      dataset->location = dataset_location;
349      g_datalist_init (&dataset->datalist);
350      g_hash_table_insert (g_dataset_location_ht,
351                           (gpointer) dataset->location,
352                           dataset);
353    }
354 
355  g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
356  G_UNLOCK (g_dataset_global);
357}
358
359void
360g_datalist_id_set_data_full (GData        **datalist,
361                             GQuark         key_id,
362                             gpointer       data,
363                             GDestroyNotify destroy_func)
364{
365  g_return_if_fail (datalist != NULL);
366  if (!data)
367    g_return_if_fail (destroy_func == NULL);
368  if (!key_id)
369    {
370      if (data)
371        g_return_if_fail (key_id > 0);
372      else
373        return;
374    }
375
376  G_LOCK (g_dataset_global);
377  if (!g_dataset_location_ht)
378    g_data_initialize ();
379 
380  g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
381  G_UNLOCK (g_dataset_global);
382}
383
384gpointer
385g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
386                               GQuark         key_id)
387{
388  gpointer ret_data = NULL;
389
390  g_return_val_if_fail (dataset_location != NULL, NULL);
391 
392  G_LOCK (g_dataset_global);
393  if (key_id && g_dataset_location_ht)
394    {
395      GDataset *dataset;
396 
397      dataset = g_dataset_lookup (dataset_location);
398      if (dataset)
399        ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
400    }
401  G_UNLOCK (g_dataset_global);
402
403  return ret_data;
404}
405
406gpointer
407g_datalist_id_remove_no_notify (GData   **datalist,
408                                GQuark    key_id)
409{
410  gpointer ret_data = NULL;
411
412  g_return_val_if_fail (datalist != NULL, NULL);
413
414  G_LOCK (g_dataset_global);
415  if (key_id && g_dataset_location_ht)
416    ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
417  G_UNLOCK (g_dataset_global);
418
419  return ret_data;
420}
421
422gpointer
423g_dataset_id_get_data (gconstpointer  dataset_location,
424                       GQuark         key_id)
425{
426  g_return_val_if_fail (dataset_location != NULL, NULL);
427 
428  G_LOCK (g_dataset_global);
429  if (key_id && g_dataset_location_ht)
430    {
431      register GDataset *dataset;
432     
433      dataset = g_dataset_lookup (dataset_location);
434      if (dataset)
435        {
436          register GData *list;
437         
438          for (list = dataset->datalist; list; list = list->next)
439            if (list->id == key_id)
440              {
441                G_UNLOCK (g_dataset_global);
442                return list->data;
443              }
444        }
445    }
446  G_UNLOCK (g_dataset_global);
447 
448  return NULL;
449}
450
451gpointer
452g_datalist_id_get_data (GData    **datalist,
453                        GQuark     key_id)
454{
455  g_return_val_if_fail (datalist != NULL, NULL);
456 
457  if (key_id)
458    {
459      register GData *list;
460     
461      for (list = *datalist; list; list = list->next)
462        if (list->id == key_id)
463          return list->data;
464    }
465 
466  return NULL;
467}
468
469void
470g_dataset_foreach (gconstpointer    dataset_location,
471                   GDataForeachFunc func,
472                   gpointer         user_data)
473{
474  register GDataset *dataset;
475 
476  g_return_if_fail (dataset_location != NULL);
477  g_return_if_fail (func != NULL);
478
479  G_LOCK (g_dataset_global);
480  if (g_dataset_location_ht)
481    {
482      dataset = g_dataset_lookup (dataset_location);
483      G_UNLOCK (g_dataset_global);
484      if (dataset)
485        {
486          register GData *list, *next;
487         
488          for (list = dataset->datalist; list; list = next)
489            {
490              next = list->next;
491              func (list->id, list->data, user_data);
492            }
493        }
494    }
495  else
496    {
497      G_UNLOCK (g_dataset_global);
498    }
499}
500
501void
502g_datalist_foreach (GData          **datalist,
503                    GDataForeachFunc func,
504                    gpointer         user_data)
505{
506  register GData *list, *next;
507
508  g_return_if_fail (datalist != NULL);
509  g_return_if_fail (func != NULL);
510 
511  for (list = *datalist; list; list = next)
512    {
513      next = list->next;
514      func (list->id, list->data, user_data);
515    }
516}
517
518void
519g_datalist_init (GData **datalist)
520{
521  g_return_if_fail (datalist != NULL);
522 
523  *datalist = NULL;
524}
525
526/* HOLDS: g_dataset_global_lock */
527static void
528g_data_initialize (void)
529{
530  g_return_if_fail (g_dataset_location_ht == NULL);
531
532  g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
533  g_dataset_cached = NULL;
534  g_dataset_mem_chunk =
535    g_mem_chunk_new ("GDataset MemChunk",
536                     sizeof (GDataset),
537                     sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
538                     G_ALLOC_AND_FREE);
539  g_data_mem_chunk =
540    g_mem_chunk_new ("GData MemChunk",
541                     sizeof (GData),
542                     sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
543                     G_ALLOC_AND_FREE);
544}
545
546GQuark
547g_quark_try_string (const gchar *string)
548{
549  GQuark quark = 0;
550  g_return_val_if_fail (string != NULL, 0);
551 
552  G_LOCK (g_quark_global);
553  if (g_quark_ht)
554    quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
555  G_UNLOCK (g_quark_global);
556 
557  return quark;
558}
559
560GQuark
561g_quark_from_string (const gchar *string)
562{
563  GQuark quark;
564 
565  g_return_val_if_fail (string != NULL, 0);
566 
567  G_LOCK (g_quark_global);
568  if (g_quark_ht)
569    quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
570  else
571    {
572      g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
573      quark = 0;
574    }
575 
576  if (!quark)
577    quark = g_quark_new (g_strdup (string));
578  G_UNLOCK (g_quark_global);
579 
580  return quark;
581}
582
583GQuark
584g_quark_from_static_string (const gchar *string)
585{
586  GQuark quark;
587 
588  g_return_val_if_fail (string != NULL, 0);
589 
590  G_LOCK (g_quark_global);
591  if (g_quark_ht)
592    quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
593  else
594    {
595      g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
596      quark = 0;
597    }
598
599  if (!quark)
600    quark = g_quark_new ((gchar*) string);
601  G_UNLOCK (g_quark_global);
602 
603  return quark;
604}
605
606G_CONST_RETURN gchar*
607g_quark_to_string (GQuark quark)
608{
609  gchar* result = NULL;
610  G_LOCK (g_quark_global);
611  if (quark > 0 && quark <= g_quark_seq_id)
612    result = g_quarks[quark - 1];
613  G_UNLOCK (g_quark_global);
614
615  return result;
616}
617
618/* HOLDS: g_quark_global_lock */
619static inline GQuark
620g_quark_new (gchar *string)
621{
622  GQuark quark;
623 
624  if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
625    g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
626 
627  g_quarks[g_quark_seq_id] = string;
628  g_quark_seq_id++;
629  quark = g_quark_seq_id;
630  g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
631 
632  return quark;
633}
Note: See TracBrowser for help on using the repository browser.