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