Function to canonicalize parameter names. Faster than using g_strcanon().
[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
26
27 #include        "gvaluecollector.h"
28 #include        <string.h>
29
30
31
32 /* --- defines --- */
33 #define G_PARAM_USER_MASK                       (~0 << G_PARAM_USER_SHIFT)
34 #define PSPEC_APPLIES_TO_VALUE(pspec, value)    (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
35 #define G_SLOCK(mutex)                          g_static_mutex_lock (mutex)
36 #define G_SUNLOCK(mutex)                        g_static_mutex_unlock (mutex)
37
38
39 /* --- prototypes --- */
40 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
41 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
42 static void     g_param_spec_class_init          (GParamSpecClass       *class,
43                                                   gpointer               class_data);
44 static void     g_param_spec_init                (GParamSpec            *pspec,
45                                                   GParamSpecClass       *class);
46 static void     g_param_spec_finalize            (GParamSpec            *pspec);
47 static void     value_param_init                (GValue         *value);
48 static void     value_param_free_value          (GValue         *value);
49 static void     value_param_copy_value          (const GValue   *src_value,
50                                                  GValue         *dest_value);
51 static void     value_param_transform_value     (const GValue   *src_value,
52                                                  GValue         *dest_value);
53 static gpointer value_param_peek_pointer        (const GValue   *value);
54 static gchar*   value_param_collect_value       (GValue         *value,
55                                                  guint           n_collect_values,
56                                                  GTypeCValue    *collect_values,
57                                                  guint           collect_flags);
58 static gchar*   value_param_lcopy_value         (const GValue   *value,
59                                                  guint           n_collect_values,
60                                                  GTypeCValue    *collect_values,
61                                                  guint           collect_flags);
62
63
64 /* --- variables --- */
65 static GQuark quark_floating = 0;
66 G_LOCK_DEFINE_STATIC (pspec_ref_count);
67
68
69 /* --- functions --- */
70 void
71 g_param_type_init (void)        /* sync with gtype.c */
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   g_free (pspec->name);
156   g_free (pspec->_nick);
157   g_free (pspec->_blurb);
158
159   g_type_free_instance ((GTypeInstance*) pspec);
160 }
161
162 GParamSpec*
163 g_param_spec_ref (GParamSpec *pspec)
164 {
165   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
166
167   G_LOCK (pspec_ref_count);
168   if (pspec->ref_count > 0)
169     {
170       pspec->ref_count += 1;
171       G_UNLOCK (pspec_ref_count);
172     }
173   else
174     {
175       G_UNLOCK (pspec_ref_count);
176       g_return_val_if_fail (pspec->ref_count > 0, NULL);
177     }
178   
179   return pspec;
180 }
181
182 void
183 g_param_spec_unref (GParamSpec *pspec)
184 {
185   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
186
187   G_LOCK (pspec_ref_count);
188   if (pspec->ref_count > 0)
189     {
190       gboolean need_finalize;
191
192       /* sync with _sink */
193       pspec->ref_count -= 1;
194       need_finalize = pspec->ref_count == 0;
195       G_UNLOCK (pspec_ref_count);
196       if (need_finalize)
197         G_PARAM_SPEC_GET_CLASS (pspec)->finalize (pspec);
198     }
199   else
200     {
201       G_UNLOCK (pspec_ref_count);
202       g_return_if_fail (pspec->ref_count > 0);
203     }
204 }
205
206 void
207 g_param_spec_sink (GParamSpec *pspec)
208 {
209   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
210
211   G_LOCK (pspec_ref_count);
212   if (pspec->ref_count > 0)
213     {
214       if (g_datalist_id_remove_no_notify (&pspec->qdata, quark_floating))
215         {
216           /* sync with _unref */
217           if (pspec->ref_count > 1)
218             pspec->ref_count -= 1;
219           else
220             {
221               G_UNLOCK (pspec_ref_count);
222               g_param_spec_unref (pspec);
223
224               return;
225             }
226         }
227       G_UNLOCK (pspec_ref_count);
228     }
229   else
230     {
231       G_UNLOCK (pspec_ref_count);
232       g_return_if_fail (pspec->ref_count > 0);
233     }
234 }
235
236 G_CONST_RETURN gchar*
237 g_param_spec_get_name (GParamSpec *pspec)
238 {
239   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
240
241   return pspec->name;
242 }
243
244 G_CONST_RETURN gchar*
245 g_param_spec_get_nick (GParamSpec *pspec)
246 {
247   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
248
249   return pspec->_nick ? pspec->_nick : pspec->name;
250 }
251
252 G_CONST_RETURN gchar*
253 g_param_spec_get_blurb (GParamSpec *pspec)
254 {
255   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
256
257   return pspec->_blurb;
258 }
259
260 static void
261 canonalize_key(gchar *key)
262 {
263   gchar *p, c;
264
265   for (p = key; (c = *p) != 0; p++)
266     {
267       if (c != '-' &&
268           (c < '0' || c > '9') &&
269           (c < 'A' || c > 'Z') &&
270           (c < 'a' || c > 'z'))
271         *p = '-';
272     }
273 }
274
275 gpointer
276 g_param_spec_internal (GType        param_type,
277                        const gchar *name,
278                        const gchar *nick,
279                        const gchar *blurb,
280                        GParamFlags  flags)
281 {
282   GParamSpec *pspec;
283
284   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
285   g_return_val_if_fail (name != NULL, NULL);
286   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
287
288   pspec = (gpointer) g_type_create_instance (param_type);
289   pspec->name = g_strdup (name);
290   canonalize_key (pspec->name);
291   pspec->_nick = g_strdup (nick);
292   pspec->_blurb = g_strdup (blurb);
293   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
294
295   return pspec;
296 }
297
298 gpointer
299 g_param_spec_get_qdata (GParamSpec *pspec,
300                         GQuark      quark)
301 {
302   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
303   
304   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
305 }
306
307 void
308 g_param_spec_set_qdata (GParamSpec *pspec,
309                         GQuark      quark,
310                         gpointer    data)
311 {
312   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
313   g_return_if_fail (quark > 0);
314
315   g_datalist_id_set_data (&pspec->qdata, quark, data);
316 }
317
318 void
319 g_param_spec_set_qdata_full (GParamSpec    *pspec,
320                              GQuark         quark,
321                              gpointer       data,
322                              GDestroyNotify destroy)
323 {
324   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
325   g_return_if_fail (quark > 0);
326
327   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
328 }
329
330 gpointer
331 g_param_spec_steal_qdata (GParamSpec *pspec,
332                           GQuark      quark)
333 {
334   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
335   g_return_val_if_fail (quark > 0, NULL);
336   
337   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
338 }
339
340 void
341 g_param_value_set_default (GParamSpec *pspec,
342                            GValue     *value)
343 {
344   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
345   g_return_if_fail (G_IS_VALUE (value));
346   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
347
348   g_value_reset (value);
349   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
350 }
351
352 gboolean
353 g_param_value_defaults (GParamSpec *pspec,
354                         GValue     *value)
355 {
356   GValue dflt_value = { 0, };
357   gboolean defaults;
358
359   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
360   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
361   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
362
363   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
364   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
365   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
366   g_value_unset (&dflt_value);
367
368   return defaults;
369 }
370
371 gboolean
372 g_param_value_validate (GParamSpec *pspec,
373                         GValue     *value)
374 {
375   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
376   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
377   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
378
379   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
380     {
381       GValue oval = *value;
382
383       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
384           memcmp (&oval.data, &value->data, sizeof (oval.data)))
385         return TRUE;
386     }
387
388   return FALSE;
389 }
390
391 gboolean
392 g_param_value_convert (GParamSpec   *pspec,
393                        const GValue *src_value,
394                        GValue       *dest_value,
395                        gboolean      strict_validation)
396 {
397   GValue tmp_value = { 0, };
398
399   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
400   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
401   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
402   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
403
404   /* better leave dest_value untouched when returning FALSE */
405
406   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
407   if (g_value_transform (src_value, &tmp_value) &&
408       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
409     {
410       g_value_unset (dest_value);
411       
412       /* values are relocatable */
413       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
414       
415       return TRUE;
416     }
417   else
418     {
419       g_value_unset (&tmp_value);
420       
421       return FALSE;
422     }
423 }
424
425 gint
426 g_param_values_cmp (GParamSpec   *pspec,
427                     const GValue *value1,
428                     const GValue *value2)
429 {
430   gint cmp;
431
432   /* param_values_cmp() effectively does: value1 - value2
433    * so the return values are:
434    * -1)  value1 < value2
435    *  0)  value1 == value2
436    *  1)  value1 > value2
437    */
438   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
439   g_return_val_if_fail (G_IS_VALUE (value1), 0);
440   g_return_val_if_fail (G_IS_VALUE (value2), 0);
441   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
442   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
443
444   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
445
446   return CLAMP (cmp, -1, 1);
447 }
448
449 static void
450 value_param_init (GValue *value)
451 {
452   value->data[0].v_pointer = NULL;
453 }
454
455 static void
456 value_param_free_value (GValue *value)
457 {
458   if (value->data[0].v_pointer)
459     g_param_spec_unref (value->data[0].v_pointer);
460 }
461
462 static void
463 value_param_copy_value (const GValue *src_value,
464                         GValue       *dest_value)
465 {
466   if (src_value->data[0].v_pointer)
467     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
468   else
469     dest_value->data[0].v_pointer = NULL;
470 }
471
472 static void
473 value_param_transform_value (const GValue *src_value,
474                              GValue       *dest_value)
475 {
476   if (src_value->data[0].v_pointer &&
477       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
478     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
479   else
480     dest_value->data[0].v_pointer = NULL;
481 }
482
483 static gpointer
484 value_param_peek_pointer (const GValue *value)
485 {
486   return value->data[0].v_pointer;
487 }
488
489 static gchar*
490 value_param_collect_value (GValue      *value,
491                            guint        n_collect_values,
492                            GTypeCValue *collect_values,
493                            guint        collect_flags)
494 {
495   if (collect_values[0].v_pointer)
496     {
497       GParamSpec *param = collect_values[0].v_pointer;
498
499       if (param->g_type_instance.g_class == NULL)
500         return g_strconcat ("invalid unclassed param spec pointer for value type `",
501                             G_VALUE_TYPE_NAME (value),
502                             "'",
503                             NULL);
504       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
505         return g_strconcat ("invalid param spec type `",
506                             G_PARAM_SPEC_TYPE_NAME (param),
507                             "' for value type `",
508                             G_VALUE_TYPE_NAME (value),
509                             "'",
510                             NULL);
511       value->data[0].v_pointer = g_param_spec_ref (param);
512     }
513   else
514     value->data[0].v_pointer = NULL;
515
516   return NULL;
517 }
518
519 static gchar*
520 value_param_lcopy_value (const GValue *value,
521                          guint         n_collect_values,
522                          GTypeCValue  *collect_values,
523                          guint         collect_flags)
524 {
525   GParamSpec **param_p = collect_values[0].v_pointer;
526
527   if (!param_p)
528     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
529
530   if (!value->data[0].v_pointer)
531     *param_p = NULL;
532   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
533     *param_p = value->data[0].v_pointer;
534   else
535     *param_p = g_param_spec_ref (value->data[0].v_pointer);
536
537   return NULL;
538 }
539
540
541 /* --- param spec pool --- */
542 struct _GParamSpecPool
543 {
544   GStaticMutex smutex;
545   gboolean     type_prefixing;
546   GHashTable  *hash_table;
547 };
548
549 static guint
550 param_spec_pool_hash (gconstpointer key_spec)
551 {
552   const GParamSpec *key = key_spec;
553   const gchar *p;
554   guint h = key->owner_type;
555
556   for (p = key->name; *p; p++)
557     h = (h << 5) - h + *p;
558
559   return h;
560 }
561
562 static gboolean
563 param_spec_pool_equals (gconstpointer key_spec_1,
564                         gconstpointer key_spec_2)
565 {
566   const GParamSpec *key1 = key_spec_1;
567   const GParamSpec *key2 = key_spec_2;
568
569   return (key1->owner_type == key2->owner_type &&
570           strcmp (key1->name, key2->name) == 0);
571 }
572
573 GParamSpecPool*
574 g_param_spec_pool_new (gboolean type_prefixing)
575 {
576   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
577   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
578
579   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
580   pool->type_prefixing = type_prefixing != FALSE;
581   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
582
583   return pool;
584 }
585
586 void
587 g_param_spec_pool_insert (GParamSpecPool *pool,
588                           GParamSpec     *pspec,
589                           GType           owner_type)
590 {
591   gchar *p;
592   
593   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
594     {
595       G_SLOCK (&pool->smutex);
596       for (p = pspec->name; *p; p++)
597         {
598           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
599             {
600               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
601               G_SUNLOCK (&pool->smutex);
602               return;
603             }
604         }
605       
606       pspec->owner_type = owner_type;
607       g_param_spec_ref (pspec);
608       g_hash_table_insert (pool->hash_table, pspec, pspec);
609       G_SUNLOCK (&pool->smutex);
610     }
611   else
612     {
613       g_return_if_fail (pool != NULL);
614       g_return_if_fail (pspec);
615       g_return_if_fail (owner_type > 0);
616       g_return_if_fail (pspec->owner_type == 0);
617     }
618 }
619
620 void
621 g_param_spec_pool_remove (GParamSpecPool *pool,
622                           GParamSpec     *pspec)
623 {
624   if (pool && pspec)
625     {
626       G_SLOCK (&pool->smutex);
627       if (g_hash_table_remove (pool->hash_table, pspec))
628         g_param_spec_unref (pspec);
629       else
630         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
631       G_SUNLOCK (&pool->smutex);
632     }
633   else
634     {
635       g_return_if_fail (pool != NULL);
636       g_return_if_fail (pspec);
637     }
638 }
639
640 static inline GParamSpec*
641 param_spec_ht_lookup (GHashTable  *hash_table,
642                       const gchar *param_name,
643                       GType        owner_type,
644                       gboolean     walk_ancestors)
645 {
646   GParamSpec key, *pspec;
647
648   key.owner_type = owner_type;
649   key.name = (gchar*) param_name;
650   if (walk_ancestors)
651     do
652       {
653         pspec = g_hash_table_lookup (hash_table, &key);
654         if (pspec)
655           return pspec;
656         key.owner_type = g_type_parent (key.owner_type);
657       }
658     while (key.owner_type);
659   else
660     pspec = g_hash_table_lookup (hash_table, &key);
661
662   if (!pspec)
663     {
664       /* try canonicalized form */
665       key.name = g_strdup (param_name);
666       key.owner_type = owner_type;
667       
668       canonalize_key (key.name);
669       if (walk_ancestors)
670         do
671           {
672             pspec = g_hash_table_lookup (hash_table, &key);
673             if (pspec)
674               {
675                 g_free (key.name);
676                 return pspec;
677               }
678             key.owner_type = g_type_parent (key.owner_type);
679           }
680         while (key.owner_type);
681       else
682         pspec = g_hash_table_lookup (hash_table, &key);
683       g_free (key.name);
684     }
685
686   return pspec;
687 }
688
689 GParamSpec*
690 g_param_spec_pool_lookup (GParamSpecPool *pool,
691                           const gchar    *param_name,
692                           GType           owner_type,
693                           gboolean        walk_ancestors)
694 {
695   GParamSpec *pspec;
696   gchar *delim;
697
698   if (!pool || !param_name)
699     {
700       g_return_val_if_fail (pool != NULL, NULL);
701       g_return_val_if_fail (param_name != NULL, NULL);
702     }
703
704   G_SLOCK (&pool->smutex);
705
706   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
707
708   /* try quick and away, i.e. without prefix */
709   if (!delim)
710     {
711       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
712       G_SUNLOCK (&pool->smutex);
713
714       return pspec;
715     }
716
717   /* strip type prefix */
718   if (pool->type_prefixing && delim[1] == ':')
719     {
720       guint l = delim - param_name;
721       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
722       GType type;
723       
724       strncpy (buffer, param_name, delim - param_name);
725       buffer[l] = 0;
726       type = g_type_from_name (buffer);
727       if (l >= 32)
728         g_free (buffer);
729       if (type)         /* type==0 isn't a valid type pefix */
730         {
731           /* sanity check, these cases don't make a whole lot of sense */
732           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
733             {
734               G_SUNLOCK (&pool->smutex);
735
736               return NULL;
737             }
738           owner_type = type;
739           param_name += l + 2;
740           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
741           G_SUNLOCK (&pool->smutex);
742
743           return pspec;
744         }
745     }
746   /* malformed param_name */
747
748   G_SUNLOCK (&pool->smutex);
749
750   return NULL;
751 }
752
753 static void
754 pool_list (gpointer key,
755            gpointer value,
756            gpointer user_data)
757 {
758   GParamSpec *pspec = value;
759   gpointer *data = user_data;
760   GType owner_type = (GType) data[1];
761
762   if (owner_type == pspec->owner_type)
763     data[0] = g_list_prepend (data[0], pspec);
764 }
765
766 GList*
767 g_param_spec_pool_list_owned (GParamSpecPool *pool,
768                               GType           owner_type)
769 {
770   gpointer data[2];
771
772   g_return_val_if_fail (pool != NULL, NULL);
773   g_return_val_if_fail (owner_type > 0, NULL);
774   
775   G_SLOCK (&pool->smutex);
776   data[0] = NULL;
777   data[1] = (gpointer) owner_type;
778   g_hash_table_foreach (pool->hash_table, pool_list, &data);
779   G_SUNLOCK (&pool->smutex);
780
781   return data[0];
782 }
783
784 static gint
785 pspec_compare_id (gconstpointer a,
786                   gconstpointer b)
787 {
788   const GParamSpec *pspec1 = a, *pspec2 = b;
789
790   return pspec1->param_id < pspec2->param_id ? -1 : pspec1->param_id > pspec2->param_id;
791 }
792
793 static inline GSList*
794 pspec_list_remove_overridden (GSList     *plist,
795                               GHashTable *ht,
796                               GType       owner_type,
797                               guint      *n_p)
798 {
799   GSList *rlist = NULL;
800
801   while (plist)
802     {
803       GSList *tmp = plist->next;
804       GParamSpec *pspec = plist->data;
805
806       if (param_spec_ht_lookup (ht, pspec->name, owner_type, TRUE) != pspec)
807         g_slist_free_1 (plist);
808       else
809         {
810           plist->next = rlist;
811           rlist = plist;
812           *n_p += 1;
813         }
814       plist = tmp;
815     }
816   return rlist;
817 }
818
819 static void
820 pool_depth_list (gpointer key,
821                  gpointer value,
822                  gpointer user_data)
823 {
824   GParamSpec *pspec = value;
825   gpointer *data = user_data;
826   GSList **slists = data[0];
827   GType owner_type = (GType) data[1];
828
829   if (g_type_is_a (owner_type, pspec->owner_type))
830     {
831       guint d = g_type_depth (pspec->owner_type);
832
833       slists[d - 1] = g_slist_prepend (slists[d - 1], pspec);
834     }
835 }
836
837 GParamSpec** /* free result */
838 g_param_spec_pool_list (GParamSpecPool *pool,
839                         GType           owner_type,
840                         guint          *n_pspecs_p)
841 {
842   GParamSpec **pspecs, **p;
843   GSList **slists, *node;
844   gpointer data[2];
845   guint d, i;
846
847   g_return_val_if_fail (pool != NULL, NULL);
848   g_return_val_if_fail (owner_type > 0, NULL);
849   g_return_val_if_fail (n_pspecs_p != NULL, NULL);
850   
851   G_SLOCK (&pool->smutex);
852   *n_pspecs_p = 0;
853   d = g_type_depth (owner_type);
854   slists = g_new0 (GSList*, d);
855   data[0] = slists;
856   data[1] = (gpointer) owner_type;
857   g_hash_table_foreach (pool->hash_table, pool_depth_list, &data);
858   for (i = 0; i < d - 1; i++)
859     slists[i] = pspec_list_remove_overridden (slists[i], pool->hash_table, owner_type, n_pspecs_p);
860   *n_pspecs_p += g_slist_length (slists[i]);
861   pspecs = g_new (GParamSpec*, *n_pspecs_p + 1);
862   p = pspecs;
863   for (i = 0; i < d; i++)
864     {
865       slists[i] = g_slist_sort (slists[i], pspec_compare_id);
866       for (node = slists[i]; node; node = node->next)
867         *p++ = node->data;
868       g_slist_free (slists[i]);
869     }
870   *p++ = NULL;
871   g_free (slists);
872   G_SUNLOCK (&pool->smutex);
873
874   return pspecs;
875 }
876
877
878 /* --- auxillary functions --- */
879 typedef struct
880 {
881   /* class portion */
882   GType           value_type;
883   void          (*finalize)             (GParamSpec   *pspec);
884   void          (*value_set_default)    (GParamSpec   *pspec,
885                                          GValue       *value);
886   gboolean      (*value_validate)       (GParamSpec   *pspec,
887                                          GValue       *value);
888   gint          (*values_cmp)           (GParamSpec   *pspec,
889                                          const GValue *value1,
890                                          const GValue *value2);
891 } ParamSpecClassInfo;
892
893 static void
894 param_spec_generic_class_init (gpointer g_class,
895                                gpointer class_data)
896 {
897   GParamSpecClass *class = g_class;
898   ParamSpecClassInfo *info = class_data;
899
900   class->value_type = info->value_type;
901   if (info->finalize)
902     class->finalize = info->finalize;                   /* optional */
903   class->value_set_default = info->value_set_default;
904   if (info->value_validate)
905     class->value_validate = info->value_validate;       /* optional */
906   class->values_cmp = info->values_cmp;
907   g_free (class_data);
908 }
909
910 static void
911 default_value_set_default (GParamSpec *pspec,
912                            GValue     *value)
913 {
914   /* value is already zero initialized */
915 }
916
917 static gint
918 default_values_cmp (GParamSpec   *pspec,
919                     const GValue *value1,
920                     const GValue *value2)
921 {
922   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
923 }
924
925 GType
926 g_param_type_register_static (const gchar              *name,
927                               const GParamSpecTypeInfo *pspec_info)
928 {
929   GTypeInfo info = {
930     sizeof (GParamSpecClass),      /* class_size */
931     NULL,                          /* base_init */
932     NULL,                          /* base_destroy */
933     param_spec_generic_class_init, /* class_init */
934     NULL,                          /* class_destroy */
935     NULL,                          /* class_data */
936     0,                             /* instance_size */
937     16,                            /* n_preallocs */
938     NULL,                          /* instance_init */
939   };
940   ParamSpecClassInfo *cinfo;
941
942   g_return_val_if_fail (name != NULL, 0);
943   g_return_val_if_fail (pspec_info != NULL, 0);
944   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
945   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
946   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
947   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
948   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
949   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
950
951   info.instance_size = pspec_info->instance_size;
952   info.n_preallocs = pspec_info->n_preallocs;
953   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
954   cinfo = g_new (ParamSpecClassInfo, 1);
955   cinfo->value_type = pspec_info->value_type;
956   cinfo->finalize = pspec_info->finalize;
957   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
958   cinfo->value_validate = pspec_info->value_validate;
959   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
960   info.class_data = cinfo;
961
962   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
963 }
964
965 void
966 g_value_set_param (GValue     *value,
967                    GParamSpec *param)
968 {
969   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
970   if (param)
971     g_return_if_fail (G_IS_PARAM_SPEC (param));
972
973   if (value->data[0].v_pointer)
974     g_param_spec_unref (value->data[0].v_pointer);
975   value->data[0].v_pointer = param;
976   if (value->data[0].v_pointer)
977     g_param_spec_ref (value->data[0].v_pointer);
978 }
979
980 void
981 g_value_set_param_take_ownership (GValue     *value,
982                                   GParamSpec *param)
983 {
984   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
985   if (param)
986     g_return_if_fail (G_IS_PARAM_SPEC (param));
987
988   if (value->data[0].v_pointer)
989     g_param_spec_unref (value->data[0].v_pointer);
990   value->data[0].v_pointer = param; /* we take over the reference count */
991 }
992
993 GParamSpec*
994 g_value_get_param (const GValue *value)
995 {
996   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
997
998   return value->data[0].v_pointer;
999 }
1000
1001 GParamSpec*
1002 g_value_dup_param (const GValue *value)
1003 {
1004   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
1005
1006   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
1007 }