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