4aecff7f26e09188e086a9b8c1ee367daf8d3660
[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
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
49 /* --- structures --- */
50 typedef struct _GDataset GDataset;
51 struct _GData
52 {
53   GData *next;
54   GQuark id;
55   gpointer data;
56   GDestroyNotify destroy_func;
57 };
58
59 struct _GDataset
60 {
61   gconstpointer location;
62   GData        *datalist;
63 };
64
65
66 /* --- prototypes --- */
67 static inline GDataset* g_dataset_lookup                (gconstpointer    dataset_location);
68 static inline void      g_datalist_clear_i              (GData          **datalist);
69 static void             g_dataset_destroy_internal      (GDataset        *dataset);
70 static inline gpointer  g_data_set_internal             (GData          **datalist,
71                                                          GQuark           key_id,
72                                                          gpointer         data,
73                                                          GDestroyNotify   destroy_func,
74                                                          GDataset        *dataset);
75 static void             g_data_initialize               (void);
76 static inline GQuark    g_quark_new                     (gchar          *string);
77
78
79 /* --- variables --- */
80 G_LOCK_DEFINE_STATIC (g_dataset_global);
81 static GHashTable   *g_dataset_location_ht = NULL;
82 static GDataset     *g_dataset_cached = NULL; /* should this be
83                                                  threadspecific? */
84 static GMemChunk    *g_dataset_mem_chunk = NULL;
85 static GMemChunk    *g_data_mem_chunk = NULL;
86 static GData        *g_data_cache = NULL;
87 static guint         g_data_cache_length = 0;
88
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
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 = *datalist;
106   *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 (*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 = *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                   *datalist = list->next;
230                   
231                   /* the dataset destruction *must* be done
232                    * prior to invokation of the data destroy function
233                    */
234                   if (!*datalist && 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 = *datalist;
313       list->id = key_id;
314       list->data = data;
315       list->destroy_func = destroy_func;
316       *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 = *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 = *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 /* HOLDS: g_dataset_global_lock */
528 static void
529 g_data_initialize (void)
530 {
531   g_return_if_fail (g_dataset_location_ht == NULL);
532
533   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
534   g_dataset_cached = NULL;
535   g_dataset_mem_chunk =
536     g_mem_chunk_new ("GDataset MemChunk",
537                      sizeof (GDataset),
538                      sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
539                      G_ALLOC_AND_FREE);
540   g_data_mem_chunk =
541     g_mem_chunk_new ("GData MemChunk",
542                      sizeof (GData),
543                      sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
544                      G_ALLOC_AND_FREE);
545 }
546
547 GQuark
548 IA__g_quark_try_string (const gchar *string)
549 {
550   GQuark quark = 0;
551   g_return_val_if_fail (string != NULL, 0);
552   
553   G_LOCK (g_quark_global);
554   if (g_quark_ht)
555     quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
556   G_UNLOCK (g_quark_global);
557   
558   return quark;
559 }
560
561 GQuark
562 g_quark_from_string (const gchar *string)
563 {
564   GQuark quark;
565   
566   g_return_val_if_fail (string != NULL, 0);
567   
568   G_LOCK (g_quark_global);
569   if (g_quark_ht)
570     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
571   else
572     {
573       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
574       quark = 0;
575     }
576   
577   if (!quark)
578     quark = g_quark_new (g_strdup (string));
579   G_UNLOCK (g_quark_global);
580   
581   return quark;
582 }
583
584 GQuark
585 g_quark_from_static_string (const gchar *string)
586 {
587   GQuark quark;
588   
589   g_return_val_if_fail (string != NULL, 0);
590   
591   G_LOCK (g_quark_global);
592   if (g_quark_ht)
593     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
594   else
595     {
596       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
597       quark = 0;
598     }
599
600   if (!quark)
601     quark = g_quark_new ((gchar*) string);
602   G_UNLOCK (g_quark_global);
603  
604   return quark;
605 }
606
607 G_CONST_RETURN gchar*
608 g_quark_to_string (GQuark quark)
609 {
610   gchar* result = NULL;
611   G_LOCK (g_quark_global);
612   if (quark > 0 && quark <= g_quark_seq_id)
613     result = g_quarks[quark - 1];
614   G_UNLOCK (g_quark_global);
615
616   return result;
617 }
618
619 /* HOLDS: g_quark_global_lock */
620 static inline GQuark
621 g_quark_new (gchar *string)
622 {
623   GQuark quark;
624   
625   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
626     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
627   
628   g_quarks[g_quark_seq_id] = string;
629   g_quark_seq_id++;
630   quark = g_quark_seq_id;
631   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
632   
633   return quark;
634 }
635
636 #define __G_DATASET_C__
637 #include "galiasdef.c"