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