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