removed the GListAllocator type and its g_*_allocator_*() function
[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                     (gchar          *string);
61
62
63 /* --- variables --- */
64 static GHashTable   *g_dataset_location_ht = NULL;
65 static GDataset     *g_dataset_cached = NULL;
66 static GMemChunk    *g_dataset_mem_chunk = NULL;
67 static GMemChunk    *g_data_mem_chunk = NULL;
68 static GData        *g_data_cache = NULL;
69 static guint         g_data_cache_length = 0;
70 static GHashTable   *g_quark_ht = NULL;
71 static gchar       **g_quarks = NULL;
72 static GQuark        g_quark_seq_id = 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   if (!g_dataset_location_ht)
113     g_data_initialize ();
114
115   while (*datalist)
116     g_datalist_clear_i (datalist);
117 }
118
119 static inline GDataset*
120 g_dataset_lookup (gconstpointer dataset_location)
121 {
122   register GDataset *dataset;
123   
124   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
125     return g_dataset_cached;
126   
127   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
128   if (dataset)
129     g_dataset_cached = dataset;
130   
131   return dataset;
132 }
133
134 static void
135 g_dataset_destroy_internal (GDataset *dataset)
136 {
137   register gconstpointer dataset_location;
138   
139   dataset_location = dataset->location;
140   while (dataset)
141     {
142       if (!dataset->datalist)
143         {
144           if (dataset == g_dataset_cached)
145             g_dataset_cached = NULL;
146           g_hash_table_remove (g_dataset_location_ht, dataset_location);
147           g_mem_chunk_free (g_dataset_mem_chunk, dataset);
148           break;
149         }
150       
151       g_datalist_clear_i (&dataset->datalist);
152       dataset = g_dataset_lookup (dataset_location);
153     }
154 }
155
156 void
157 g_dataset_destroy (gconstpointer  dataset_location)
158 {
159   g_return_if_fail (dataset_location != NULL);
160   
161   if (g_dataset_location_ht)
162     {
163       register GDataset *dataset;
164
165       dataset = g_dataset_lookup (dataset_location);
166       if (dataset)
167         g_dataset_destroy_internal (dataset);
168     }
169 }
170
171 static inline void
172 g_data_set_internal (GData        **datalist,
173                      GQuark         key_id,
174                      gpointer       data,
175                      GDestroyNotify destroy_func,
176                      GDataset      *dataset)
177 {
178   register GData *list;
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   if (!g_dataset_location_ht)
331     g_data_initialize ();
332   
333   g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
334 }
335
336 void
337 g_dataset_id_remove_no_notify (gconstpointer  dataset_location,
338                                GQuark         key_id)
339 {
340   g_return_if_fail (dataset_location != NULL);
341   
342   if (key_id && g_dataset_location_ht)
343     {
344       GDataset *dataset;
345   
346       dataset = g_dataset_lookup (dataset_location);
347       if (dataset)
348         g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
349     }
350 }
351
352 void
353 g_datalist_id_remove_no_notify (GData   **datalist,
354                                 GQuark    key_id)
355 {
356   g_return_if_fail (datalist != NULL);
357
358   if (key_id && g_dataset_location_ht)
359     g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
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 (GData    **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_dataset_foreach (gconstpointer    dataset_location,
406                    GDataForeachFunc func,
407                    gpointer         user_data)
408 {
409   register GDataset *dataset;
410   
411   g_return_if_fail (dataset_location != NULL);
412   g_return_if_fail (func != NULL);
413
414   if (g_dataset_location_ht)
415     {
416       dataset = g_dataset_lookup (dataset_location);
417       if (dataset)
418         {
419           register GData *list;
420           
421           for (list = dataset->datalist; list; list = list->next)
422             func (list->id, list->data, user_data);
423         }
424     }
425 }
426
427 void
428 g_datalist_foreach (GData          **datalist,
429                     GDataForeachFunc func,
430                     gpointer         user_data)
431 {
432   register GData *list;
433
434   g_return_if_fail (datalist != NULL);
435   g_return_if_fail (func != NULL);
436   
437   for (list = *datalist; list; list = list->next)
438     func (list->id, list->data, user_data);
439 }
440
441 void
442 g_datalist_init (GData **datalist)
443 {
444   g_return_if_fail (datalist != NULL);
445   
446   *datalist = NULL;
447 }
448
449 static void
450 g_data_initialize (void)
451 {
452   g_return_if_fail (g_dataset_location_ht == NULL);
453
454   g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
455   g_dataset_cached = NULL;
456   g_dataset_mem_chunk =
457     g_mem_chunk_new ("GDataset MemChunk",
458                      sizeof (GDataset),
459                      sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
460                      G_ALLOC_AND_FREE);
461   g_data_mem_chunk =
462     g_mem_chunk_new ("GData MemChunk",
463                      sizeof (GData),
464                      sizeof (GData) * G_DATA_MEM_CHUNK_PREALLOC,
465                      G_ALLOC_AND_FREE);
466 }
467
468 GQuark
469 g_quark_try_string (const gchar *string)
470 {
471   g_return_val_if_fail (string != NULL, 0);
472   
473   if (g_quark_ht)
474     return (gulong) g_hash_table_lookup (g_quark_ht, string);
475   else
476     return 0;
477 }
478
479 GQuark
480 g_quark_from_string (const gchar *string)
481 {
482   GQuark quark;
483   
484   g_return_val_if_fail (string != NULL, 0);
485   
486   if (g_quark_ht)
487     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
488   else
489     {
490       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
491       quark = 0;
492     }
493   
494   if (!quark)
495     quark = g_quark_new (g_strdup (string));
496   
497   return quark;
498 }
499
500 GQuark
501 g_quark_from_static_string (const gchar *string)
502 {
503   GQuark quark;
504   
505   g_return_val_if_fail (string != NULL, 0);
506   
507   if (g_quark_ht)
508     quark = (gulong) g_hash_table_lookup (g_quark_ht, string);
509   else
510     {
511       g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
512       quark = 0;
513     }
514
515   if (!quark)
516     quark = g_quark_new ((gchar*) string);
517   
518   return quark;
519 }
520
521 gchar*
522 g_quark_to_string (GQuark quark)
523 {
524   if (quark > 0 && quark <= g_quark_seq_id)
525     return g_quarks[quark - 1];
526   else
527     return NULL;
528 }
529
530 static inline GQuark
531 g_quark_new (gchar *string)
532 {
533   GQuark quark;
534   
535   if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
536     g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
537   
538   g_quarks[g_quark_seq_id] = string;
539   g_quark_seq_id++;
540   quark = g_quark_seq_id;
541   g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
542   
543   return quark;
544 }