1 /* GLIB - Library of useful routines for C programming
2 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
4 * gdataset.c: Generic dataset mechanism, similar to GtkObject data.
5 * Copyright (C) 1998 Tim Janik
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.
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.
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.
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/.
30 * MT safe ; FIXME: might still freeze, watch out, not thoroughly
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)
46 /* --- structures --- */
47 typedef struct _GDataset GDataset
;
53 GDestroyNotify destroy_func
;
58 gconstpointer location
;
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 gpointer
g_data_set_internal (GData
**datalist
,
70 GDestroyNotify destroy_func
,
72 static void g_data_initialize (void);
73 static inline GQuark
g_quark_new (gchar
*string
);
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
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;
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;
92 /* --- functions --- */
94 /* HOLDS: g_dataset_global_lock */
96 g_datalist_clear_i (GData
**datalist
)
100 /* unlink *all* items before walking their destructors
107 register GData
*prev
;
112 if (prev
->destroy_func
)
114 G_UNLOCK (g_dataset_global
);
115 prev
->destroy_func (prev
->data
);
116 G_LOCK (g_dataset_global
);
119 if (g_data_cache_length
< G_DATA_CACHE_MAX
)
121 prev
->next
= g_data_cache
;
123 g_data_cache_length
++;
126 g_mem_chunk_free (g_data_mem_chunk
, prev
);
131 g_datalist_clear (GData
**datalist
)
133 g_return_if_fail (datalist
!= NULL
);
135 G_LOCK (g_dataset_global
);
136 if (!g_dataset_location_ht
)
137 g_data_initialize ();
140 g_datalist_clear_i (datalist
);
141 G_UNLOCK (g_dataset_global
);
144 /* HOLDS: g_dataset_global_lock */
145 static inline GDataset
*
146 g_dataset_lookup (gconstpointer dataset_location
)
148 register GDataset
*dataset
;
150 if (g_dataset_cached
&& g_dataset_cached
->location
== dataset_location
)
151 return g_dataset_cached
;
153 dataset
= g_hash_table_lookup (g_dataset_location_ht
, dataset_location
);
155 g_dataset_cached
= dataset
;
160 /* HOLDS: g_dataset_global_lock */
162 g_dataset_destroy_internal (GDataset
*dataset
)
164 register gconstpointer dataset_location
;
166 dataset_location
= dataset
->location
;
169 if (!dataset
->datalist
)
171 if (dataset
== g_dataset_cached
)
172 g_dataset_cached
= NULL
;
173 g_hash_table_remove (g_dataset_location_ht
, dataset_location
);
174 g_mem_chunk_free (g_dataset_mem_chunk
, dataset
);
178 g_datalist_clear_i (&dataset
->datalist
);
179 dataset
= g_dataset_lookup (dataset_location
);
184 g_dataset_destroy (gconstpointer dataset_location
)
186 g_return_if_fail (dataset_location
!= NULL
);
188 G_LOCK (g_dataset_global
);
189 if (g_dataset_location_ht
)
191 register GDataset
*dataset
;
193 dataset
= g_dataset_lookup (dataset_location
);
195 g_dataset_destroy_internal (dataset
);
197 G_UNLOCK (g_dataset_global
);
200 /* HOLDS: g_dataset_global_lock */
201 static inline gpointer
202 g_data_set_internal (GData
**datalist
,
205 GDestroyNotify destroy_func
,
208 register GData
*list
;
213 register GData
*prev
;
218 if (list
->id
== key_id
)
220 gpointer ret_data
= NULL
;
223 prev
->next
= list
->next
;
226 *datalist
= list
->next
;
228 /* the dataset destruction *must* be done
229 * prior to invokation of the data destroy function
231 if (!*datalist
&& dataset
)
232 g_dataset_destroy_internal (dataset
);
235 /* the GData struct *must* already be unlinked
236 * when invoking the destroy function.
237 * we use (data==NULL && destroy_func!=NULL) as
238 * a special hint combination to "steal"
239 * data without destroy notification
241 if (list
->destroy_func
&& !destroy_func
)
243 G_UNLOCK (g_dataset_global
);
244 list
->destroy_func (list
->data
);
245 G_LOCK (g_dataset_global
);
248 ret_data
= list
->data
;
250 if (g_data_cache_length
< G_DATA_CACHE_MAX
)
252 list
->next
= g_data_cache
;
254 g_data_cache_length
++;
257 g_mem_chunk_free (g_data_mem_chunk
, list
);
270 if (list
->id
== key_id
)
272 if (!list
->destroy_func
)
275 list
->destroy_func
= destroy_func
;
279 register GDestroyNotify dfunc
;
280 register gpointer ddata
;
282 dfunc
= list
->destroy_func
;
285 list
->destroy_func
= destroy_func
;
287 /* we need to have updated all structures prior to
288 * invokation of the destroy function
290 G_UNLOCK (g_dataset_global
);
292 G_LOCK (g_dataset_global
);
304 g_data_cache
= list
->next
;
305 g_data_cache_length
--;
308 list
= g_chunk_new (GData
, g_data_mem_chunk
);
309 list
->next
= *datalist
;
312 list
->destroy_func
= destroy_func
;
320 g_dataset_id_set_data_full (gconstpointer dataset_location
,
323 GDestroyNotify destroy_func
)
325 register GDataset
*dataset
;
327 g_return_if_fail (dataset_location
!= NULL
);
329 g_return_if_fail (destroy_func
== NULL
);
333 g_return_if_fail (key_id
> 0);
338 G_LOCK (g_dataset_global
);
339 if (!g_dataset_location_ht
)
340 g_data_initialize ();
342 dataset
= g_dataset_lookup (dataset_location
);
345 dataset
= g_chunk_new (GDataset
, g_dataset_mem_chunk
);
346 dataset
->location
= dataset_location
;
347 g_datalist_init (&dataset
->datalist
);
348 g_hash_table_insert (g_dataset_location_ht
,
349 (gpointer
) dataset
->location
,
353 g_data_set_internal (&dataset
->datalist
, key_id
, data
, destroy_func
, dataset
);
354 G_UNLOCK (g_dataset_global
);
358 g_datalist_id_set_data_full (GData
**datalist
,
361 GDestroyNotify destroy_func
)
363 g_return_if_fail (datalist
!= NULL
);
365 g_return_if_fail (destroy_func
== NULL
);
369 g_return_if_fail (key_id
> 0);
374 G_LOCK (g_dataset_global
);
375 if (!g_dataset_location_ht
)
376 g_data_initialize ();
378 g_data_set_internal (datalist
, key_id
, data
, destroy_func
, NULL
);
379 G_UNLOCK (g_dataset_global
);
383 g_dataset_id_remove_no_notify (gconstpointer dataset_location
,
386 gpointer ret_data
= NULL
;
388 g_return_val_if_fail (dataset_location
!= NULL
, NULL
);
390 G_LOCK (g_dataset_global
);
391 if (key_id
&& g_dataset_location_ht
)
395 dataset
= g_dataset_lookup (dataset_location
);
397 ret_data
= g_data_set_internal (&dataset
->datalist
, key_id
, NULL
, (GDestroyNotify
) 42, dataset
);
399 G_UNLOCK (g_dataset_global
);
405 g_datalist_id_remove_no_notify (GData
**datalist
,
408 gpointer ret_data
= NULL
;
410 g_return_val_if_fail (datalist
!= NULL
, NULL
);
412 G_LOCK (g_dataset_global
);
413 if (key_id
&& g_dataset_location_ht
)
414 ret_data
= g_data_set_internal (datalist
, key_id
, NULL
, (GDestroyNotify
) 42, NULL
);
415 G_UNLOCK (g_dataset_global
);
421 g_dataset_id_get_data (gconstpointer dataset_location
,
424 g_return_val_if_fail (dataset_location
!= NULL
, NULL
);
426 G_LOCK (g_dataset_global
);
427 if (key_id
&& g_dataset_location_ht
)
429 register GDataset
*dataset
;
431 dataset
= g_dataset_lookup (dataset_location
);
434 register GData
*list
;
436 for (list
= dataset
->datalist
; list
; list
= list
->next
)
437 if (list
->id
== key_id
)
439 G_UNLOCK (g_dataset_global
);
444 G_UNLOCK (g_dataset_global
);
450 g_datalist_id_get_data (GData
**datalist
,
453 g_return_val_if_fail (datalist
!= NULL
, NULL
);
457 register GData
*list
;
459 for (list
= *datalist
; list
; list
= list
->next
)
460 if (list
->id
== key_id
)
468 g_dataset_foreach (gconstpointer dataset_location
,
469 GDataForeachFunc func
,
472 register GDataset
*dataset
;
474 g_return_if_fail (dataset_location
!= NULL
);
475 g_return_if_fail (func
!= NULL
);
477 G_LOCK (g_dataset_global
);
478 if (g_dataset_location_ht
)
480 dataset
= g_dataset_lookup (dataset_location
);
481 G_UNLOCK (g_dataset_global
);
484 register GData
*list
;
486 for (list
= dataset
->datalist
; list
; list
= list
->next
)
487 func (list
->id
, list
->data
, user_data
);
492 G_UNLOCK (g_dataset_global
);
497 g_datalist_foreach (GData
**datalist
,
498 GDataForeachFunc func
,
501 register GData
*list
;
503 g_return_if_fail (datalist
!= NULL
);
504 g_return_if_fail (func
!= NULL
);
506 for (list
= *datalist
; list
; list
= list
->next
)
507 func (list
->id
, list
->data
, user_data
);
511 g_datalist_init (GData
**datalist
)
513 g_return_if_fail (datalist
!= NULL
);
518 /* HOLDS: g_dataset_global_lock */
520 g_data_initialize (void)
522 g_return_if_fail (g_dataset_location_ht
== NULL
);
524 g_dataset_location_ht
= g_hash_table_new (g_direct_hash
, NULL
);
525 g_dataset_cached
= NULL
;
526 g_dataset_mem_chunk
=
527 g_mem_chunk_new ("GDataset MemChunk",
529 sizeof (GDataset
) * G_DATASET_MEM_CHUNK_PREALLOC
,
532 g_mem_chunk_new ("GData MemChunk",
534 sizeof (GData
) * G_DATA_MEM_CHUNK_PREALLOC
,
539 g_quark_try_string (const gchar
*string
)
542 g_return_val_if_fail (string
!= NULL
, 0);
544 G_LOCK (g_quark_global
);
546 quark
= GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht
, string
));
547 G_UNLOCK (g_quark_global
);
553 g_quark_from_string (const gchar
*string
)
557 g_return_val_if_fail (string
!= NULL
, 0);
559 G_LOCK (g_quark_global
);
561 quark
= (gulong
) g_hash_table_lookup (g_quark_ht
, string
);
564 g_quark_ht
= g_hash_table_new (g_str_hash
, g_str_equal
);
569 quark
= g_quark_new (g_strdup (string
));
570 G_UNLOCK (g_quark_global
);
576 g_quark_from_static_string (const gchar
*string
)
580 g_return_val_if_fail (string
!= NULL
, 0);
582 G_LOCK (g_quark_global
);
584 quark
= (gulong
) g_hash_table_lookup (g_quark_ht
, string
);
587 g_quark_ht
= g_hash_table_new (g_str_hash
, g_str_equal
);
592 quark
= g_quark_new ((gchar
*) string
);
593 G_UNLOCK (g_quark_global
);
598 G_CONST_RETURN gchar
*
599 g_quark_to_string (GQuark quark
)
601 gchar
* result
= NULL
;
602 G_LOCK (g_quark_global
);
603 if (quark
> 0 && quark
<= g_quark_seq_id
)
604 result
= g_quarks
[quark
- 1];
605 G_UNLOCK (g_quark_global
);
610 /* HOLDS: g_quark_global_lock */
612 g_quark_new (gchar
*string
)
616 if (g_quark_seq_id
% G_QUARK_BLOCK_SIZE
== 0)
617 g_quarks
= g_renew (gchar
*, g_quarks
, g_quark_seq_id
+ G_QUARK_BLOCK_SIZE
);
619 g_quarks
[g_quark_seq_id
] = string
;
621 quark
= g_quark_seq_id
;
622 g_hash_table_insert (g_quark_ht
, string
, GUINT_TO_POINTER (quark
));