• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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