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 ; except for g_data*_foreach()
31 */
32
33 #include "config.h"
34
35 #include <string.h>
36
37 #include "glib.h"
38 #include "gdatasetprivate.h"
39 #include "galias.h"
40
41
42 /* --- defines --- */
43 #define G_QUARK_BLOCK_SIZE (512)
44
45 /* datalist pointer accesses have to be carried out atomically */
46 #define G_DATALIST_GET_POINTER(datalist) \
47 ((GData*) ((gsize) g_atomic_pointer_get (datalist) & ~(gsize) G_DATALIST_FLAGS_MASK))
48
49 #define G_DATALIST_SET_POINTER(datalist, pointer) G_STMT_START { \
50 gpointer _oldv, _newv; \
51 do { \
52 _oldv = g_atomic_pointer_get (datalist); \
53 _newv = (gpointer) (((gsize) _oldv & G_DATALIST_FLAGS_MASK) | (gsize) pointer); \
54 } while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, _oldv, _newv)); \
55 } G_STMT_END
56
57 /* --- structures --- */
58 typedef struct _GDataset GDataset;
59 struct _GData
60 {
61 GData *next;
62 GQuark id;
63 gpointer data;
64 GDestroyNotify destroy_func;
65 };
66
67 struct _GDataset
68 {
69 gconstpointer location;
70 GData *datalist;
71 };
72
73
74 /* --- prototypes --- */
75 static inline GDataset* g_dataset_lookup (gconstpointer dataset_location);
76 static inline void g_datalist_clear_i (GData **datalist);
77 static void g_dataset_destroy_internal (GDataset *dataset);
78 static inline gpointer g_data_set_internal (GData **datalist,
79 GQuark key_id,
80 gpointer data,
81 GDestroyNotify destroy_func,
82 GDataset *dataset);
83 static void g_data_initialize (void);
84 static inline GQuark g_quark_new (gchar *string);
85
86
87 /* --- variables --- */
88 G_LOCK_DEFINE_STATIC (g_dataset_global);
89 static GHashTable *g_dataset_location_ht = NULL;
90 static GDataset *g_dataset_cached = NULL; /* should this be
91 threadspecific? */
92 G_LOCK_DEFINE_STATIC (g_quark_global);
93 static GHashTable *g_quark_ht = NULL;
94 static gchar **g_quarks = NULL;
95 static GQuark g_quark_seq_id = 0;
96
97 /* --- functions --- */
98
99 /* HOLDS: g_dataset_global_lock */
100 static inline void
g_datalist_clear_i(GData ** datalist)101 g_datalist_clear_i (GData **datalist)
102 {
103 register GData *list;
104
105 /* unlink *all* items before walking their destructors
106 */
107 list = G_DATALIST_GET_POINTER (datalist);
108 G_DATALIST_SET_POINTER (datalist, NULL);
109
110 while (list)
111 {
112 register GData *prev;
113
114 prev = list;
115 list = prev->next;
116
117 if (prev->destroy_func)
118 {
119 G_UNLOCK (g_dataset_global);
120 prev->destroy_func (prev->data);
121 G_LOCK (g_dataset_global);
122 }
123
124 g_slice_free (GData, prev);
125 }
126 }
127
128 void
g_datalist_clear(GData ** datalist)129 g_datalist_clear (GData **datalist)
130 {
131 g_return_if_fail (datalist != NULL);
132
133 G_LOCK (g_dataset_global);
134 if (!g_dataset_location_ht)
135 g_data_initialize ();
136
137 while (G_DATALIST_GET_POINTER (datalist))
138 g_datalist_clear_i (datalist);
139 G_UNLOCK (g_dataset_global);
140 }
141
142 /* HOLDS: g_dataset_global_lock */
143 static inline GDataset*
g_dataset_lookup(gconstpointer dataset_location)144 g_dataset_lookup (gconstpointer dataset_location)
145 {
146 register GDataset *dataset;
147
148 if (g_dataset_cached && g_dataset_cached->location == dataset_location)
149 return g_dataset_cached;
150
151 dataset = g_hash_table_lookup (g_dataset_location_ht, dataset_location);
152 if (dataset)
153 g_dataset_cached = dataset;
154
155 return dataset;
156 }
157
158 /* HOLDS: g_dataset_global_lock */
159 static void
g_dataset_destroy_internal(GDataset * dataset)160 g_dataset_destroy_internal (GDataset *dataset)
161 {
162 register gconstpointer dataset_location;
163
164 dataset_location = dataset->location;
165 while (dataset)
166 {
167 if (!dataset->datalist)
168 {
169 if (dataset == g_dataset_cached)
170 g_dataset_cached = NULL;
171 g_hash_table_remove (g_dataset_location_ht, dataset_location);
172 g_slice_free (GDataset, dataset);
173 break;
174 }
175
176 g_datalist_clear_i (&dataset->datalist);
177 dataset = g_dataset_lookup (dataset_location);
178 }
179 }
180
181 void
g_dataset_destroy(gconstpointer dataset_location)182 g_dataset_destroy (gconstpointer dataset_location)
183 {
184 g_return_if_fail (dataset_location != NULL);
185
186 G_LOCK (g_dataset_global);
187 if (g_dataset_location_ht)
188 {
189 register GDataset *dataset;
190
191 dataset = g_dataset_lookup (dataset_location);
192 if (dataset)
193 g_dataset_destroy_internal (dataset);
194 }
195 G_UNLOCK (g_dataset_global);
196 }
197
198 /* HOLDS: g_dataset_global_lock */
199 static inline gpointer
g_data_set_internal(GData ** datalist,GQuark key_id,gpointer data,GDestroyNotify destroy_func,GDataset * dataset)200 g_data_set_internal (GData **datalist,
201 GQuark key_id,
202 gpointer data,
203 GDestroyNotify destroy_func,
204 GDataset *dataset)
205 {
206 register GData *list;
207
208 list = G_DATALIST_GET_POINTER (datalist);
209 if (!data)
210 {
211 register GData *prev;
212
213 prev = NULL;
214 while (list)
215 {
216 if (list->id == key_id)
217 {
218 gpointer ret_data = NULL;
219
220 if (prev)
221 prev->next = list->next;
222 else
223 {
224 G_DATALIST_SET_POINTER (datalist, list->next);
225
226 /* the dataset destruction *must* be done
227 * prior to invocation of the data destroy function
228 */
229 if (!list->next && dataset)
230 g_dataset_destroy_internal (dataset);
231 }
232
233 /* the GData struct *must* already be unlinked
234 * when invoking the destroy function.
235 * we use (data==NULL && destroy_func!=NULL) as
236 * a special hint combination to "steal"
237 * data without destroy notification
238 */
239 if (list->destroy_func && !destroy_func)
240 {
241 G_UNLOCK (g_dataset_global);
242 list->destroy_func (list->data);
243 G_LOCK (g_dataset_global);
244 }
245 else
246 ret_data = list->data;
247
248 g_slice_free (GData, list);
249
250 return ret_data;
251 }
252
253 prev = list;
254 list = list->next;
255 }
256 }
257 else
258 {
259 while (list)
260 {
261 if (list->id == key_id)
262 {
263 if (!list->destroy_func)
264 {
265 list->data = data;
266 list->destroy_func = destroy_func;
267 }
268 else
269 {
270 register GDestroyNotify dfunc;
271 register gpointer ddata;
272
273 dfunc = list->destroy_func;
274 ddata = list->data;
275 list->data = data;
276 list->destroy_func = destroy_func;
277
278 /* we need to have updated all structures prior to
279 * invocation of the destroy function
280 */
281 G_UNLOCK (g_dataset_global);
282 dfunc (ddata);
283 G_LOCK (g_dataset_global);
284 }
285
286 return NULL;
287 }
288
289 list = list->next;
290 }
291
292 list = g_slice_new (GData);
293 list->next = G_DATALIST_GET_POINTER (datalist);
294 list->id = key_id;
295 list->data = data;
296 list->destroy_func = destroy_func;
297 G_DATALIST_SET_POINTER (datalist, list);
298 }
299
300 return NULL;
301 }
302
303 void
g_dataset_id_set_data_full(gconstpointer dataset_location,GQuark key_id,gpointer data,GDestroyNotify destroy_func)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_slice_new (GDataset);
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
g_datalist_id_set_data_full(GData ** datalist,GQuark key_id,gpointer data,GDestroyNotify destroy_func)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 gpointer
g_dataset_id_remove_no_notify(gconstpointer dataset_location,GQuark key_id)367 g_dataset_id_remove_no_notify (gconstpointer dataset_location,
368 GQuark key_id)
369 {
370 gpointer ret_data = NULL;
371
372 g_return_val_if_fail (dataset_location != NULL, NULL);
373
374 G_LOCK (g_dataset_global);
375 if (key_id && g_dataset_location_ht)
376 {
377 GDataset *dataset;
378
379 dataset = g_dataset_lookup (dataset_location);
380 if (dataset)
381 ret_data = g_data_set_internal (&dataset->datalist, key_id, NULL, (GDestroyNotify) 42, dataset);
382 }
383 G_UNLOCK (g_dataset_global);
384
385 return ret_data;
386 }
387
388 gpointer
g_datalist_id_remove_no_notify(GData ** datalist,GQuark key_id)389 g_datalist_id_remove_no_notify (GData **datalist,
390 GQuark key_id)
391 {
392 gpointer ret_data = NULL;
393
394 g_return_val_if_fail (datalist != NULL, NULL);
395
396 G_LOCK (g_dataset_global);
397 if (key_id && g_dataset_location_ht)
398 ret_data = g_data_set_internal (datalist, key_id, NULL, (GDestroyNotify) 42, NULL);
399 G_UNLOCK (g_dataset_global);
400
401 return ret_data;
402 }
403
404 gpointer
g_dataset_id_get_data(gconstpointer dataset_location,GQuark key_id)405 g_dataset_id_get_data (gconstpointer dataset_location,
406 GQuark key_id)
407 {
408 g_return_val_if_fail (dataset_location != NULL, NULL);
409
410 G_LOCK (g_dataset_global);
411 if (key_id && g_dataset_location_ht)
412 {
413 register GDataset *dataset;
414
415 dataset = g_dataset_lookup (dataset_location);
416 if (dataset)
417 {
418 register GData *list;
419
420 for (list = dataset->datalist; list; list = list->next)
421 if (list->id == key_id)
422 {
423 G_UNLOCK (g_dataset_global);
424 return list->data;
425 }
426 }
427 }
428 G_UNLOCK (g_dataset_global);
429
430 return NULL;
431 }
432
433 gpointer
g_datalist_id_get_data(GData ** datalist,GQuark key_id)434 g_datalist_id_get_data (GData **datalist,
435 GQuark key_id)
436 {
437 gpointer data = NULL;
438 g_return_val_if_fail (datalist != NULL, NULL);
439 if (key_id)
440 {
441 register GData *list;
442 G_LOCK (g_dataset_global);
443 for (list = G_DATALIST_GET_POINTER (datalist); list; list = list->next)
444 if (list->id == key_id)
445 {
446 data = list->data;
447 break;
448 }
449 G_UNLOCK (g_dataset_global);
450 }
451 return data;
452 }
453
454 void
g_dataset_foreach(gconstpointer dataset_location,GDataForeachFunc func,gpointer user_data)455 g_dataset_foreach (gconstpointer dataset_location,
456 GDataForeachFunc func,
457 gpointer user_data)
458 {
459 register GDataset *dataset;
460
461 g_return_if_fail (dataset_location != NULL);
462 g_return_if_fail (func != NULL);
463
464 G_LOCK (g_dataset_global);
465 if (g_dataset_location_ht)
466 {
467 dataset = g_dataset_lookup (dataset_location);
468 G_UNLOCK (g_dataset_global);
469 if (dataset)
470 {
471 register GData *list, *next;
472
473 for (list = dataset->datalist; list; list = next)
474 {
475 next = list->next;
476 func (list->id, list->data, user_data);
477 }
478 }
479 }
480 else
481 {
482 G_UNLOCK (g_dataset_global);
483 }
484 }
485
486 void
g_datalist_foreach(GData ** datalist,GDataForeachFunc func,gpointer user_data)487 g_datalist_foreach (GData **datalist,
488 GDataForeachFunc func,
489 gpointer user_data)
490 {
491 register GData *list, *next;
492
493 g_return_if_fail (datalist != NULL);
494 g_return_if_fail (func != NULL);
495
496 for (list = G_DATALIST_GET_POINTER (datalist); list; list = next)
497 {
498 next = list->next;
499 func (list->id, list->data, user_data);
500 }
501 }
502
503 void
g_datalist_init(GData ** datalist)504 g_datalist_init (GData **datalist)
505 {
506 g_return_if_fail (datalist != NULL);
507
508 g_atomic_pointer_set (datalist, NULL);
509 }
510
511 /**
512 * g_datalist_set_flags:
513 * @datalist: pointer to the location that holds a list
514 * @flags: the flags to turn on. The values of the flags are
515 * restricted by %G_DATALIST_FLAGS_MASK (currently
516 * 3; giving two possible boolean flags).
517 * A value for @flags that doesn't fit within the mask is
518 * an error.
519 *
520 * Turns on flag values for a data list. This function is used
521 * to keep a small number of boolean flags in an object with
522 * a data list without using any additional space. It is
523 * not generally useful except in circumstances where space
524 * is very tight. (It is used in the base #GObject type, for
525 * example.)
526 *
527 * Since: 2.8
528 **/
529 void
g_datalist_set_flags(GData ** datalist,guint flags)530 g_datalist_set_flags (GData **datalist,
531 guint flags)
532 {
533 gpointer oldvalue;
534 g_return_if_fail (datalist != NULL);
535 g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
536
537 do
538 {
539 oldvalue = g_atomic_pointer_get (datalist);
540 }
541 while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue,
542 (gpointer) ((gsize) oldvalue | flags)));
543 }
544
545 /**
546 * g_datalist_unset_flags:
547 * @datalist: pointer to the location that holds a list
548 * @flags: the flags to turn off. The values of the flags are
549 * restricted by %G_DATALIST_FLAGS_MASK (currently
550 * 3: giving two possible boolean flags).
551 * A value for @flags that doesn't fit within the mask is
552 * an error.
553 *
554 * Turns off flag values for a data list. See g_datalist_unset_flags()
555 *
556 * Since: 2.8
557 **/
558 void
g_datalist_unset_flags(GData ** datalist,guint flags)559 g_datalist_unset_flags (GData **datalist,
560 guint flags)
561 {
562 gpointer oldvalue;
563 g_return_if_fail (datalist != NULL);
564 g_return_if_fail ((flags & ~G_DATALIST_FLAGS_MASK) == 0);
565
566 do
567 {
568 oldvalue = g_atomic_pointer_get (datalist);
569 }
570 while (!g_atomic_pointer_compare_and_exchange ((void**) datalist, oldvalue,
571 (gpointer) ((gsize) oldvalue & ~(gsize) flags)));
572 }
573
574 /**
575 * g_datalist_get_flags:
576 * @datalist: pointer to the location that holds a list
577 *
578 * Gets flags values packed in together with the datalist.
579 * See g_datalist_set_flags().
580 *
581 * Return value: the flags of the datalist
582 *
583 * Since: 2.8
584 **/
585 guint
g_datalist_get_flags(GData ** datalist)586 g_datalist_get_flags (GData **datalist)
587 {
588 g_return_val_if_fail (datalist != NULL, 0);
589
590 return G_DATALIST_GET_FLAGS (datalist); /* atomic macro */
591 }
592
593 /* HOLDS: g_dataset_global_lock */
594 static void
g_data_initialize(void)595 g_data_initialize (void)
596 {
597 g_return_if_fail (g_dataset_location_ht == NULL);
598
599 g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
600 g_dataset_cached = NULL;
601 }
602
603 GQuark
g_quark_try_string(const gchar * string)604 g_quark_try_string (const gchar *string)
605 {
606 GQuark quark = 0;
607 g_return_val_if_fail (string != NULL, 0);
608
609 G_LOCK (g_quark_global);
610 if (g_quark_ht)
611 quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
612 G_UNLOCK (g_quark_global);
613
614 return quark;
615 }
616
617 /* HOLDS: g_quark_global_lock */
618 static inline GQuark
g_quark_from_string_internal(const gchar * string,gboolean duplicate)619 g_quark_from_string_internal (const gchar *string,
620 gboolean duplicate)
621 {
622 GQuark quark = 0;
623
624 if (g_quark_ht)
625 quark = GPOINTER_TO_UINT (g_hash_table_lookup (g_quark_ht, string));
626
627 if (!quark)
628 quark = g_quark_new (duplicate ? g_strdup (string) : (gchar *)string);
629
630 return quark;
631 }
632
633 GQuark
g_quark_from_string(const gchar * string)634 g_quark_from_string (const gchar *string)
635 {
636 GQuark quark;
637
638 if (!string)
639 return 0;
640
641 G_LOCK (g_quark_global);
642 quark = g_quark_from_string_internal (string, TRUE);
643 G_UNLOCK (g_quark_global);
644
645 return quark;
646 }
647
648 GQuark
g_quark_from_static_string(const gchar * string)649 g_quark_from_static_string (const gchar *string)
650 {
651 GQuark quark;
652
653 if (!string)
654 return 0;
655
656 G_LOCK (g_quark_global);
657 quark = g_quark_from_string_internal (string, FALSE);
658 G_UNLOCK (g_quark_global);
659
660 return quark;
661 }
662
663 G_CONST_RETURN gchar*
g_quark_to_string(GQuark quark)664 g_quark_to_string (GQuark quark)
665 {
666 gchar* result = NULL;
667
668 G_LOCK (g_quark_global);
669 if (quark < g_quark_seq_id)
670 result = g_quarks[quark];
671 G_UNLOCK (g_quark_global);
672
673 return result;
674 }
675
676 /* HOLDS: g_quark_global_lock */
677 static inline GQuark
g_quark_new(gchar * string)678 g_quark_new (gchar *string)
679 {
680 GQuark quark;
681
682 if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
683 g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
684 if (!g_quark_ht)
685 {
686 g_assert (g_quark_seq_id == 0);
687 g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
688 g_quarks[g_quark_seq_id++] = NULL;
689 }
690
691 quark = g_quark_seq_id++;
692 g_quarks[quark] = string;
693 g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
694
695 return quark;
696 }
697
698 /**
699 * g_intern_string:
700 * @string: a string
701 *
702 * Returns a canonical representation for @string. Interned strings can
703 * be compared for equality by comparing the pointers, instead of using strcmp().
704 *
705 * Returns: a canonical representation for the string
706 *
707 * Since: 2.10
708 */
709 G_CONST_RETURN gchar*
g_intern_string(const gchar * string)710 g_intern_string (const gchar *string)
711 {
712 const gchar *result;
713 GQuark quark;
714
715 if (!string)
716 return NULL;
717
718 G_LOCK (g_quark_global);
719 quark = g_quark_from_string_internal (string, TRUE);
720 result = g_quarks[quark];
721 G_UNLOCK (g_quark_global);
722
723 return result;
724 }
725
726 /**
727 * g_intern_static_string:
728 * @string: a static string
729 *
730 * Returns a canonical representation for @string. Interned strings can
731 * be compared for equality by comparing the pointers, instead of using strcmp().
732 * g_intern_static_string() does not copy the string, therefore @string must
733 * not be freed or modified.
734 *
735 * Returns: a canonical representation for the string
736 *
737 * Since: 2.10
738 */
739 G_CONST_RETURN gchar*
g_intern_static_string(const gchar * string)740 g_intern_static_string (const gchar *string)
741 {
742 GQuark quark;
743 const gchar *result;
744
745 if (!string)
746 return NULL;
747
748 G_LOCK (g_quark_global);
749 quark = g_quark_from_string_internal (string, FALSE);
750 result = g_quarks[quark];
751 G_UNLOCK (g_quark_global);
752
753 return result;
754 }
755
756
757
758 #define __G_DATASET_C__
759 #include "galiasdef.c"
760