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