removed archaic gpointer derived_data; relict and added a GData member
[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_SPEC_CLASS(class)               (G_TYPE_CHECK_CLASS_CAST ((class), G_TYPE_PARAM, GParamSpecClass))
34 #define G_PARAM_USER_MASK                       (~0 << G_PARAM_USER_SHIFT)
35 #define PSPEC_APPLIES_TO_VALUE(pspec, value)    (G_TYPE_CHECK_VALUE_TYPE ((value), G_PARAM_SPEC_VALUE_TYPE (pspec)))
36 #define G_SLOCK(mutex)                          g_static_mutex_lock (mutex)
37 #define G_SUNLOCK(mutex)                        g_static_mutex_unlock (mutex)
38
39
40 /* --- prototypes --- */
41 static void     g_param_spec_class_base_init     (GParamSpecClass       *class);
42 static void     g_param_spec_class_base_finalize (GParamSpecClass       *class);
43 static void     g_param_spec_class_init          (GParamSpecClass       *class,
44                                                   gpointer               class_data);
45 static void     g_param_spec_init                (GParamSpec            *pspec,
46                                                   GParamSpecClass       *class);
47 static void     g_param_spec_finalize            (GParamSpec            *pspec);
48 static void     value_param_init                (GValue         *value);
49 static void     value_param_free_value          (GValue         *value);
50 static void     value_param_copy_value          (const GValue   *src_value,
51                                                  GValue         *dest_value);
52 static void     value_param_transform_value     (const GValue   *src_value,
53                                                  GValue         *dest_value);
54 static gpointer value_param_peek_pointer        (const GValue   *value);
55 static gchar*   value_param_collect_value       (GValue         *value,
56                                                  guint           n_collect_values,
57                                                  GTypeCValue    *collect_values,
58                                                  guint           collect_flags);
59 static gchar*   value_param_lcopy_value         (const GValue   *value,
60                                                  guint           n_collect_values,
61                                                  GTypeCValue    *collect_values,
62                                                  guint           collect_flags);
63
64
65 /* --- variables --- */
66 static GQuark quark_floating = 0;
67 G_LOCK_DEFINE_STATIC (pspec_ref_count);
68
69
70 /* --- functions --- */
71 void
72 g_param_type_init (void)        /* sync with gtype.c */
73 {
74   static const GTypeFundamentalInfo finfo = {
75     (G_TYPE_FLAG_CLASSED |
76      G_TYPE_FLAG_INSTANTIATABLE |
77      G_TYPE_FLAG_DERIVABLE |
78      G_TYPE_FLAG_DEEP_DERIVABLE),
79   };
80   static const GTypeValueTable param_value_table = {
81     value_param_init,           /* value_init */
82     value_param_free_value,     /* value_free */
83     value_param_copy_value,     /* value_copy */
84     value_param_peek_pointer,   /* value_peek_pointer */
85     "p",                        /* collect_format */
86     value_param_collect_value,  /* collect_value */
87     "p",                        /* lcopy_format */
88     value_param_lcopy_value,    /* lcopy_value */
89   };
90   static const GTypeInfo param_spec_info = {
91     sizeof (GParamSpecClass),
92
93     (GBaseInitFunc) g_param_spec_class_base_init,
94     (GBaseFinalizeFunc) g_param_spec_class_base_finalize,
95     (GClassInitFunc) g_param_spec_class_init,
96     (GClassFinalizeFunc) NULL,
97     NULL,       /* class_data */
98
99     sizeof (GParamSpec),
100     0,          /* n_preallocs */
101     (GInstanceInitFunc) g_param_spec_init,
102
103     &param_value_table,
104   };
105   GType type;
106
107   type = g_type_register_fundamental (G_TYPE_PARAM, "GParam", &param_spec_info, &finfo, G_TYPE_FLAG_ABSTRACT);
108   g_assert (type == G_TYPE_PARAM);
109   g_value_register_transform_func (G_TYPE_PARAM, G_TYPE_PARAM, value_param_transform_value);
110 }
111
112 static void
113 g_param_spec_class_base_init (GParamSpecClass *class)
114 {
115 }
116
117 static void
118 g_param_spec_class_base_finalize (GParamSpecClass *class)
119 {
120 }
121
122 static void
123 g_param_spec_class_init (GParamSpecClass *class,
124                          gpointer         class_data)
125 {
126   quark_floating = g_quark_from_static_string ("GParamSpec-floating");
127
128   class->value_type = G_TYPE_NONE;
129   class->finalize = g_param_spec_finalize;
130   class->value_set_default = NULL;
131   class->value_validate = NULL;
132   class->values_cmp = NULL;
133 }
134
135 static void
136 g_param_spec_init (GParamSpec      *pspec,
137                    GParamSpecClass *class)
138 {
139   pspec->name = NULL;
140   pspec->nick = NULL;
141   pspec->blurb = NULL;
142   pspec->flags = 0;
143   pspec->value_type = class->value_type;
144   pspec->owner_type = 0;
145   pspec->qdata = NULL;
146   pspec->ref_count = 1;
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 gpointer
237 g_param_spec_internal (GType        param_type,
238                        const gchar *name,
239                        const gchar *nick,
240                        const gchar *blurb,
241                        GParamFlags  flags)
242 {
243   GParamSpec *pspec;
244
245   g_return_val_if_fail (G_TYPE_IS_PARAM (param_type) && param_type != G_TYPE_PARAM, NULL);
246   g_return_val_if_fail (name != NULL, NULL);
247   g_return_val_if_fail ((name[0] >= 'A' && name[0] <= 'Z') || (name[0] >= 'a' && name[0] <= 'z'), NULL);
248
249   pspec = (gpointer) g_type_create_instance (param_type);
250   pspec->name = g_strdup (name);
251   g_strcanon (pspec->name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
252   pspec->nick = g_strdup (nick ? nick : pspec->name);
253   pspec->blurb = g_strdup (blurb);
254   pspec->flags = (flags & G_PARAM_USER_MASK) | (flags & G_PARAM_MASK);
255
256   return pspec;
257 }
258
259 gpointer
260 g_param_spec_get_qdata (GParamSpec *pspec,
261                         GQuark      quark)
262 {
263   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
264   
265   return quark ? g_datalist_id_get_data (&pspec->qdata, quark) : NULL;
266 }
267
268 void
269 g_param_spec_set_qdata (GParamSpec *pspec,
270                         GQuark      quark,
271                         gpointer    data)
272 {
273   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
274   g_return_if_fail (quark > 0);
275
276   g_datalist_id_set_data (&pspec->qdata, quark, data);
277 }
278
279 void
280 g_param_spec_set_qdata_full (GParamSpec    *pspec,
281                              GQuark         quark,
282                              gpointer       data,
283                              GDestroyNotify destroy)
284 {
285   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
286   g_return_if_fail (quark > 0);
287
288   g_datalist_id_set_data_full (&pspec->qdata, quark, data, data ? destroy : (GDestroyNotify) NULL);
289 }
290
291 gpointer
292 g_param_spec_steal_qdata (GParamSpec *pspec,
293                           GQuark      quark)
294 {
295   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), NULL);
296   g_return_val_if_fail (quark > 0, NULL);
297   
298   return g_datalist_id_remove_no_notify (&pspec->qdata, quark);
299 }
300
301 void
302 g_param_value_set_default (GParamSpec *pspec,
303                            GValue     *value)
304 {
305   g_return_if_fail (G_IS_PARAM_SPEC (pspec));
306   g_return_if_fail (G_IS_VALUE (value));
307   g_return_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value));
308
309   g_value_reset (value);
310   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, value);
311 }
312
313 gboolean
314 g_param_value_defaults (GParamSpec *pspec,
315                         GValue     *value)
316 {
317   GValue dflt_value = { 0, };
318   gboolean defaults;
319
320   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
321   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
322   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
323
324   g_value_init (&dflt_value, G_PARAM_SPEC_VALUE_TYPE (pspec));
325   G_PARAM_SPEC_GET_CLASS (pspec)->value_set_default (pspec, &dflt_value);
326   defaults = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value, &dflt_value) == 0;
327   g_value_unset (&dflt_value);
328
329   return defaults;
330 }
331
332 gboolean
333 g_param_value_validate (GParamSpec *pspec,
334                         GValue     *value)
335 {
336   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
337   g_return_val_if_fail (G_IS_VALUE (value), FALSE);
338   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value), FALSE);
339
340   if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate)
341     {
342       GValue oval = *value;
343
344       if (G_PARAM_SPEC_GET_CLASS (pspec)->value_validate (pspec, value) ||
345           memcmp (&oval.data, &value->data, sizeof (oval.data)))
346         return TRUE;
347     }
348
349   return FALSE;
350 }
351
352 gboolean
353 g_param_value_convert (GParamSpec   *pspec,
354                        const GValue *src_value,
355                        GValue       *dest_value,
356                        gboolean      strict_validation)
357 {
358   GValue tmp_value = { 0, };
359
360   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE);
361   g_return_val_if_fail (G_IS_VALUE (src_value), FALSE);
362   g_return_val_if_fail (G_IS_VALUE (dest_value), FALSE);
363   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, dest_value), FALSE);
364
365   /* better leave dest_value untouched when returning FALSE */
366
367   g_value_init (&tmp_value, G_VALUE_TYPE (dest_value));
368   if (g_value_transform (src_value, &tmp_value) &&
369       (!g_param_value_validate (pspec, &tmp_value) || !strict_validation))
370     {
371       g_value_unset (dest_value);
372       
373       /* values are relocatable */
374       memcpy (dest_value, &tmp_value, sizeof (tmp_value));
375       
376       return TRUE;
377     }
378   else
379     {
380       g_value_unset (&tmp_value);
381       
382       return FALSE;
383     }
384 }
385
386 gint
387 g_param_values_cmp (GParamSpec   *pspec,
388                     const GValue *value1,
389                     const GValue *value2)
390 {
391   gint cmp;
392
393   /* param_values_cmp() effectively does: value1 - value2
394    * so the return values are:
395    * -1)  value1 < value2
396    *  0)  value1 == value2
397    *  1)  value1 > value2
398    */
399   g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), 0);
400   g_return_val_if_fail (G_IS_VALUE (value1), 0);
401   g_return_val_if_fail (G_IS_VALUE (value2), 0);
402   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value1), 0);
403   g_return_val_if_fail (PSPEC_APPLIES_TO_VALUE (pspec, value2), 0);
404
405   cmp = G_PARAM_SPEC_GET_CLASS (pspec)->values_cmp (pspec, value1, value2);
406
407   return CLAMP (cmp, -1, 1);
408 }
409
410 static void
411 value_param_init (GValue *value)
412 {
413   value->data[0].v_pointer = NULL;
414 }
415
416 static void
417 value_param_free_value (GValue *value)
418 {
419   if (value->data[0].v_pointer)
420     g_param_spec_unref (value->data[0].v_pointer);
421 }
422
423 static void
424 value_param_copy_value (const GValue *src_value,
425                         GValue       *dest_value)
426 {
427   if (src_value->data[0].v_pointer)
428     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
429   else
430     dest_value->data[0].v_pointer = NULL;
431 }
432
433 static void
434 value_param_transform_value (const GValue *src_value,
435                              GValue       *dest_value)
436 {
437   if (src_value->data[0].v_pointer &&
438       g_type_is_a (G_PARAM_SPEC_TYPE (dest_value->data[0].v_pointer), G_VALUE_TYPE (dest_value)))
439     dest_value->data[0].v_pointer = g_param_spec_ref (src_value->data[0].v_pointer);
440   else
441     dest_value->data[0].v_pointer = NULL;
442 }
443
444 static gpointer
445 value_param_peek_pointer (const GValue *value)
446 {
447   return value->data[0].v_pointer;
448 }
449
450 static gchar*
451 value_param_collect_value (GValue      *value,
452                            guint        n_collect_values,
453                            GTypeCValue *collect_values,
454                            guint        collect_flags)
455 {
456   if (collect_values[0].v_pointer)
457     {
458       GParamSpec *param = collect_values[0].v_pointer;
459
460       if (param->g_type_instance.g_class == NULL)
461         return g_strconcat ("invalid unclassed param spec pointer for value type `",
462                             G_VALUE_TYPE_NAME (value),
463                             "'",
464                             NULL);
465       else if (!g_value_type_compatible (G_PARAM_SPEC_TYPE (param), G_VALUE_TYPE (value)))
466         return g_strconcat ("invalid param spec type `",
467                             G_PARAM_SPEC_TYPE_NAME (param),
468                             "' for value type `",
469                             G_VALUE_TYPE_NAME (value),
470                             "'",
471                             NULL);
472       value->data[0].v_pointer = g_param_spec_ref (param);
473     }
474   else
475     value->data[0].v_pointer = NULL;
476
477   return NULL;
478 }
479
480 static gchar*
481 value_param_lcopy_value (const GValue *value,
482                          guint         n_collect_values,
483                          GTypeCValue  *collect_values,
484                          guint         collect_flags)
485 {
486   GParamSpec **param_p = collect_values[0].v_pointer;
487
488   if (!param_p)
489     return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
490
491   if (!value->data[0].v_pointer)
492     *param_p = NULL;
493   else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
494     *param_p = value->data[0].v_pointer;
495   else
496     *param_p = g_param_spec_ref (value->data[0].v_pointer);
497
498   return NULL;
499 }
500
501
502 /* --- param spec pool --- */
503 struct _GParamSpecPool
504 {
505   GStaticMutex smutex;
506   gboolean     type_prefixing;
507   GHashTable  *hash_table;
508 };
509
510 static guint
511 param_spec_pool_hash (gconstpointer key_spec)
512 {
513   const GParamSpec *key = key_spec;
514   const gchar *p;
515   guint h = key->owner_type;
516
517   for (p = key->name; *p; p++)
518     h = (h << 5) - h + *p;
519
520   return h;
521 }
522
523 static gboolean
524 param_spec_pool_equals (gconstpointer key_spec_1,
525                         gconstpointer key_spec_2)
526 {
527   const GParamSpec *key1 = key_spec_1;
528   const GParamSpec *key2 = key_spec_2;
529
530   return (key1->owner_type == key2->owner_type &&
531           strcmp (key1->name, key2->name) == 0);
532 }
533
534 GParamSpecPool*
535 g_param_spec_pool_new (gboolean type_prefixing)
536 {
537   static GStaticMutex init_smutex = G_STATIC_MUTEX_INIT;
538   GParamSpecPool *pool = g_new (GParamSpecPool, 1);
539
540   memcpy (&pool->smutex, &init_smutex, sizeof (init_smutex));
541   pool->type_prefixing = type_prefixing != FALSE;
542   pool->hash_table = g_hash_table_new (param_spec_pool_hash, param_spec_pool_equals);
543
544   return pool;
545 }
546
547 void
548 g_param_spec_pool_insert (GParamSpecPool *pool,
549                           GParamSpec     *pspec,
550                           GType           owner_type)
551 {
552   gchar *p;
553   
554   if (pool && pspec && owner_type > 0 && pspec->owner_type == 0)
555     {
556       G_SLOCK (&pool->smutex);
557       for (p = pspec->name; *p; p++)
558         {
559           if (!strchr (G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-_", *p))
560             {
561               g_warning (G_STRLOC ": pspec name \"%s\" contains invalid characters", pspec->name);
562               G_SUNLOCK (&pool->smutex);
563               return;
564             }
565         }
566       
567       pspec->owner_type = owner_type;
568       g_param_spec_ref (pspec);
569       g_hash_table_insert (pool->hash_table, pspec, pspec);
570       G_SUNLOCK (&pool->smutex);
571     }
572   else
573     {
574       g_return_if_fail (pool != NULL);
575       g_return_if_fail (pspec);
576       g_return_if_fail (owner_type > 0);
577       g_return_if_fail (pspec->owner_type == 0);
578     }
579 }
580
581 void
582 g_param_spec_pool_remove (GParamSpecPool *pool,
583                           GParamSpec     *pspec)
584 {
585   if (pool && pspec)
586     {
587       G_SLOCK (&pool->smutex);
588       if (g_hash_table_remove (pool->hash_table, pspec))
589         g_param_spec_unref (pspec);
590       else
591         g_warning (G_STRLOC ": attempt to remove unknown pspec `%s' from pool", pspec->name);
592       G_SUNLOCK (&pool->smutex);
593     }
594   else
595     {
596       g_return_if_fail (pool != NULL);
597       g_return_if_fail (pspec);
598     }
599 }
600
601 static inline GParamSpec*
602 param_spec_ht_lookup (GHashTable  *hash_table,
603                       const gchar *param_name,
604                       GType        owner_type,
605                       gboolean     walk_ancestors)
606 {
607   GParamSpec key, *pspec;
608
609   key.owner_type = owner_type;
610   key.name = (gchar*) param_name;
611   if (walk_ancestors)
612     do
613       {
614         pspec = g_hash_table_lookup (hash_table, &key);
615         if (pspec)
616           return pspec;
617         key.owner_type = g_type_parent (key.owner_type);
618       }
619     while (key.owner_type);
620   else
621     pspec = g_hash_table_lookup (hash_table, &key);
622
623   if (!pspec)
624     {
625       /* try canonicalized form */
626       key.name = g_strdup (param_name);
627       key.owner_type = owner_type;
628       
629       g_strcanon (key.name, G_CSET_A_2_Z G_CSET_a_2_z G_CSET_DIGITS "-", '-');
630       if (walk_ancestors)
631         do
632           {
633             pspec = g_hash_table_lookup (hash_table, &key);
634             if (pspec)
635               {
636                 g_free (key.name);
637                 return pspec;
638               }
639             key.owner_type = g_type_parent (key.owner_type);
640           }
641         while (key.owner_type);
642       else
643         pspec = g_hash_table_lookup (hash_table, &key);
644       g_free (key.name);
645     }
646
647   return pspec;
648 }
649
650 GParamSpec*
651 g_param_spec_pool_lookup (GParamSpecPool *pool,
652                           const gchar    *param_name,
653                           GType           owner_type,
654                           gboolean        walk_ancestors)
655 {
656   GParamSpec *pspec;
657   gchar *delim;
658
659   if (!pool || !param_name)
660     {
661       g_return_val_if_fail (pool != NULL, NULL);
662       g_return_val_if_fail (param_name != NULL, NULL);
663     }
664
665   G_SLOCK (&pool->smutex);
666
667   delim = pool->type_prefixing ? strchr (param_name, ':') : NULL;
668
669   /* try quick and away, i.e. without prefix */
670   if (!delim)
671     {
672       pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
673       G_SUNLOCK (&pool->smutex);
674
675       return pspec;
676     }
677
678   /* strip type prefix */
679   if (pool->type_prefixing && delim[1] == ':')
680     {
681       guint l = delim - param_name;
682       gchar stack_buffer[32], *buffer = l < 32 ? stack_buffer : g_new (gchar, l + 1);
683       GType type;
684       
685       strncpy (buffer, param_name, delim - param_name);
686       buffer[l] = 0;
687       type = g_type_from_name (buffer);
688       if (l >= 32)
689         g_free (buffer);
690       if (type)         /* type==0 isn't a valid type pefix */
691         {
692           /* sanity check, these cases don't make a whole lot of sense */
693           if ((!walk_ancestors && type != owner_type) || !g_type_is_a (owner_type, type))
694             {
695               G_SUNLOCK (&pool->smutex);
696
697               return NULL;
698             }
699           owner_type = type;
700           param_name += l + 2;
701           pspec = param_spec_ht_lookup (pool->hash_table, param_name, owner_type, walk_ancestors);
702           G_SUNLOCK (&pool->smutex);
703
704           return pspec;
705         }
706     }
707   /* malformed param_name */
708
709   G_SUNLOCK (&pool->smutex);
710
711   return NULL;
712 }
713
714
715 /* --- auxillary functions --- */
716 typedef struct
717 {
718   /* class portion */
719   GType           value_type;
720   void          (*finalize)             (GParamSpec   *pspec);
721   void          (*value_set_default)    (GParamSpec   *pspec,
722                                          GValue       *value);
723   gboolean      (*value_validate)       (GParamSpec   *pspec,
724                                          GValue       *value);
725   gint          (*values_cmp)           (GParamSpec   *pspec,
726                                          const GValue *value1,
727                                          const GValue *value2);
728 } ParamSpecClassInfo;
729
730 static void
731 param_spec_generic_class_init (gpointer g_class,
732                                gpointer class_data)
733 {
734   GParamSpecClass *class = g_class;
735   ParamSpecClassInfo *info = class_data;
736
737   class->value_type = info->value_type;
738   if (info->finalize)
739     class->finalize = info->finalize;                   /* optional */
740   class->value_set_default = info->value_set_default;
741   if (info->value_validate)
742     class->value_validate = info->value_validate;       /* optional */
743   class->values_cmp = info->values_cmp;
744   g_free (class_data);
745 }
746
747 static void
748 default_value_set_default (GParamSpec *pspec,
749                            GValue     *value)
750 {
751   /* value is already zero initialized */
752 }
753
754 static gint
755 default_values_cmp (GParamSpec   *pspec,
756                     const GValue *value1,
757                     const GValue *value2)
758 {
759   return memcmp (&value1->data, &value2->data, sizeof (value1->data));
760 }
761
762 GType
763 g_param_type_register_static (const gchar              *name,
764                               const GParamSpecTypeInfo *pspec_info)
765 {
766   GTypeInfo info = {
767     sizeof (GParamSpecClass),      /* class_size */
768     NULL,                          /* base_init */
769     NULL,                          /* base_destroy */
770     param_spec_generic_class_init, /* class_init */
771     NULL,                          /* class_destroy */
772     NULL,                          /* class_data */
773     0,                             /* instance_size */
774     16,                            /* n_preallocs */
775     NULL,                          /* instance_init */
776   };
777   ParamSpecClassInfo *cinfo;
778
779   g_return_val_if_fail (name != NULL, 0);
780   g_return_val_if_fail (pspec_info != NULL, 0);
781   g_return_val_if_fail (g_type_from_name (name) == 0, 0);
782   g_return_val_if_fail (pspec_info->instance_size >= sizeof (GParamSpec), 0);
783   g_return_val_if_fail (g_type_name (pspec_info->value_type) != NULL, 0);
784   /* default: g_return_val_if_fail (pspec_info->value_set_default != NULL, 0); */
785   /* optional: g_return_val_if_fail (pspec_info->value_validate != NULL, 0); */
786   /* default: g_return_val_if_fail (pspec_info->values_cmp != NULL, 0); */
787
788   info.instance_size = pspec_info->instance_size;
789   info.n_preallocs = pspec_info->n_preallocs;
790   info.instance_init = (GInstanceInitFunc) pspec_info->instance_init;
791   cinfo = g_new (ParamSpecClassInfo, 1);
792   cinfo->value_type = pspec_info->value_type;
793   cinfo->finalize = pspec_info->finalize;
794   cinfo->value_set_default = pspec_info->value_set_default ? pspec_info->value_set_default : default_value_set_default;
795   cinfo->value_validate = pspec_info->value_validate;
796   cinfo->values_cmp = pspec_info->values_cmp ? pspec_info->values_cmp : default_values_cmp;
797   info.class_data = cinfo;
798
799   return g_type_register_static (G_TYPE_PARAM, name, &info, 0);
800 }
801
802 void
803 g_value_set_param (GValue     *value,
804                    GParamSpec *param)
805 {
806   g_return_if_fail (G_VALUE_HOLDS_PARAM (value));
807   if (param)
808     g_return_if_fail (G_IS_PARAM_SPEC (param));
809
810   if (value->data[0].v_pointer)
811     g_param_spec_unref (value->data[0].v_pointer);
812   value->data[0].v_pointer = param;
813   if (value->data[0].v_pointer)
814     g_param_spec_ref (value->data[0].v_pointer);
815 }
816
817 GParamSpec*
818 g_value_get_param (const GValue *value)
819 {
820   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
821
822   return value->data[0].v_pointer;
823 }
824
825 GParamSpec*
826 g_value_dup_param (const GValue *value)
827 {
828   g_return_val_if_fail (G_VALUE_HOLDS_PARAM (value), NULL);
829
830   return value->data[0].v_pointer ? g_param_spec_ref (value->data[0].v_pointer) : NULL;
831 }