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