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