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