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