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