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