Make refcounting threadsafe by using atomic operations. (#166020, Wim
[platform/upstream/glib.git] / gobject / gparam.c
1 /* GObject - GLib Type, Object, Parameter and Signal Library
2  * Copyright (C) 1997-1999, 2000-2001 Tim Janik and Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General
15  * Public License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 /*
21  * MT safe
22  */
23
24 #include        "gparam.h"
25 #include        "gparamspecs.h"
26
27 #include        "gvaluecollector.h"
28 #include        "gobjectalias.h"
29 #include        <string.h>
30
31
32
33 /* --- defines --- */
34 #define G_PARAM_USER_MASK                       (~0 << G_PARAM_USER_SHIFT)
35 #define PSPEC_APPLIES_TO_VALUE(pspec, value)    (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
36 #define G_SLOCK(mutex)                          g_static_mutex_lock (mutex)
37 #define G_SUNLOCK(mutex)                        g_static_mutex_unlock (mutex)
38
39
40 /* --- prototypes --- */
41 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
42 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
43 static void     g_param_spec_class_init          (GParamSpecClass       *class,
44                                                   gpointer               class_data);
45 static void     g_param_spec_init                (GParamSpec            *pspec,
46                                                   GParamSpecClass       *class);
47 static void     g_param_spec_finalize            (GParamSpec            *pspec);
48 static void     value_param_init                (GValue         *value);
49 static void     value_param_free_value          (GValue         *value);
50 static void     value_param_copy_value          (const GValue   *src_value,
51                                                  GValue         *dest_value);
52 static void     value_param_transform_value     (const GValue   *src_value,
53                                                  GValue         *dest_value);
54 static gpointer value_param_peek_pointer        (const GValue   *value);
55 static gchar*   value_param_collect_value       (GValue         *value,
56                                                  guint           n_collect_values,
57                                                  GTypeCValue    *collect_values,
58                                                  guint           collect_flags);
59 static gchar*   value_param_lcopy_value         (const GValue   *value,
60                                                  guint           n_collect_values,
61                                                  GTypeCValue    *collect_values,
62                                                  guint           collect_flags);
63
64
65 /* --- variables --- */
66 static GQuark quark_floating = 0;
67
68
69 /* --- functions --- */
70 void
71 g_param_type_init (void)
72 {
73   static const GTypeFundamentalInfo finfo = {
74     (G_TYPE_FLAG_CLASSED |
75      G_TYPE_FLAG_INSTANTIATABLE |
76      G_TYPE_FLAG_DERIVABLE |
77      G_TYPE_FLAG_DEEP_DERIVABLE),
78   };
79   static const GTypeValueTable param_value_table = {
80     value_param_init,           /* value_init */
81     value_param_free_value,     /* value_free */
82     value_param_copy_value,     /* value_copy */
83     value_param_peek_pointer,   /* value_peek_pointer */
84     "p",                        /* collect_format */
85     value_param_collect_value,  /* collect_value */
86     "p",                        /* lcopy_format */
87     value_param_lcopy_value,    /* lcopy_value */
88   };
89   static const GTypeInfo param_spec_info = {
90     sizeof (GParamSpecClass),
91
92     (GBaseInitFunc) g_param_spec_class_base_init,
93     (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
94     (GClassInitFunc) g_param_spec_class_init,
95     (GClassFinalizeFunc) NULL,
96     NULL,       /* class_data */
97
98     sizeof (GParamSpec),
99     0,          /* n_preallocs */
100     (GInstanceInitFunc) g_param_spec_init,
101
102     &param_value_table,
103   };
104   GType type;
105
106   type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", &param_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT);
107   g_assert (type == G_TYPE_PARAM);
108   g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
109 }
110
111 static void
112 g_param_spec_class_base_init (GParamSpecClass *class)
113 {
114 }
115
116 static void
117 g_param_spec_class_base_finalize (GParamSpecClass *class)
118 {
119 }
120
121 static void
122 g_param_spec_class_init (GParamSpecClass *class,
123                          gpointer         class_data)
124 {
125   quark_floating = g_quark_from_static_string ("GParamSpec-floating");
126
127   class->value_type = G_TYPE_NONE;
128   class->finalize = g_param_spec_finalize;
129   class->value_set_default = NULL;
130   class->value_validate = NULL;
131   class->values_cmp = NULL;
132 }
133
134 static void
135 g_param_spec_init (GParamSpec      *pspec,
136                    GParamSpecClass *class)
137 {
138   pspec->name = NULL;
139   pspec->_nick = NULL;
140   pspec->_blurb = NULL;
141   pspec->flags = 0;
142   pspec->value_type = class->value_type;
143   pspec->owner_type = 0;
144   pspec->qdata = NULL;
145   pspec->ref_count = 1;
146   pspec->param_id = 0;
147   g_datalist_id_set_data (&pspec->qdata, quark_floating, GUINT_TO_POINTER (TRUE));
148 }
149
150 static void
151 g_param_spec_finalize (GParamSpec *pspec)
152 {
153   g_datalist_clear (&pspec->qdata);
154
155   if (!(pspec->flags & G_PARAM_STATIC_NAME))
156     g_free (pspec->name);
157   
158   if (!(pspec->flags & G_PARAM_STATIC_NICK))
159     g_free (pspec->_nick);
160
161   if (!(pspec->flags & G_PARAM_STATIC_BLURB))
162     g_free (pspec->_blurb);
163
164   g_type_free_instance ((GTypeInstance*) pspec);
165 }
166
167 GParamSpec*
168 g_param_spec_ref (GParamSpec *pspec)
169 {
170   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
171   g_return_val_if_fail (pspec->ref_count > 0, NULL);
172
173   g_atomic_int_inc (&pspec->ref_count);
174
175   return pspec;
176 }
177
178 void
179 g_param_spec_unref (GParamSpec *pspec)
180 {
181   gboolean is_zero;
182
183   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
184   g_return_if_fail (pspec->ref_count > 0);
185
186   is_zero = g_atomic_int_dec_and_test (&pspec->ref_count);
187
188   if (G_UNLIKELY (is_zero))
189     {
190       G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
191     }
192 }
193
194 void
195 g_param_spec_sink (GParamSpec *pspec)
196 {
197   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
198   g_return_if_fail (pspec->ref_count > 0);
199
200   if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating))
201     {
202       g_param_spec_unref (pspec);
203     }
204 }
205
206 G_CONST_RETURN gchar*
207 g_param_spec_get_name (GParamSpec *pspec)
208 {
209   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
210
211   return pspec->name;
212 }
213
214 G_CONST_RETURN gchar*
215 g_param_spec_get_nick (GParamSpec *pspec)
216 {
217   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
218
219   if (pspec->_nick)
220     return pspec->_nick;
221   else
222     {
223       GParamSpec *redirect_target;
224
225       redirect_target = g_param_spec_get_redirect_target (pspec);
226       if (redirect_target && redirect_target->_nick)
227         return redirect_target->_nick;
228     }
229
230   return pspec->name;
231 }
232
233 G_CONST_RETURN gchar*
234 g_param_spec_get_blurb (GParamSpec *pspec)
235 {
236   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
237
238   if (pspec->_blurb)
239     return pspec->_blurb;
240   else
241     {
242       GParamSpec *redirect_target;
243
244       redirect_target = g_param_spec_get_redirect_target (pspec);
245       if (redirect_target && redirect_target->_blurb)
246         return redirect_target->_blurb;
247     }
248
249   return NULL;
250 }
251
252 static void
253 canonicalize_key (gchar *key)
254 {
255   gchar *p;
256   
257   for (p = key; *p != 0; p++)
258     {
259       gchar c = *p;
260       
261       if (c != '-' &&
262           (c < '0' || c > '9') &&
263           (c < 'A' || c > 'Z') &&
264           (c < 'a' || c > 'z'))
265         *p = '-';
266     }
267 }
268
269 static gboolean
270 is_canonical (const gchar *key)
271 {
272   const gchar *p;
273
274   for (p = key; *p != 0; p++)
275     {
276       gchar c = *p;
277       
278       if (c != '-' &&
279           (c < '0' || c > '9') &&
280           (c < 'A' || c > 'Z') &&
281           (c < 'a' || c > 'z'))
282         return FALSE;
283     }
284
285   return TRUE;
286 }
287
288 gpointer
289 g_param_spec_internal (GType        param_type,
290                        const gchar *name,
291                        const gchar *nick,
292                        const gchar *blurb,
293                        GParamFlags  flags)
294 {
295   GParamSpec *pspec;
296   
297   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
298   g_return_val_if_fail (name != NULL, NULL);
299   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
300   g_return_val_if_fail (!(flags & G_PARAM_STATIC_NAME) || is_canonical (name), NULL);
301   
302   pspec = (gpointer) g_type_create_instance (param_type);
303
304   if ((flags & G_PARAM_STATIC_NAME))
305     pspec->name = (gchar *) name;
306   else
307     {
308       pspec->name = g_strdup (name);
309       canonicalize_key (pspec->name);
310     }
311
312   if (flags & G_PARAM_STATIC_NICK)
313     pspec->_nick = (gchar *) nick;
314   else
315     pspec->_nick = g_strdup (nick);
316
317   if (flags & G_PARAM_STATIC_BLURB)
318     pspec->_blurb = (gchar *) blurb;
319   else
320     pspec->_blurb = g_strdup (blurb);
321
322   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
323   
324   return pspec;
325 }
326
327 gpointer
328 g_param_spec_get_qdata (GParamSpec *pspec,
329                         GQuark      quark)
330 {
331   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
332   
333   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
334 }
335
336 void
337 g_param_spec_set_qdata (GParamSpec *pspec,
338                         GQuark      quark,
339                         gpointer    data)
340 {
341   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
342   g_return_if_fail (quark > 0);
343
344   g_datalist_id_set_data (&pspec->qdata, quark, data);
345 }
346
347 void
348 g_param_spec_set_qdata_full (GParamSpec    *pspec,
349                              GQuark         quark,
350                              gpointer       data,
351                              GDestroyNotify destroy)
352 {
353   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
354   g_return_if_fail (quark > 0);
355
356   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
357 }
358
359 gpointer
360 g_param_spec_steal_qdata (GParamSpec *pspec,
361                           GQuark      quark)
362 {
363   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
364   g_return_val_if_fail (quark > 0, NULL);
365   
366   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
367 }
368
369 GParamSpec*
370 g_param_spec_get_redirect_target (GParamSpec *pspec)
371 {
372   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
373
374   if (G_IS_PARAM_SPEC_OVERRIDE (pspec))
375     {
376       GParamSpecOverride *ospec = G_PARAM_SPEC_OVERRIDE (pspec);
377
378       return ospec->overridden;
379     }
380   else
381     return NULL;
382 }
383
384 void
385 g_param_value_set_default (GParamSpec *pspec,
386                            GValue     *value)
387 {
388   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
389   g_return_if_fail (G_IS_VALUE (value));
390   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
391
392   g_value_reset (value);
393   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
394 }
395
396 gboolean
397 g_param_value_defaults (GParamSpec *pspec,
398                         GValue     *value)
399 {
400   GValue dflt_value = { 0, };
401   gboolean defaults;
402
403   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
404   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
405   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
406
407   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
408   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
409   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
410   g_value_unset (&dflt_value);
411
412   return defaults;
413 }
414
415 gboolean
416 g_param_value_validate (GParamSpec *pspec,
417                         GValue     *value)
418 {
419   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
420   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
421   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
422
423   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
424     {
425       GValue oval = *value;
426
427       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
428           memcmp (&oval.data, &value->data, sizeof (oval.data)))
429         return TRUE;
430     }
431
432   return FALSE;
433 }
434
435 gboolean
436 g_param_value_convert (GParamSpec   *pspec,
437                        const GValue *src_value,
438                        GValue       *dest_value,
439                        gboolean      strict_validation)
440 {
441   GValue tmp_value = { 0, };
442
443   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
444   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
445   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
446   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
447
448   /* better leave dest_value untouched when returning FALSE */
449
450   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
451   if (g_value_transform (src_value, &tmp_value) &&
452       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
453     {
454       g_value_unset (dest_value);
455       
456       /* values are relocatable */
457       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
458       
459       return TRUE;
460     }
461   else
462     {
463       g_value_unset (&tmp_value);
464       
465       return FALSE;
466     }
467 }
468
469 gint
470 g_param_values_cmp (GParamSpec   *pspec,
471                     const GValue *value1,
472                     const GValue *value2)
473 {
474   gint cmp;
475
476   /* param_values_cmp() effectively does: value1 - value2
477    * so the return values are:
478    * -1)  value1 < value2
479    *  0)  value1 == value2
480    *  1)  value1 > value2
481    */
482   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
483   g_return_val_if_fail (G_IS_VALUE (value1), 0);
484   g_return_val_if_fail (G_IS_VALUE (value2), 0);
485   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
486   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
487
488   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
489
490   return CLAMP (cmp, -1, 1);
491 }
492
493 static void
494 value_param_init (GValue *value)
495 {
496   value->data[0].v_pointer = NULL;
497 }
498
499 static void
500 value_param_free_value (GValue *value)
501 {
502   if (value->data[0].v_pointer)
503     g_param_spec_unref (value->data[0].v_pointer);
504 }
505
506 static void
507 value_param_copy_value (const GValue *src_value,
508                         GValue       *dest_value)
509 {
510   if (src_value->data[0].v_pointer)
511     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
512   else
513     dest_value->data[0].v_pointer = NULL;
514 }
515
516 static void
517 value_param_transform_value (const GValue *src_value,
518                              GValue       *dest_value)
519 {
520   if (src_value->data[0].v_pointer &&
521       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
522     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
523   else
524     dest_value->data[0].v_pointer = NULL;
525 }
526
527 static gpointer
528 value_param_peek_pointer (const GValue *value)
529 {
530   return value->data[0].v_pointer;
531 }
532
533 static gchar*
534 value_param_collect_value (GValue      *value,
535                            guint        n_collect_values,
536                            GTypeCValue *collect_values,
537                            guint        collect_flags)
538 {
539   if (collect_values[0].v_pointer)
540     {
541       GParamSpec *param = collect_values[0].v_pointer;
542
543       if (param->g_type_instance.g_class == NULL)
544         return g_strconcat ("invalid unclassed param spec pointer for value type `",
545                             G_VALUE_TYPE_NAME (value),
546                             "'",
547                             NULL);
548       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
549         return g_strconcat ("invalid param spec type `",
550                             G_PARAM_SPEC_TYPE_NAME (param),
551                             "' for value type `",
552                             G_VALUE_TYPE_NAME (value),
553                             "'",
554                             NULL);
555       value->data[0].v_pointer = g_param_spec_ref (param);
556     }
557   else
558     value->data[0].v_pointer = NULL;
559
560   return NULL;
561 }
562
563 static gchar*
564 value_param_lcopy_value (const GValue *value,
565                          guint         n_collect_values,
566                          GTypeCValue  *collect_values,
567                          guint         collect_flags)
568 {
569   GParamSpec **param_p = collect_values[0].v_pointer;
570
571   if (!param_p)
572     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
573
574   if (!value->data[0].v_pointer)
575     *param_p = NULL;
576   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
577     *param_p = value->data[0].v_pointer;
578   else
579     *param_p = g_param_spec_ref (value->data[0].v_pointer);
580
581   return NULL;
582 }
583
584
585 /* --- param spec pool --- */
586 struct _GParamSpecPool
587 {
588   GStaticMutex smutex;
589   gboolean     type_prefixing;
590   GHashTable  *hash_table;
591 };
592
593 static guint
594 param_spec_pool_hash (gconstpointer key_spec)
595 {
596   const GParamSpec *key = key_spec;
597   const gchar *p;
598   guint h = key->owner_type;
599
600   for (p = key->name; *p; p++)
601     h = (h << 5) - h + *p;
602
603   return h;
604 }
605
606 static gboolean
607 param_spec_pool_equals (gconstpointer key_spec_1,
608                         gconstpointer key_spec_2)
609 {
610   const GParamSpec *key1 = key_spec_1;
611   const GParamSpec *key2 = key_spec_2;
612
613   return (key1->owner_type == key2->owner_type &&
614           strcmp (key1->name, key2->name) == 0);
615 }
616
617 GParamSpecPool*
618 g_param_spec_pool_new (gboolean type_prefixing)
619 {
620   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
621   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
622
623   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
624   pool->type_prefixing = type_prefixing != FALSE;
625   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
626
627   return pool;
628 }
629
630 void
631 g_param_spec_pool_insert (GParamSpecPool *pool,
632                           GParamSpec     *pspec,
633                           GType           owner_type)
634 {
635   gchar *p;
636   
637   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
638     {
639       G_SLOCK (&pool->smutex);
640       for (p = pspec->name; *p; p++)
641         {
642           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
643             {
644               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
645               G_SUNLOCK (&pool->smutex);
646               return;
647             }
648         }
649       
650       pspec->owner_type = owner_type;
651       g_param_spec_ref (pspec);
652       g_hash_table_insert (pool->hash_table, pspec, pspec);
653       G_SUNLOCK (&pool->smutex);
654     }
655   else
656     {
657       g_return_if_fail (pool != NULL);
658       g_return_if_fail (pspec);
659       g_return_if_fail (owner_type > 0);
660       g_return_if_fail (pspec->owner_type == 0);
661     }
662 }
663
664 void
665 g_param_spec_pool_remove (GParamSpecPool *pool,
666                           GParamSpec     *pspec)
667 {
668   if (pool && pspec)
669     {
670       G_SLOCK (&pool->smutex);
671       if (g_hash_table_remove (pool->hash_table, pspec))
672         g_param_spec_unref (pspec);
673       else
674         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
675       G_SUNLOCK (&pool->smutex);
676     }
677   else
678     {
679       g_return_if_fail (pool != NULL);
680       g_return_if_fail (pspec);
681     }
682 }
683
684 static inline GParamSpec*
685 param_spec_ht_lookup (GHashTable  *hash_table,
686                       const gchar *param_name,
687                       GType        owner_type,
688                       gboolean     walk_ancestors)
689 {
690   GParamSpec key, *pspec;
691
692   key.owner_type = owner_type;
693   key.name = (gchar*) param_name;
694   if (walk_ancestors)
695     do
696       {
697         pspec = g_hash_table_lookup (hash_table, &key);
698         if (pspec)
699           return pspec;
700         key.owner_type = g_type_parent (key.owner_type);
701       }
702     while (key.owner_type);
703   else
704     pspec = g_hash_table_lookup (hash_table, &key);
705
706   if (!pspec && !is_canonical (param_name))
707     {
708       /* try canonicalized form */
709       key.name = g_strdup (param_name);
710       key.owner_type = owner_type;
711       
712       canonicalize_key (key.name);
713       if (walk_ancestors)
714         do
715           {
716             pspec = g_hash_table_lookup (hash_table, &key);
717             if (pspec)
718               {
719                 g_free (key.name);
720                 return pspec;
721               }
722             key.owner_type = g_type_parent (key.owner_type);
723           }
724         while (key.owner_type);
725       else
726         pspec = g_hash_table_lookup (hash_table, &key);
727       g_free (key.name);
728     }
729
730   return pspec;
731 }
732
733 GParamSpec*
734 g_param_spec_pool_lookup (GParamSpecPool *pool,
735                           const gchar    *param_name,
736                           GType           owner_type,
737                           gboolean        walk_ancestors)
738 {
739   GParamSpec *pspec;
740   gchar *delim;
741
742   if (!pool || !param_name)
743     {
744       g_return_val_if_fail (pool != NULL, NULL);
745       g_return_val_if_fail (param_name != NULL, NULL);
746     }
747
748   G_SLOCK (&pool->smutex);
749
750   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
751
752   /* try quick and away, i.e. without prefix */
753   if (!delim)
754     {
755       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
756       G_SUNLOCK (&pool->smutex);
757
758       return pspec;
759     }
760
761   /* strip type prefix */
762   if (pool->type_prefixing && delim[1] == ':')
763     {
764       guint l = delim - param_name;
765       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
766       GType type;
767       
768       strncpy (buffer, param_name, delim - param_name);
769       buffer[l] = 0;
770       type = g_type_from_name (buffer);
771       if (l >= 32)
772         g_free (buffer);
773       if (type)         /* type==0 isn't a valid type pefix */
774         {
775           /* sanity check, these cases don't make a whole lot of sense */
776           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
777             {
778               G_SUNLOCK (&pool->smutex);
779
780               return NULL;
781             }
782           owner_type = type;
783           param_name += l + 2;
784           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
785           G_SUNLOCK (&pool->smutex);
786
787           return pspec;
788         }
789     }
790   /* malformed param_name */
791
792   G_SUNLOCK (&pool->smutex);
793
794   return NULL;
795 }
796
797 static void
798 pool_list (gpointer key,
799            gpointer value,
800            gpointer user_data)
801 {
802   GParamSpec *pspec = value;
803   gpointer *data = user_data;
804   GType owner_type = (GType) data[1];
805
806   if (owner_type == pspec->owner_type)
807     data[0] = g_list_prepend (data[0], pspec);
808 }
809
810 GList*
811 g_param_spec_pool_list_owned (GParamSpecPool *pool,
812                               GType           owner_type)
813 {
814   gpointer data[2];
815
816   g_return_val_if_fail (pool != NULL, NULL);
817   g_return_val_if_fail (owner_type > 0, NULL);
818   
819   G_SLOCK (&pool->smutex);
820   data[0] = NULL;
821   data[1] = (gpointer) owner_type;
822   g_hash_table_foreach (pool->hash_table, pool_list, &data);
823   G_SUNLOCK (&pool->smutex);
824
825   return data[0];
826 }
827
828 static gint
829 pspec_compare_id (gconstpointer a,
830                   gconstpointer b)
831 {
832   const GParamSpec *pspec1 = a, *pspec2 = b;
833
834   return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id;
835 }
836
837 static inline GSList*
838 pspec_list_remove_overridden_and_redirected (GSList     *plist,
839                                              GHashTable *ht,
840                                              GType       owner_type,
841                                              guint      *n_p)
842 {
843   GSList *rlist = NULL;
844
845   while (plist)
846     {
847       GSList *tmp = plist->next;
848       GParamSpec *pspec = plist->data;
849       GParamSpec *found;
850       gboolean remove = FALSE;
851
852       /* Remove paramspecs that are redirected, and also paramspecs
853        * that have are overridden by non-redirected properties.
854        * The idea is to get the single paramspec for each name that
855        * best corresponds to what the application sees.
856        */
857       if (g_param_spec_get_redirect_target (pspec))
858         remove = TRUE;
859       else
860         {
861           found = param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE);
862           if (found != pspec)
863             {
864               GParamSpec *redirect = g_param_spec_get_redirect_target (found);
865               if (redirect != pspec)
866                 remove = TRUE;
867             }
868         }
869
870       if (remove)
871         {
872           g_slist_free_1 (plist);
873         }
874       else
875         {
876           plist->next = rlist;
877           rlist = plist;
878           *n_p += 1;
879         }
880       plist = tmp;
881     }
882   return rlist;
883 }
884
885 static void
886 pool_depth_list (gpointer key,
887                  gpointer value,
888                  gpointer user_data)
889 {
890   GParamSpec *pspec = value;
891   gpointer *data = user_data;
892   GSList **slists = data[0];
893   GType owner_type = (GType) data[1];
894
895   if (g_type_is_a (owner_type, pspec->owner_type))
896     {
897       if (G_TYPE_IS_INTERFACE (pspec->owner_type))
898         {
899           slists[0] = g_slist_prepend (slists[0], pspec);
900         }
901       else
902         {
903           guint d = g_type_depth (pspec->owner_type);
904
905           slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
906         }
907     }
908 }
909
910 /* We handle interfaces specially since we don't want to
911  * count interface prerequsites like normal inheritance;
912  * the property comes from the direct inheritance from
913  * the prerequisite class, not from the interface that
914  * prerequires it.
915  * 
916  * also 'depth' isn't a meaningful concept for interface
917  * prerequites.
918  */
919 static void
920 pool_depth_list_for_interface (gpointer key,
921                                gpointer value,
922                                gpointer user_data)
923 {
924   GParamSpec *pspec = value;
925   gpointer *data = user_data;
926   GSList **slists = data[0];
927   GType owner_type = (GType) data[1];
928
929   if (pspec->owner_type == owner_type)
930     slists[0] = g_slist_prepend (slists[0], pspec);
931 }
932
933 GParamSpec** /* free result */
934 g_param_spec_pool_list (GParamSpecPool *pool,
935                         GType           owner_type,
936                         guint          *n_pspecs_p)
937 {
938   GParamSpec **pspecs, **p;
939   GSList **slists, *node;
940   gpointer data[2];
941   guint d, i;
942
943   g_return_val_if_fail (pool != NULL, NULL);
944   g_return_val_if_fail (owner_type > 0, NULL);
945   g_return_val_if_fail (n_pspecs_p != NULL, NULL);
946   
947   G_SLOCK (&pool->smutex);
948   *n_pspecs_p = 0;
949   d = g_type_depth (owner_type);
950   slists = g_new0 (GSList*, d);
951   data[0] = slists;
952   data[1] = (gpointer) owner_type;
953
954   g_hash_table_foreach (pool->hash_table,
955                         G_TYPE_IS_INTERFACE (owner_type) ?
956                            pool_depth_list_for_interface :
957                            pool_depth_list,
958                         &data);
959   
960   for (i = 0; i < d; i++)
961     slists[i] = pspec_list_remove_overridden_and_redirected (slists[i], pool->hash_table, owner_type, n_pspecs_p);
962   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
963   p = pspecs;
964   for (i = 0; i < d; i++)
965     {
966       slists[i] = g_slist_sort (slists[i], pspec_compare_id);
967       for (node = slists[i]; node; node = node->next)
968         *p++ = node->data;
969       g_slist_free (slists[i]);
970     }
971   *p++ = NULL;
972   g_free (slists);
973   G_SUNLOCK (&pool->smutex);
974
975   return pspecs;
976 }
977
978
979 /* --- auxillary functions --- */
980 typedef struct
981 {
982   /* class portion */
983   GType           value_type;
984   void          (*finalize)             (GParamSpec   *pspec);
985   void          (*value_set_default)    (GParamSpec   *pspec,
986                                          GValue       *value);
987   gboolean      (*value_validate)       (GParamSpec   *pspec,
988                                          GValue       *value);
989   gint          (*values_cmp)           (GParamSpec   *pspec,
990                                          const GValue *value1,
991                                          const GValue *value2);
992 } ParamSpecClassInfo;
993
994 static void
995 param_spec_generic_class_init (gpointer g_class,
996                                gpointer class_data)
997 {
998   GParamSpecClass *class = g_class;
999   ParamSpecClassInfo *info = class_data;
1000
1001   class->value_type = info->value_type;
1002   if (info->finalize)
1003     class->finalize = info->finalize;                   /* optional */
1004   class->value_set_default = info->value_set_default;
1005   if (info->value_validate)
1006     class->value_validate = info->value_validate;       /* optional */
1007   class->values_cmp = info->values_cmp;
1008   g_free (class_data);
1009 }
1010
1011 static void
1012 default_value_set_default (GParamSpec *pspec,
1013                            GValue     *value)
1014 {
1015   /* value is already zero initialized */
1016 }
1017
1018 static gint
1019 default_values_cmp (GParamSpec   *pspec,
1020                     const GValue *value1,
1021                     const GValue *value2)
1022 {
1023   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
1024 }
1025
1026 GType
1027 g_param_type_register_static (const gchar              *name,
1028                               const GParamSpecTypeInfo *pspec_info)
1029 {
1030   GTypeInfo info = {
1031     sizeof (GParamSpecClass),      /* class_size */
1032     NULL,                          /* base_init */
1033     NULL,                          /* base_destroy */
1034     param_spec_generic_class_init, /* class_init */
1035     NULL,                          /* class_destroy */
1036     NULL,                          /* class_data */
1037     0,                             /* instance_size */
1038     16,                            /* n_preallocs */
1039     NULL,                          /* instance_init */
1040   };
1041   ParamSpecClassInfo *cinfo;
1042
1043   g_return_val_if_fail (name != NULL, 0);
1044   g_return_val_if_fail (pspec_info != NULL, 0);
1045   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
1046   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
1047   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
1048   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
1049   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
1050   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
1051
1052   info.instance_size = pspec_info->instance_size;
1053   info.n_preallocs = pspec_info->n_preallocs;
1054   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
1055   cinfo = g_new (ParamSpecClassInfo, 1);
1056   cinfo->value_type = pspec_info->value_type;
1057   cinfo->finalize = pspec_info->finalize;
1058   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
1059   cinfo->value_validate = pspec_info->value_validate;
1060   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
1061   info.class_data = cinfo;
1062
1063   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
1064 }
1065
1066 void
1067 g_value_set_param (GValue     *value,
1068                    GParamSpec *param)
1069 {
1070   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1071   if (param)
1072     g_return_if_fail (G_IS_PARAM_SPEC (param));
1073
1074   if (value->data[0].v_pointer)
1075     g_param_spec_unref (value->data[0].v_pointer);
1076   value->data[0].v_pointer = param;
1077   if (value->data[0].v_pointer)
1078     g_param_spec_ref (value->data[0].v_pointer);
1079 }
1080
1081 void
1082 g_value_set_param_take_ownership (GValue     *value,
1083                                   GParamSpec *param)
1084 {
1085   g_value_take_param (value, param);
1086 }
1087
1088 void
1089 g_value_take_param (GValue     *value,
1090                     GParamSpec *param)
1091 {
1092   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
1093   if (param)
1094     g_return_if_fail (G_IS_PARAM_SPEC (param));
1095
1096   if (value->data[0].v_pointer)
1097     g_param_spec_unref (value->data[0].v_pointer);
1098   value->data[0].v_pointer = param; /* we take over the reference count */
1099 }
1100
1101 GParamSpec*
1102 g_value_get_param (const GValue *value)
1103 {
1104   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1105
1106   return value->data[0].v_pointer;
1107 }
1108
1109 GParamSpec*
1110 g_value_dup_param (const GValue *value)
1111 {
1112   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1113
1114   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
1115 }
1116
1117 #define __G_PARAM_C__
1118 #include "gobjectaliasdef.c"