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