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