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