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