45d3ba66bf05029b55979a0d26221a95af06f70e
[platform/upstream/glib.git] / glib / gdataset.c
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 ; except for g_data*_foreach()
31  */
32
33 #include "config.h"
34
35 #include <string.h>
36
37 #include "glib.h"
38 #include "galias.h"
39 #include "gdatasetprivate.h"
40
41
42 /* --- defines --- */
43 #define G_QUARK_BLOCK_SIZE                      (512)
44 #define G_DATA_MEM_CHUNK_PREALLOC               (128)
45 #define G_DATA_CACHE_MAX                        (512)
46 #define G_DATASET_MEM_CHUNK_PREALLOC            (32)
47
48 /* datalist pointer modifications have to be done with the g_dataset_global mutex held */
49 #define G_DATALIST_GET_POINTER(datalist)                                                \
50   ((GData*) ((gsize) *(datalist) & ~(gsize) G_DATALIST_FLAGS_MASK))
51 #define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START {                        \
52   *(datalist) = (GData*) (G_DATALIST_GET_FLAGS (datalist) |                             \
53                           (gsize) pointer);                                             \
54 } G_STMT_END
55
56
57 /* --- structures --- */
58 typedef struct _GDataset GDataset;
59 struct _GData
60 {
61   GData *next;
62   GQuark id;
63   gpointer data;
64   GDestroyNotify destroy_func;
65 };
66
67 struct _GDataset
68 {
69   gconstpointer location;
70   GData        *datalist;
71 };
72
73
74 /* --- prototypes --- */
75 static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
76 static inline void      g_datalist_clear_i              (GData          **datalist);
77 static void             g_dataset_destroy_internal      (GDataset        *dataset);
78 static inline gpointer  g_data_set_internal             (GData          **datalist,
79                                                          GQuark           key_id,
80                                                          gpointer         data,
81                                                          GDestroyNotify   destroy_func,
82                                                          GDataset        *dataset);
83 static void             g_data_initialize               (void);
84 static inline GQuark    g_quark_new                     (gchar          *string);
85
86
87 /* --- variables --- */
88 G_LOCK_DEFINE_STATIC (g_dataset_global);
89 static GHashTable   *g_dataset_location_ht = NULL;
90 static GDataset     *g_dataset_cached = NULL; /* should this be
91                                                  threadspecific? */
92 static GMemChunk    *g_dataset_mem_chunk = NULL;
93 static GMemChunk    *g_data_mem_chunk = NULL;
94 static GData        *g_data_cache = NULL;
95 static guint         g_data_cache_length = 0;
96
97 G_LOCK_DEFINE_STATIC (g_quark_global);
98 static GHashTable   *g_quark_ht = NULL;
99 static gchar       **g_quarks = NULL;
100 static GQuark        g_quark_seq_id = 0;
101
102 /* --- functions --- */
103
104 /* HOLDS: g_dataset_global_lock */
105 static inline void
106 g_datalist_clear_i (GData **datalist)
107 {
108   register GData *list;
109   
110   /* unlink *all* items before walking their destructors
111    */
112   list = G_DATALIST_GET_POINTER (datalist);
113   G_DATALIST_SET_POINTER (datalist, NULL);
114   
115   while (list)
116     {
117       register GData *prev;
118       
119       prev = list;
120       list = prev->next;
121       
122       if (prev->destroy_func)
123         {
124           G_UNLOCK (g_dataset_global);
125           prev->destroy_func (prev->data);
126           G_LOCK (g_dataset_global);
127         }
128       
129       if (g_data_cache_length < G_DATA_CACHE_MAX)
130         {
131           prev->next = g_data_cache;
132           g_data_cache = prev;
133           g_data_cache_length++;
134         }
135       else
136         g_mem_chunk_free (g_data_mem_chunk, prev);
137     }
138 }
139
140 void
141 g_datalist_clear (GData **datalist)
142 {
143   g_return_if_fail (datalist != NULL);
144   
145   G_LOCK (g_dataset_global);
146   if (!g_dataset_location_ht)
147     g_data_initialize ();
148
149   while (G_DATALIST_GET_POINTER (datalist))
150     g_datalist_clear_i (datalist);
151   G_UNLOCK (g_dataset_global);
152 }
153
154 /* HOLDS: g_dataset_global_lock */
155 static inline GDataset*
156 g_dataset_lookup (gconstpointer dataset_location)
157 {
158   register GDataset *dataset;
159   
160   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
161     return g_dataset_cached;
162   
163   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
164   if (dataset)
165     g_dataset_cached = dataset;
166   
167   return dataset;
168 }
169
170 /* HOLDS: g_dataset_global_lock */
171 static void
172 g_dataset_destroy_internal (GDataset *dataset)
173 {
174   register gconstpointer dataset_location;
175   
176   dataset_location = dataset->location;
177   while (dataset)
178     {
179       if (!dataset->datalist)
180         {
181           if (dataset == g_dataset_cached)
182             g_dataset_cached = NULL;
183           g_hash_table_remove (g_dataset_location_ht, dataset_location);
184           g_mem_chunk_free (g_dataset_mem_chunk, dataset);
185           break;
186         }
187       
188       g_datalist_clear_i (&dataset->datalist);
189       dataset = g_dataset_lookup (dataset_location);
190     }
191 }
192
193 void
194 g_dataset_destroy (gconstpointer  dataset_location)
195 {
196   g_return_if_fail (dataset_location != NULL);
197   
198   G_LOCK (g_dataset_global);
199   if (g_dataset_location_ht)
200     {
201       register GDataset *dataset;
202
203       dataset = g_dataset_lookup (dataset_location);
204       if (dataset)
205         g_dataset_destroy_internal (dataset);
206     }
207   G_UNLOCK (g_dataset_global);
208 }
209
210 /* HOLDS: g_dataset_global_lock */
211 static inline gpointer
212 g_data_set_internal (GData        **datalist,
213                      GQuark         key_id,
214                      gpointer       data,
215                      GDestroyNotify destroy_func,
216                      GDataset      *dataset)
217 {
218   register GData *list;
219   
220   list = G_DATALIST_GET_POINTER (datalist);
221   if (!data)
222     {
223       register GData *prev;
224       
225       prev = NULL;
226       while (list)
227         {
228           if (list->id == key_id)
229             {
230               gpointer ret_data = NULL;
231
232               if (prev)
233                 prev->next = list->next;
234               else
235                 {
236                   G_DATALIST_SET_POINTER (datalist, list->next);
237                   
238                   /* the dataset destruction *must* be done
239                    * prior to invocation of the data destroy function
240                    */
241                   if (!list->next && dataset)
242                     g_dataset_destroy_internal (dataset);
243                 }
244               
245               /* the GData struct *must* already be unlinked
246                * when invoking the destroy function.
247                * we use (data==NULL && destroy_func!=NULL) as
248                * a special hint combination to "steal"
249                * data without destroy notification
250                */
251               if (list->destroy_func && !destroy_func)
252                 {
253                   G_UNLOCK (g_dataset_global);
254                   list->destroy_func (list->data);
255                   G_LOCK (g_dataset_global);
256                 }
257               else
258                 ret_data = list->data;
259               
260               if (g_data_cache_length < G_DATA_CACHE_MAX)
261                 {
262                   list->next = g_data_cache;
263                   g_data_cache = list;
264                   g_data_cache_length++;
265                 }
266               else
267                 g_mem_chunk_free (g_data_mem_chunk, list);
268               
269               return ret_data;
270             }
271           
272           prev = list;
273           list = list->next;
274         }
275     }
276   else
277     {
278       while (list)
279         {
280           if (list->id == key_id)
281             {
282               if (!list->destroy_func)
283                 {
284                   list->data = data;
285                   list->destroy_func = destroy_func;
286                 }
287               else
288                 {
289                   register GDestroyNotify dfunc;
290                   register gpointer ddata;
291                   
292                   dfunc = list->destroy_func;
293                   ddata = list->data;
294                   list->data = data;
295                   list->destroy_func = destroy_func;
296                   
297                   /* we need to have updated all structures prior to
298                    * invocation of the destroy function
299                    */
300                   G_UNLOCK (g_dataset_global);
301                   dfunc (ddata);
302                   G_LOCK (g_dataset_global);
303                 }
304               
305               return NULL;
306             }
307           
308           list = list->next;
309         }
310       
311       if (g_data_cache)
312         {
313           list = g_data_cache;
314           g_data_cache = list->next;
315           g_data_cache_length--;
316         }
317       else
318         list = g_chunk_new (GData, g_data_mem_chunk);
319       list->next = G_DATALIST_GET_POINTER (datalist);
320       list->id = key_id;
321       list->data = data;
322       list->destroy_func = destroy_func;
323       G_DATALIST_SET_POINTER (datalist, list);
324     }
325
326   return NULL;
327 }
328
329 void
330 g_dataset_id_set_data_full (gconstpointer  dataset_location,
331                             GQuark         key_id,
332                             gpointer       data,
333                             GDestroyNotify destroy_func)
334 {
335   register GDataset *dataset;
336   
337   g_return_if_fail (dataset_location != NULL);
338   if (!data)
339     g_return_if_fail (destroy_func == NULL);
340   if (!key_id)
341     {
342       if (data)
343         g_return_if_fail (key_id > 0);
344       else
345         return;
346     }
347   
348   G_LOCK (g_dataset_global);
349   if (!g_dataset_location_ht)
350     g_data_initialize ();
351  
352   dataset = g_dataset_lookup (dataset_location);
353   if (!dataset)
354     {
355       dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
356       dataset->location = dataset_location;
357       g_datalist_init (&dataset->datalist);
358       g_hash_table_insert (g_dataset_location_ht, 
359                            (gpointer) dataset->location,
360                            dataset);
361     }
362   
363   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
364   G_UNLOCK (g_dataset_global);
365 }
366
367 void
368 g_datalist_id_set_data_full (GData        **datalist,
369                              GQuark         key_id,
370                              gpointer       data,
371                              GDestroyNotify destroy_func)
372 {
373   g_return_if_fail (datalist != NULL);
374   if (!data)
375     g_return_if_fail (destroy_func == NULL);
376   if (!key_id)
377     {
378       if (data)
379         g_return_if_fail (key_id > 0);
380       else
381         return;
382     }
383
384   G_LOCK (g_dataset_global);
385   if (!g_dataset_location_ht)
386     g_data_initialize ();
387   
388   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
389   G_UNLOCK (g_dataset_global);
390 }
391
392 gpointer
393 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
394                                GQuark         key_id)
395 {
396   gpointer ret_data = NULL;
397
398   g_return_val_if_fail (dataset_location != NULL, NULL);
399   
400   G_LOCK (g_dataset_global);
401   if (key_id && g_dataset_location_ht)
402     {
403       GDataset *dataset;
404   
405       dataset = g_dataset_lookup (dataset_location);
406       if (dataset)
407         ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
408     } 
409   G_UNLOCK (g_dataset_global);
410
411   return ret_data;
412 }
413
414 gpointer
415 g_datalist_id_remove_no_notify (GData   **datalist,
416                                 GQuark    key_id)
417 {
418   gpointer ret_data = NULL;
419
420   g_return_val_if_fail (datalist != NULL, NULL);
421
422   G_LOCK (g_dataset_global);
423   if (key_id && g_dataset_location_ht)
424     ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
425   G_UNLOCK (g_dataset_global);
426
427   return ret_data;
428 }
429
430 gpointer
431 g_dataset_id_get_data (gconstpointer  dataset_location,
432                        GQuark         key_id)
433 {
434   g_return_val_if_fail (dataset_location != NULL, NULL);
435   
436   G_LOCK (g_dataset_global);
437   if (key_id && g_dataset_location_ht)
438     {
439       register GDataset *dataset;
440       
441       dataset = g_dataset_lookup (dataset_location);
442       if (dataset)
443         {
444           register GData *list;
445           
446           for (list = dataset->datalist; list; list = list->next)
447             if (list->id == key_id)
448               {
449                 G_UNLOCK (g_dataset_global);
450                 return list->data;
451               }
452         }
453     }
454   G_UNLOCK (g_dataset_global);
455  
456   return NULL;
457 }
458
459 gpointer
460 g_datalist_id_get_data (GData    **datalist,
461                         GQuark     key_id)
462 {
463   gpointer data = NULL;
464   g_return_val_if_fail (datalist != NULL, NULL);
465   if (key_id)
466     {
467       register GData *list;
468       G_LOCK (g_dataset_global);
469       for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next)
470         if (list->id == key_id)
471           {
472             data = list->data;
473             break;
474           }
475       G_UNLOCK (g_dataset_global);
476     }
477   return data;
478 }
479
480 void
481 g_dataset_foreach (gconstpointer    dataset_location,
482                    GDataForeachFunc func,
483                    gpointer         user_data)
484 {
485   register GDataset *dataset;
486   
487   g_return_if_fail (dataset_location != NULL);
488   g_return_if_fail (func != NULL);
489
490   G_LOCK (g_dataset_global);
491   if (g_dataset_location_ht)
492     {
493       dataset = g_dataset_lookup (dataset_location);
494       G_UNLOCK (g_dataset_global);
495       if (dataset)
496         {
497           register GData *list, *next;
498           
499           for (list = dataset->datalist; list; list = next)
500             {
501               next = list->next;
502               func (list->id, list->data, user_data);
503             }
504         }
505     }
506   else
507     {
508       G_UNLOCK (g_dataset_global);
509     }
510 }
511
512 void
513 g_datalist_foreach (GData          **datalist,
514                     GDataForeachFunc func,
515                     gpointer         user_data)
516 {
517   register GData *list, *next;
518
519   g_return_if_fail (datalist != NULL);
520   g_return_if_fail (func != NULL);
521   
522   for (list = G_DATALIST_GET_POINTER (datalist); list; list = next)
523     {
524       next = list->next;
525       func (list->id, list->data, user_data);
526     }
527 }
528
529 void
530 g_datalist_init (GData **datalist)
531 {
532   g_return_if_fail (datalist != NULL);
533   
534   *datalist = NULL;
535 }
536
537 /**
538  * g_datalist_set_flags:
539  * @datalist: pointer to the location that holds a list
540  * @flags: the flags to turn on. The values of the flags are
541  *   restricted by %G_DATALIST_FLAGS_MASK (currently
542  *   3; giving two possible boolean flags).
543  *   A value for @flags that doesn't fit within the mask is
544  *   an error.
545  * 
546  * Turns on flag values for a data list. This function is used
547  * to keep a small number of boolean flags in an object with
548  * a data list without using any additional space. It is
549  * not generally useful except in circumstances where space
550  * is very tight. (It is used in the base #GObject type, for
551  * example.)
552  **/
553 void
554 g_datalist_set_flags (GData **datalist,
555                       guint   flags)
556 {
557   g_return_if_fail (datalist != NULL);
558   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
559
560   G_LOCK (g_dataset_global);
561   *datalist = (GData*) (flags | (gsize) *datalist);
562   G_UNLOCK (g_dataset_global);
563 }
564
565 /**
566  * g_datalist_unset_flags:
567  * @datalist: pointer to the location that holds a list
568  * @flags: the flags to turn off. The values of the flags are
569  *   restricted by %G_DATALIST_FLAGS_MASK (currently
570  *   3: giving two possible boolean flags).
571  *   A value for @flags that doesn't fit within the mask is
572  *   an error.
573  * 
574  * Turns off flag values for a data list. See g_datalist_unset_flags()
575  **/
576 void
577 g_datalist_unset_flags (GData **datalist,
578                         guint   flags)
579 {
580   g_return_if_fail (datalist != NULL);
581   g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
582
583   G_LOCK (g_dataset_global);
584   *datalist = (GData*) (~(gsize) flags & (gsize) *datalist);
585   G_UNLOCK (g_dataset_global);
586 }
587
588 /**
589  * g_datalist_get_flags:
590  * @datalist: pointer to the location that holds a list
591  * 
592  * Gets flags values packed in together with the datalist.
593  * See g_datalist_set_flags().
594  * 
595  * Return value: the flags of the datalist
596  **/
597 guint
598 g_datalist_get_flags (GData **datalist)
599 {
600   g_return_val_if_fail (datalist != NULL, 0);
601
602   return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
603 }
604
605 /* HOLDS: g_dataset_global_lock */
606 static void
607 g_data_initialize (void)
608 {
609   g_return_if_fail (g_dataset_location_ht == NULL);
610
611   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
612   g_dataset_cached = NULL;
613   g_dataset_mem_chunk =
614     g_mem_chunk_new ("GDataset MemChunk",
615                      sizeof (GDataset),
616                      sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
617                      G_ALLOC_AND_FREE);
618   g_data_mem_chunk =
619     g_mem_chunk_new ("GData MemChunk",
620                      sizeof (GData),
621                      sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
622                      G_ALLOC_AND_FREE);
623 }
624
625 GQuark
626 g_quark_try_string (const gchar *string)
627 {
628   GQuark quark = 0;
629   g_return_val_if_fail (string != NULL, 0);
630   
631   G_LOCK (g_quark_global);
632   if (g_quark_ht)
633     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
634   G_UNLOCK (g_quark_global);
635   
636   return quark;
637 }
638
639 GQuark
640 g_quark_from_string (const gchar *string)
641 {
642   GQuark quark;
643   
644   g_return_val_if_fail (string != NULL, 0);
645   
646   G_LOCK (g_quark_global);
647   if (g_quark_ht)
648     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
649   else
650     {
651       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
652       quark = 0;
653     }
654   
655   if (!quark)
656     quark = g_quark_new (g_strdup (string));
657   G_UNLOCK (g_quark_global);
658   
659   return quark;
660 }
661
662 GQuark
663 g_quark_from_static_string (const gchar *string)
664 {
665   GQuark quark;
666   
667   g_return_val_if_fail (string != NULL, 0);
668   
669   G_LOCK (g_quark_global);
670   if (g_quark_ht)
671     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
672   else
673     {
674       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
675       quark = 0;
676     }
677
678   if (!quark)
679     quark = g_quark_new ((gchar*) string);
680   G_UNLOCK (g_quark_global);
681  
682   return quark;
683 }
684
685 G_CONST_RETURN gchar*
686 g_quark_to_string (GQuark quark)
687 {
688   gchar* result = NULL;
689   G_LOCK (g_quark_global);
690   if (quark > 0 && quark <= g_quark_seq_id)
691     result = g_quarks[quark - 1];
692   G_UNLOCK (g_quark_global);
693
694   return result;
695 }
696
697 /* HOLDS: g_quark_global_lock */
698 static inline GQuark
699 g_quark_new (gchar *string)
700 {
701   GQuark quark;
702   
703   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
704     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
705   
706   g_quarks[g_quark_seq_id] = string;
707   g_quark_seq_id++;
708   quark = g_quark_seq_id;
709   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
710   
711   return quark;
712 }
713
714 #define __G_DATASET_C__
715 #include "galiasdef.c"