Fix for g_datalist_id_set_full() use (error is deeper down, some internal
[platform/upstream/glib.git] / 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   if (!g_dataset_location_ht)
178     g_data_initialize ();
179   
180   list = *datalist;
181   if (!data)
182     {
183       register GData *prev;
184       
185       prev = NULL;
186       while (list)
187         {
188           if (list->id == key_id)
189             {
190               if (prev)
191                 prev->next = list->next;
192               else
193                 {
194                   *datalist = list->next;
195                   
196                   /* the dataset destruction *must* be done
197                    * prior to invokation of the data destroy function
198                    */
199                   if (!*datalist && dataset)
200                     g_dataset_destroy_internal (dataset);
201                 }
202               
203               /* the GData struct *must* already be unlinked
204                * when invoking the destroy function
205                * we use (data==NULL && destroy_func!=NULL) as
206                * a special hint combination to "steal"
207                * data without destroy notification
208                */
209               if (list->destroy_func && !destroy_func)
210                 list->destroy_func (list->data);
211               
212               if (g_data_cache_length < G_DATA_CACHE_MAX)
213                 {
214                   list->next = g_data_cache;
215                   g_data_cache = list;
216                   g_data_cache_length++;
217                 }
218               else
219                 g_mem_chunk_free (g_data_mem_chunk, list);
220               
221               return;
222             }
223           
224           prev = list;
225           list = list->next;
226         }
227     }
228   else
229     {
230       while (list)
231         {
232           if (list->id == key_id)
233             {
234               if (!list->destroy_func)
235                 {
236                   list->data = data;
237                   list->destroy_func = destroy_func;
238                 }
239               else
240                 {
241                   register GDestroyNotify dfunc;
242                   register gpointer ddata;
243                   
244                   dfunc = list->destroy_func;
245                   ddata = list->data;
246                   list->data = data;
247                   list->destroy_func = destroy_func;
248                   
249                   /* we need to have updated all structures prior to
250                    * invokation of the destroy function
251                    */
252                   dfunc (ddata);
253                 }
254               
255               return;
256             }
257           
258           list = list->next;
259         }
260       
261       if (g_data_cache)
262         {
263           list = g_data_cache;
264           g_data_cache = list->next;
265           g_data_cache_length--;
266         }
267       else
268         list = g_chunk_new (GData, g_data_mem_chunk);
269       list->next = *datalist;
270       list->id = key_id;
271       list->data = data;
272       list->destroy_func = destroy_func;
273       *datalist = list;
274     }
275 }
276
277 void
278 g_dataset_id_set_data_full (gconstpointer  dataset_location,
279                             GQuark         key_id,
280                             gpointer       data,
281                             GDestroyNotify destroy_func)
282 {
283   register GDataset *dataset;
284   
285   g_return_if_fail (dataset_location != NULL);
286   if (!data)
287     g_return_if_fail (destroy_func == NULL);
288   if (!key_id)
289     {
290       if (data)
291         g_return_if_fail (key_id > 0);
292       else
293         return;
294     }
295   
296   if (!g_dataset_location_ht)
297     g_data_initialize ();
298   
299   dataset = g_dataset_lookup (dataset_location);
300   if (!dataset)
301     {
302       dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
303       dataset->location = dataset_location;
304       g_datalist_init (&dataset->datalist);
305       g_hash_table_insert (g_dataset_location_ht, 
306                            (gpointer) dataset->location,
307                            dataset);
308     }
309   
310   g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
311 }
312
313 void
314 g_datalist_id_set_data_full (GData        **datalist,
315                              GQuark         key_id,
316                              gpointer       data,
317                              GDestroyNotify destroy_func)
318 {
319   g_return_if_fail (datalist != NULL);
320   if (!data)
321     g_return_if_fail (destroy_func == NULL);
322   if (!key_id)
323     {
324       if (data)
325         g_return_if_fail (key_id > 0);
326       else
327         return;
328     }
329   
330   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
331 }
332
333 void
334 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
335                                GQuark         key_id)
336 {
337   g_return_if_fail (dataset_location != NULL);
338   
339   if (key_id && g_dataset_location_ht)
340     {
341       GDataset *dataset;
342   
343       dataset = g_dataset_lookup (dataset_location);
344       if (dataset)
345         g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
346     }
347 }
348
349 void
350 g_datalist_id_remove_no_notify (GData   **datalist,
351                                 GQuark    key_id)
352 {
353   g_return_if_fail (datalist != NULL);
354
355   if (key_id)
356     g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
357 }
358
359 gpointer
360 g_dataset_id_get_data (gconstpointer  dataset_location,
361                        GQuark         key_id)
362 {
363   g_return_val_if_fail (dataset_location != NULL, NULL);
364   
365   if (key_id && g_dataset_location_ht)
366     {
367       register GDataset *dataset;
368       
369       dataset = g_dataset_lookup (dataset_location);
370       if (dataset)
371         {
372           register GData *list;
373           
374           for (list = dataset->datalist; list; list = list->next)
375             if (list->id == key_id)
376               return list->data;
377         }
378     }
379   
380   return NULL;
381 }
382
383 gpointer
384 g_datalist_id_get_data (GData    **datalist,
385                         GQuark     key_id)
386 {
387   g_return_val_if_fail (datalist != NULL, NULL);
388   
389   if (key_id)
390     {
391       register GData *list;
392       
393       for (list = *datalist; list; list = list->next)
394         if (list->id == key_id)
395           return list->data;
396     }
397   
398   return NULL;
399 }
400
401 void
402 g_dataset_foreach (gconstpointer    dataset_location,
403                    GDataForeachFunc func,
404                    gpointer         user_data)
405 {
406   register GDataset *dataset;
407   
408   g_return_if_fail (dataset_location != NULL);
409   g_return_if_fail (func != NULL);
410   
411   dataset = g_dataset_lookup (dataset_location);
412   if (dataset)
413     {
414       register GData *list;
415       
416       for (list = dataset->datalist; list; list = list->next)
417         func (list->id, list->data, user_data);
418     }
419 }
420
421 void
422 g_datalist_foreach (GData          **datalist,
423                     GDataForeachFunc func,
424                     gpointer         user_data)
425 {
426   register GData *list;
427
428   g_return_if_fail (datalist != NULL);
429   g_return_if_fail (func != NULL);
430   
431   for (list = *datalist; list; list = list->next)
432     func (list->id, list->data, user_data);
433 }
434
435 void
436 g_datalist_init (GData **datalist)
437 {
438   g_return_if_fail (datalist != NULL);
439   
440   *datalist = NULL;
441 }
442
443 static void
444 g_data_initialize (void)
445 {
446   if (!g_dataset_location_ht)
447     {
448       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
449       g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
450       g_dataset_cached = NULL;
451       g_dataset_mem_chunk =
452         g_mem_chunk_new ("GDataset MemChunk",
453                          sizeof (GDataset),
454                          sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
455                          G_ALLOC_AND_FREE);
456       g_data_mem_chunk =
457         g_mem_chunk_new ("GData MemChunk",
458                          sizeof (GData),
459                          sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
460                          G_ALLOC_AND_FREE);
461     }
462 }
463
464 GQuark
465 g_quark_try_string (const gchar *string)
466 {
467   g_return_val_if_fail (string != NULL, 0);
468   
469   if (g_quark_ht)
470     return (gulong) g_hash_table_lookup (g_quark_ht, string);
471   else
472     return 0;
473 }
474
475 GQuark
476 g_quark_from_string (const gchar *string)
477 {
478   GQuark quark;
479   
480   g_return_val_if_fail (string != NULL, 0);
481   
482   if (!g_quark_ht)
483     g_data_initialize ();
484   
485   quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
486   if (!quark)
487     quark = g_quark_new (g_strdup (string));
488   
489   return quark;
490 }
491
492 GQuark
493 g_quark_from_static_string (const gchar *string)
494 {
495   GQuark quark;
496   
497   g_return_val_if_fail (string != NULL, 0);
498   
499   if (!g_quark_ht)
500     g_data_initialize ();
501   
502   quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
503   if (!quark)
504     quark = g_quark_new (string);
505   
506   return quark;
507 }
508
509 gchar*
510 g_quark_to_string (GQuark quark)
511 {
512   if (quark > 0 && quark <= g_quark_seq_id)
513     return g_quarks[quark - 1];
514   else
515     return NULL;
516 }
517
518 static inline GQuark
519 g_quark_new (const gchar *string)
520 {
521   GQuark quark;
522   
523   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
524     g_quarks = g_realloc (g_quarks,
525                           (g_quark_seq_id + G_QUARK_BLOCK_SIZE) * sizeof (gchar*));
526   
527   
528   g_quarks[g_quark_seq_id] = (gchar*) string;
529   g_quark_seq_id++;
530   quark = g_quark_seq_id;
531   g_hash_table_insert (g_quark_ht, (gchar*) string, GUINT_TO_POINTER (quark));
532   
533   return quark;
534 }