Initial revision
[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        "glib.h"
22
23
24
25 /* --- defines --- */
26 #define G_DATASET_ID_BLOCK_SIZE                 (1024)
27 #define G_DATASET_MEM_CHUNK_PREALLOC            (512)
28 #define G_DATASET_DATA_MEM_CHUNK_PREALLOC       (1024)
29
30
31 /* --- structures --- */
32 typedef struct _GDatasetData GDatasetData;
33 typedef struct _GDataset GDataset;
34 struct _GDatasetData
35 {
36   GDatasetData *next;
37   guint id;
38   gpointer data;
39   GDestroyNotify destroy_func;
40 };
41
42 struct _GDataset
43 {
44   gconstpointer location;
45   GDatasetData *data_list;
46 };
47
48
49 /* --- prototypes --- */
50 static inline GDataset* g_dataset_lookup        (gconstpointer dataset_location);
51 static inline void      g_dataset_destroy_i     (GDataset     *dataset);
52 static void             g_dataset_initialize    (void);
53 static guint*           g_dataset_id_new        (void);
54
55
56 /* --- variables --- */
57 static GHashTable *g_dataset_location_ht = NULL;
58 static GHashTable *g_dataset_key_ht = NULL;
59 static GDataset *g_dataset_cached = NULL;
60 static GMemChunk *g_dataset_mem_chunk = NULL;
61 static GMemChunk *g_dataset_data_mem_chunk = NULL;
62 static guint *g_dataset_id_block = NULL;
63 static guint g_dataset_id_index = G_DATASET_ID_BLOCK_SIZE + 1;
64
65
66 /* --- functions --- */
67 static inline GDataset*
68 g_dataset_lookup (gconstpointer dataset_location)
69 {
70   register GDataset *dataset;
71   
72   if (g_dataset_cached && g_dataset_cached->location == dataset_location)
73     return g_dataset_cached;
74   
75   if (!g_dataset_location_ht)
76     g_dataset_initialize ();
77   
78   dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
79   if (dataset)
80     g_dataset_cached = dataset;
81   
82   return dataset;
83 }
84
85 static inline void
86 g_dataset_destroy_i (GDataset *dataset)
87 {
88   register GDatasetData *list;
89   
90   if (dataset == g_dataset_cached)
91     g_dataset_cached = NULL;
92   g_hash_table_remove (g_dataset_location_ht, dataset->location);
93   
94   list = dataset->data_list;
95   g_mem_chunk_free (g_dataset_mem_chunk, dataset);
96   
97   while (list)
98     {
99       register GDatasetData *prev;
100       
101       prev = list;
102       list = prev->next;
103       
104       if (prev->destroy_func)
105         prev->destroy_func (prev->data);
106       
107       g_mem_chunk_free (g_dataset_data_mem_chunk, prev);
108     }
109 }
110
111 void
112 g_dataset_destroy (gconstpointer  dataset_location)
113 {
114   register GDataset *dataset;
115   
116   g_return_if_fail (dataset_location != NULL);
117   
118   dataset = g_dataset_lookup (dataset_location);
119   if (dataset)
120     g_dataset_destroy_i (dataset);
121 }
122
123 void
124 g_dataset_id_set_destroy (gconstpointer  dataset_location,
125                           guint          key_id,
126                           GDestroyNotify destroy_func)
127 {
128   g_return_if_fail (dataset_location != NULL);
129   
130   if (key_id)
131     {
132       register GDataset *dataset;
133       
134       dataset = g_dataset_lookup (dataset_location);
135       if (dataset)
136         {
137           register GDatasetData *list;
138           
139           list = dataset->data_list;
140           while (list)
141             {
142               if (list->id == key_id)
143                 {
144                   list->destroy_func = destroy_func;
145                   return;
146                 }
147             }
148         }
149     }
150 }
151
152 gpointer
153 g_dataset_id_get_data (gconstpointer  dataset_location,
154                        guint          key_id)
155 {
156   g_return_val_if_fail (dataset_location != NULL, NULL);
157   
158   if (key_id)
159     {
160       register GDataset *dataset;
161       
162       dataset = g_dataset_lookup (dataset_location);
163       if (dataset)
164         {
165           register GDatasetData *list;
166           
167           for (list = dataset->data_list; list; list = list->next)
168             if (list->id == key_id)
169               return list->data;
170         }
171     }
172   
173   return NULL;
174 }
175
176 void
177 g_dataset_id_set_data_full (gconstpointer  dataset_location,
178                             guint          key_id,
179                             gpointer       data,
180                             GDestroyNotify destroy_func)
181 {
182   register GDataset *dataset;
183   register GDatasetData *list;
184   
185   g_return_if_fail (dataset_location != NULL);
186   g_return_if_fail (key_id > 0);
187   
188   dataset = g_dataset_lookup (dataset_location);
189   if (!dataset)
190     {
191       dataset = g_chunk_new (GDataset, g_dataset_mem_chunk);
192       dataset->location = dataset_location;
193       dataset->data_list = NULL;
194       g_hash_table_insert (g_dataset_location_ht, 
195                            (gpointer) dataset->location, /* Yuck */
196                            dataset);
197     }
198   
199   list = dataset->data_list;
200   if (!data)
201     {
202       register GDatasetData *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                   dataset->data_list = list->next;
214                   
215                   if (!dataset->data_list)
216                     g_dataset_destroy_i (dataset);
217                 }
218               
219               /* we need to have unlinked before invoking the destroy function
220                */
221               if (list->destroy_func)
222                 list->destroy_func (list->data);
223               
224               g_mem_chunk_free (g_dataset_data_mem_chunk, list);
225               break;
226             }
227           
228           prev = list;
229           list = list->next;
230         }
231     }
232   else
233     {
234       register GDatasetData *prev;
235       
236       prev = NULL;
237       while (list)
238         {
239           if (list->id == key_id)
240             {
241               if (prev)
242                 prev->next = list->next;
243               else
244                 dataset->data_list = list->next;
245               
246               /* we need to have unlinked before invoking the destroy function
247                */
248               if (list->destroy_func)
249                 list->destroy_func (list->data);
250               
251               break;
252             }
253           
254           prev = list;
255           list = list->next;
256         }
257       
258       if (!list)
259         list = g_chunk_new (GDatasetData, g_dataset_data_mem_chunk);
260       list->next = dataset->data_list;
261       list->id = key_id;
262       list->data = data;
263       list->destroy_func = destroy_func;
264       dataset->data_list = list;
265     }
266 }
267
268 guint
269 g_dataset_try_key (const gchar    *key)
270 {
271   register guint *id;
272   g_return_val_if_fail (key != NULL, 0);
273   
274   if (g_dataset_key_ht)
275     {
276       id = g_hash_table_lookup (g_dataset_key_ht, (gpointer) key);
277       
278       if (id)
279         return *id;
280     }
281   
282   return 0;
283 }
284
285 guint
286 g_dataset_force_id (const gchar    *key)
287 {
288   register guint *id;
289   
290   g_return_val_if_fail (key != NULL, 0);
291   
292   if (!g_dataset_key_ht)
293     g_dataset_initialize ();
294   
295   id = g_hash_table_lookup (g_dataset_key_ht, (gpointer) key);
296   if (!id)
297     {
298       id = g_dataset_id_new ();
299       g_hash_table_insert (g_dataset_key_ht, g_strdup (key), id);
300     }
301   
302   return *id;
303 }
304
305 static void
306 g_dataset_initialize (void)
307 {
308   if (!g_dataset_location_ht)
309     {
310       g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
311       g_dataset_key_ht = g_hash_table_new (g_str_hash, g_str_equal);
312       g_dataset_cached = NULL;
313       g_dataset_mem_chunk =
314         g_mem_chunk_new ("GDataset MemChunk",
315                          sizeof (GDataset),
316                          sizeof (GDataset) * G_DATASET_MEM_CHUNK_PREALLOC,
317                          G_ALLOC_AND_FREE);
318       g_dataset_data_mem_chunk =
319         g_mem_chunk_new ("GDatasetData MemChunk",
320                          sizeof (GDatasetData),
321                          sizeof (GDatasetData) * G_DATASET_DATA_MEM_CHUNK_PREALLOC,
322                          G_ALLOC_AND_FREE);
323     }
324 }
325
326 static guint*
327 g_dataset_id_new (void)
328 {
329   static guint seq_id = 1;
330   register guint *id;
331   
332   if (g_dataset_id_index >= G_DATASET_ID_BLOCK_SIZE)
333     {
334       g_dataset_id_block = g_new (guint, G_DATASET_ID_BLOCK_SIZE);
335       g_dataset_id_index = 0;
336     }
337   
338   id = &g_dataset_id_block[g_dataset_id_index++];
339   *id = seq_id++;
340   
341   return id;
342 }