1 /* -*- mode: C; c-file-style: "gnu" -*- */
2 /* dbus-gvalue-utils.c: Non-DBus-specific functions related to GType/GValue
3  *
4  * Copyright (C) 2005 Red Hat, Inc.
5  *
6  * SPDX-License-Identifier: AFL-2.1 OR GPL-2.0-or-later
7  *
8  * Licensed under the Academic Free License version 2.1
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
23  *
24  */
25 
26 #include <config.h>
27 #include "dbus/dbus-glib.h"
28 #include "dbus-gtype-specialized-priv.h"
29 #include "dbus-gvalue-utils.h"
30 #include "dbus-gtest.h"
31 #include <glib.h>
32 #include <string.h>
33 #include <gobject/gvaluecollector.h>
34 
35 
36 static guint
fixed_type_get_size(GType type)37 fixed_type_get_size (GType type)
38 {
39   switch (type)
40     {
41     case G_TYPE_CHAR:
42     case G_TYPE_UCHAR:
43       return sizeof (gchar);
44     case G_TYPE_BOOLEAN:
45       return sizeof (gboolean);
46     case G_TYPE_LONG:
47     case G_TYPE_ULONG:
48       return sizeof (glong);
49     case G_TYPE_INT:
50     case G_TYPE_UINT:
51       return sizeof (gint);
52     case G_TYPE_INT64:
53     case G_TYPE_UINT64:
54       return sizeof (gint64);
55     case G_TYPE_FLOAT:
56       return sizeof (gfloat);
57     case G_TYPE_DOUBLE:
58       return sizeof (gdouble);
59     default:
60       return 0;
61     }
62 }
63 
64 gboolean
_dbus_g_type_is_fixed(GType type)65 _dbus_g_type_is_fixed (GType type)
66 {
67   return fixed_type_get_size (type) > 0;
68 }
69 
70 guint
_dbus_g_type_fixed_get_size(GType type)71 _dbus_g_type_fixed_get_size (GType type)
72 {
73   g_assert (_dbus_g_type_is_fixed (type));
74   return fixed_type_get_size (type);
75 }
76 
77 gboolean
_dbus_gvalue_store(GValue * value,gpointer storage)78 _dbus_gvalue_store (GValue          *value,
79 		   gpointer        storage)
80 {
81   /* FIXME - can we use the GValue lcopy_value method
82    * to do this in a cleaner way?
83    */
84   switch (g_type_fundamental (G_VALUE_TYPE (value)))
85     {
86     case G_TYPE_CHAR:
87       *((gchar *) storage) = g_value_get_char (value);
88       return TRUE;
89     case G_TYPE_UCHAR:
90       *((guchar *) storage) = g_value_get_uchar (value);
91       return TRUE;
92     case G_TYPE_BOOLEAN:
93       *((gboolean *) storage) = g_value_get_boolean (value);
94       return TRUE;
95     case G_TYPE_LONG:
96       *((glong *) storage) = g_value_get_long (value);
97       return TRUE;
98     case G_TYPE_ULONG:
99       *((gulong *) storage) = g_value_get_ulong (value);
100       return TRUE;
101     case G_TYPE_INT:
102       *((gint *) storage) = g_value_get_int (value);
103       return TRUE;
104     case G_TYPE_UINT:
105       *((guint *) storage) = g_value_get_uint (value);
106       return TRUE;
107     case G_TYPE_INT64:
108       *((gint64 *) storage) = g_value_get_int64 (value);
109       return TRUE;
110     case G_TYPE_UINT64:
111       *((guint64 *) storage) = g_value_get_uint64 (value);
112       return TRUE;
113     case G_TYPE_DOUBLE:
114       *((gdouble *) storage) = g_value_get_double (value);
115       return TRUE;
116     case G_TYPE_STRING:
117       *((gchar **) storage) = (char*) g_value_get_string (value);
118       return TRUE;
119     case G_TYPE_OBJECT:
120       *((gpointer *) storage) = g_value_get_object (value);
121       return TRUE;
122     case G_TYPE_BOXED:
123       *((gpointer *) storage) = g_value_get_boxed (value);
124       return TRUE;
125     default:
126       return FALSE;
127     }
128 }
129 
130 gboolean
_dbus_gvalue_set_from_pointer(GValue * value,gconstpointer storage)131 _dbus_gvalue_set_from_pointer (GValue          *value,
132 			      gconstpointer    storage)
133 {
134   /* FIXME - is there a better way to do this? */
135   switch (g_type_fundamental (G_VALUE_TYPE (value)))
136     {
137     case G_TYPE_CHAR:
138       g_value_set_char (value, *((gchar *) storage));
139       return TRUE;
140     case G_TYPE_UCHAR:
141       g_value_set_uchar (value, *((guchar *) storage));
142       return TRUE;
143     case G_TYPE_BOOLEAN:
144       g_value_set_boolean (value, *((gboolean *) storage));
145       return TRUE;
146     case G_TYPE_LONG:
147       g_value_set_long (value, *((glong *) storage));
148       return TRUE;
149     case G_TYPE_ULONG:
150       g_value_set_ulong (value, *((gulong *) storage));
151       return TRUE;
152     case G_TYPE_INT:
153       g_value_set_int (value, *((gint *) storage));
154       return TRUE;
155     case G_TYPE_UINT:
156       g_value_set_uint (value, *((guint *) storage));
157       return TRUE;
158     case G_TYPE_INT64:
159       g_value_set_int64 (value, *((gint64 *) storage));
160       return TRUE;
161     case G_TYPE_UINT64:
162       g_value_set_uint64 (value, *((guint64 *) storage));
163       return TRUE;
164     case G_TYPE_DOUBLE:
165       g_value_set_double (value, *((gdouble *) storage));
166       return TRUE;
167     case G_TYPE_STRING:
168       g_value_set_string (value, *((gchar **) storage));
169       return TRUE;
170     case G_TYPE_OBJECT:
171       g_value_set_object (value, *((gpointer *) storage));
172       return TRUE;
173     case G_TYPE_BOXED:
174       g_value_set_boxed (value, *((gpointer *) storage));
175       return TRUE;
176     default:
177       return FALSE;
178     }
179 }
180 
181 gboolean
_dbus_gvalue_take(GValue * value,GTypeCValue * cvalue)182 _dbus_gvalue_take (GValue          *value,
183 		  GTypeCValue     *cvalue)
184 {
185   GType g_type;
186   GTypeValueTable *value_table;
187   char *error_msg;
188 
189   g_type = G_VALUE_TYPE (value);
190   value_table = g_type_value_table_peek (g_type);
191 
192   error_msg = value_table->collect_value (value, 1, cvalue, G_VALUE_NOCOPY_CONTENTS);
193   if (error_msg)
194     {
195       g_warning ("%s: %s", G_STRLOC, error_msg);
196       g_free (error_msg);
197       return FALSE;
198     }
199   /* Clear the NOCOPY_CONTENTS flag; we want to take ownership
200    * of the value.
201    */
202   value->data[1].v_uint &= ~(G_VALUE_NOCOPY_CONTENTS);
203   return TRUE;
204 }
205 
206 gboolean
_dbus_gtype_can_signal_error(GType gtype)207 _dbus_gtype_can_signal_error (GType gtype)
208 {
209   switch (gtype)
210     {
211     case G_TYPE_BOOLEAN:
212     case G_TYPE_INT:
213     case G_TYPE_UINT:
214     case G_TYPE_STRING:
215     case G_TYPE_BOXED:
216     case G_TYPE_OBJECT:
217       return TRUE;
218     default:
219       return FALSE;
220     }
221 }
222 
223 gboolean
_dbus_gvalue_signals_error(const GValue * value)224 _dbus_gvalue_signals_error (const GValue *value)
225 {
226   /* Hardcoded rules for return value semantics for certain
227    * types.  Perhaps in the future we'd want an annotation
228    * specifying which return values are errors, but in
229    * reality people will probably just use boolean and
230    * boxed, and there the semantics are pretty standard.
231    */
232   switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value)))
233     {
234     case G_TYPE_BOOLEAN:
235       return (g_value_get_boolean (value) == FALSE);
236       break;
237     case G_TYPE_INT:
238       return (g_value_get_int (value) < 0);
239       break;
240     case G_TYPE_UINT:
241       return (g_value_get_uint (value) == 0);
242       break;
243     case G_TYPE_STRING:
244       return (g_value_get_string (value) == NULL);
245       break;
246     case G_TYPE_BOXED:
247       return (g_value_get_boxed (value) == NULL);
248       break;
249     case G_TYPE_OBJECT:
250       return (g_value_get_object (value) == NULL);
251       break;
252     default:
253       g_assert_not_reached ();
254       return FALSE;
255     }
256 }
257 
258 
259 static gboolean
hash_func_from_gtype(GType gtype,GHashFunc * func)260 hash_func_from_gtype (GType gtype, GHashFunc *func)
261 {
262   switch (gtype)
263     {
264     case G_TYPE_CHAR:
265     case G_TYPE_UCHAR:
266     case G_TYPE_BOOLEAN:
267     case G_TYPE_INT:
268     case G_TYPE_UINT:
269       *func = NULL;
270       return TRUE;
271     case G_TYPE_STRING:
272       *func = g_str_hash;
273       return TRUE;
274     default:
275       if (gtype == DBUS_TYPE_G_OBJECT_PATH)
276         {
277           *func = g_str_hash;
278           return TRUE;
279         }
280       else if (gtype == DBUS_TYPE_G_SIGNATURE)
281         {
282           *func = g_str_hash;
283           return TRUE;
284         }
285       return FALSE;
286     }
287 }
288 
289 static void
unset_and_free_g_value(gpointer val)290 unset_and_free_g_value (gpointer val)
291 {
292   GValue *value = val;
293 
294   g_value_unset (value);
295   g_free (value);
296 }
297 
298 static gboolean
299 gtype_can_simple_free (GType type);
300 
301 static gboolean
hash_simple_free_from_gtype(GType gtype,GDestroyNotify * func)302 hash_simple_free_from_gtype (GType gtype, GDestroyNotify *func)
303 {
304   switch (gtype)
305     {
306     case G_TYPE_CHAR:
307     case G_TYPE_UCHAR:
308     case G_TYPE_BOOLEAN:
309     case G_TYPE_INT:
310     case G_TYPE_UINT:
311       *func = NULL;
312       return TRUE;
313     case G_TYPE_DOUBLE:
314     case G_TYPE_STRING:
315       *func = g_free;
316       return TRUE;
317     default:
318       if (gtype == G_TYPE_VALUE)
319 	{
320 	  *func = unset_and_free_g_value;
321 	  return TRUE;
322 	}
323       else if (gtype == G_TYPE_VALUE_ARRAY)
324         {
325           *func = (GDestroyNotify) g_value_array_free;
326           return TRUE;
327         }
328       else if (gtype == G_TYPE_STRV)
329         {
330           *func = (GDestroyNotify) g_strfreev;
331           return TRUE;
332         }
333       else if (gtype == DBUS_TYPE_G_OBJECT_PATH)
334         {
335           *func = g_free;
336           return TRUE;
337         }
338       else if (gtype == DBUS_TYPE_G_SIGNATURE)
339         {
340           *func = g_free;
341           return TRUE;
342         }
343       else if (dbus_g_type_is_collection (gtype))
344         {
345           const DBusGTypeSpecializedCollectionVtable* vtable;
346           vtable = dbus_g_type_collection_peek_vtable (gtype);
347           if (vtable->base_vtable.simple_free_func)
348             {
349               *func = vtable->base_vtable.simple_free_func;
350               return TRUE;
351             }
352         }
353       else if (dbus_g_type_is_map (gtype))
354         {
355           const DBusGTypeSpecializedMapVtable* vtable;
356           GType key_gtype, value_gtype;
357 
358           key_gtype = dbus_g_type_get_map_key_specialization (gtype);
359           value_gtype = dbus_g_type_get_map_value_specialization (gtype);
360 
361           /* if either the key or the value don't have "simple" (without a
362            * GType) free functions, then the hashtable's contents must be freed
363            * with hashtable_free, so the hashtable itself can't have a simple
364            * free function. */
365           if (!gtype_can_simple_free (key_gtype) ||
366               !gtype_can_simple_free (value_gtype))
367             return FALSE;
368 
369           vtable = dbus_g_type_map_peek_vtable (gtype);
370           if (vtable->base_vtable.simple_free_func)
371             {
372               *func = vtable->base_vtable.simple_free_func;
373               return TRUE;
374             }
375         }
376       else if (dbus_g_type_is_struct (gtype))
377         {
378           const DBusGTypeSpecializedStructVtable *vtable;
379           vtable = dbus_g_type_struct_peek_vtable (gtype);
380           if (vtable->base_vtable.simple_free_func)
381             {
382               *func = vtable->base_vtable.simple_free_func;
383               return TRUE;
384             }
385         }
386       return FALSE;
387     }
388 }
389 
390 static gboolean
gtype_can_simple_free(GType type)391 gtype_can_simple_free (GType type)
392 {
393   GDestroyNotify func;
394   return hash_simple_free_from_gtype (type, &func);
395 }
396 
397 gboolean
_dbus_gtype_is_valid_hash_key(GType type)398 _dbus_gtype_is_valid_hash_key (GType type)
399 {
400   GHashFunc func;
401   return hash_func_from_gtype (type, &func);
402 }
403 
404 gboolean
_dbus_gtype_is_valid_hash_value(GType gtype)405 _dbus_gtype_is_valid_hash_value (GType gtype)
406 {
407   /* anything we can take into a GValue using gvalue_take_hash_value is fine */
408   switch (g_type_fundamental (gtype))
409     {
410     case G_TYPE_CHAR:
411     case G_TYPE_UCHAR:
412     case G_TYPE_BOOLEAN:
413     case G_TYPE_INT:
414     case G_TYPE_UINT:
415     case G_TYPE_DOUBLE:
416     case G_TYPE_STRING:
417     case G_TYPE_BOXED:
418     case G_TYPE_OBJECT:
419       return TRUE;
420     }
421 
422   return FALSE;
423 }
424 
425 GHashFunc
_dbus_g_hash_func_from_gtype(GType gtype)426 _dbus_g_hash_func_from_gtype (GType gtype)
427 {
428   GHashFunc func;
429   gboolean ret;
430   ret = hash_func_from_gtype (gtype, &func);
431   g_assert (ret != FALSE);
432   return func;
433 }
434 
435 GEqualFunc
_dbus_g_hash_equal_from_gtype(GType gtype)436 _dbus_g_hash_equal_from_gtype (GType gtype)
437 {
438   g_assert (_dbus_gtype_is_valid_hash_key (gtype));
439 
440   switch (gtype)
441     {
442     case G_TYPE_CHAR:
443     case G_TYPE_UCHAR:
444     case G_TYPE_BOOLEAN:
445     case G_TYPE_INT:
446     case G_TYPE_UINT:
447       return NULL;
448     case G_TYPE_STRING:
449       return g_str_equal;
450     default:
451       if (gtype == DBUS_TYPE_G_OBJECT_PATH)
452         return g_str_equal;
453       else if (gtype == DBUS_TYPE_G_SIGNATURE)
454         return g_str_equal;
455       g_assert_not_reached ();
456       return NULL;
457     }
458 }
459 
460 static void
hash_fake_simple_free_func(gpointer val)461 hash_fake_simple_free_func (gpointer val)
462 {
463   /* Havoc would be proud... :P */
464   g_critical ("If you see this message then the author of this application or "
465       "one of its libraries has tried to remove or replace the value %p in a "
466       "hash table which was constructed by the D-Bus Glib bindings.\n\n"
467 
468       "However, it was not possible for the bindings to provide a destroy "
469       "function to g_hash_table_new_full which is able to free this value, as "
470       "its GType must be known in order to free it. This means the memory "
471       "allocated to store the value has most likely just been leaked.\n\n"
472 
473       "To avoid this error, the GHashTable (or keys/values \"stolen\" from "
474       "it) should be freed by using g_boxed_free as follows:\n"
475       "  g_boxed_free (dbus_g_type_get_map (\"GHashTable\", key_gtype, "
476       "value_gtype), hash_table);\n", val);
477 }
478 
479 GDestroyNotify
_dbus_g_hash_free_from_gtype(GType gtype)480 _dbus_g_hash_free_from_gtype (GType gtype)
481 {
482   GDestroyNotify func;
483   gboolean ret;
484 
485   ret = hash_simple_free_from_gtype (gtype, &func);
486 
487   /* if the value doesn't have a simple free function, we cannot define a
488    * meaningful free function here. instead, this hash table must be freed
489    * using g_boxed_free so that the hash_free function gets invoked. if the
490    * user does not do so, we provide a fake free function to provide a warning
491    * in this case. */
492   if (ret == FALSE)
493     {
494       g_assert (_dbus_gtype_is_valid_hash_value (gtype));
495 
496       func = hash_fake_simple_free_func;
497     }
498 
499   return func;
500 }
501 
502 static void gvalue_take_ptrarray_value (GValue *value, gpointer instance);
503 
504 static void
gvalue_take_hash_value(GValue * value,gpointer instance)505 gvalue_take_hash_value (GValue *value, gpointer instance)
506 {
507   switch (g_type_fundamental (G_VALUE_TYPE (value)))
508     {
509     case G_TYPE_CHAR:
510       g_value_set_char (value, (gchar) GPOINTER_TO_INT (instance));
511       break;
512     case G_TYPE_UCHAR:
513       g_value_set_uchar (value, (guchar) GPOINTER_TO_UINT (instance));
514       break;
515     case G_TYPE_BOOLEAN:
516       g_value_set_boolean (value, (gboolean) GPOINTER_TO_UINT (instance));
517       break;
518     case G_TYPE_INT:
519       g_value_set_int (value, GPOINTER_TO_INT (instance));
520       break;
521     case G_TYPE_UINT:
522       g_value_set_uint (value, GPOINTER_TO_UINT (instance));
523       break;
524     case G_TYPE_DOUBLE:
525       g_value_set_double (value, *(gdouble *) instance);
526       break;
527     default:
528       gvalue_take_ptrarray_value (value, instance);
529       break;
530     }
531 }
532 
533 static gpointer ptrarray_value_from_gvalue (const GValue *value);
534 
535 static gpointer
hash_value_from_gvalue(GValue * value)536 hash_value_from_gvalue (GValue *value)
537 {
538   switch (g_type_fundamental (G_VALUE_TYPE (value)))
539     {
540     case G_TYPE_CHAR:
541       return GINT_TO_POINTER ((int) g_value_get_char (value));
542       break;
543     case G_TYPE_UCHAR:
544       return GUINT_TO_POINTER ((guint) g_value_get_uchar (value));
545       break;
546     case G_TYPE_BOOLEAN:
547       return GUINT_TO_POINTER ((guint) g_value_get_boolean (value));
548       break;
549     case G_TYPE_INT:
550       return GINT_TO_POINTER (g_value_get_int (value));
551       break;
552     case G_TYPE_UINT:
553       return GUINT_TO_POINTER (g_value_get_uint (value));
554       break;
555     case G_TYPE_DOUBLE:
556       {
557         gdouble *p = (gdouble *) g_malloc0 (sizeof (gdouble));
558         *p = g_value_get_double (value);
559         return (gpointer) p;
560       }
561       break;
562     default:
563       return ptrarray_value_from_gvalue (value);
564       break;
565     }
566 }
567 
568 struct DBusGHashTableValueForeachData
569 {
570   DBusGTypeSpecializedMapIterator func;
571   GType key_type;
572   GType value_type;
573   gpointer data;
574 };
575 
576 static void
hashtable_foreach_with_values(gpointer key,gpointer value,gpointer user_data)577 hashtable_foreach_with_values (gpointer key, gpointer value, gpointer user_data)
578 {
579   GValue key_val = {0, };
580   GValue value_val = {0, };
581   struct DBusGHashTableValueForeachData *data = user_data;
582 
583   g_value_init (&key_val, data->key_type);
584   g_value_init (&value_val, data->value_type);
585   gvalue_take_hash_value (&key_val, key);
586   gvalue_take_hash_value (&value_val, value);
587 
588   data->func (&key_val, &value_val, data->data);
589 }
590 
591 
592 static void
hashtable_iterator(GType hash_type,gpointer instance,DBusGTypeSpecializedMapIterator iterator,gpointer user_data)593 hashtable_iterator (GType                           hash_type,
594 		    gpointer                        instance,
595 		    DBusGTypeSpecializedMapIterator iterator,
596 		    gpointer                        user_data)
597 {
598   struct DBusGHashTableValueForeachData data;
599   GType key_gtype;
600   GType value_gtype;
601 
602   key_gtype = dbus_g_type_get_map_key_specialization (hash_type);
603   value_gtype = dbus_g_type_get_map_value_specialization (hash_type);
604 
605   data.func = iterator;
606   data.key_type = key_gtype;
607   data.value_type = value_gtype;
608   data.data = user_data;
609 
610   g_hash_table_foreach (instance, hashtable_foreach_with_values, &data);
611 }
612 
613 void
_dbus_g_hash_table_insert_steal_values(GHashTable * table,GValue * key_val,GValue * value_val)614 _dbus_g_hash_table_insert_steal_values (GHashTable *table,
615 				       GValue     *key_val,
616 				       GValue     *value_val)
617 {
618   gpointer key, val;
619 
620   key = hash_value_from_gvalue (key_val);
621   val = hash_value_from_gvalue (value_val);
622 
623   g_hash_table_insert (table, key, val);
624 }
625 
626 static void
hashtable_append(DBusGTypeSpecializedAppendContext * ctx,GValue * key,GValue * val)627 hashtable_append (DBusGTypeSpecializedAppendContext *ctx,
628 		  GValue                            *key,
629 		  GValue                            *val)
630 {
631   GHashTable *table;
632 
633   table = g_value_get_boxed (ctx->val);
634   _dbus_g_hash_table_insert_steal_values (table, key, val);
635 }
636 
637 static gpointer
hashtable_constructor(GType type)638 hashtable_constructor (GType type)
639 {
640   GHashTable *ret;
641   GType key_gtype;
642   GType value_gtype;
643 
644   key_gtype = dbus_g_type_get_map_key_specialization (type);
645   value_gtype = dbus_g_type_get_map_value_specialization (type);
646 
647   ret = g_hash_table_new_full (_dbus_g_hash_func_from_gtype (key_gtype),
648 			       _dbus_g_hash_equal_from_gtype (key_gtype),
649 			       _dbus_g_hash_free_from_gtype (key_gtype),
650 			       _dbus_g_hash_free_from_gtype (value_gtype));
651   return ret;
652 }
653 
654 static void
hashtable_insert_values(GHashTable * table,const GValue * key_val,const GValue * value_val)655 hashtable_insert_values (GHashTable       *table,
656 			 const GValue     *key_val,
657 			 const GValue     *value_val)
658 {
659   GValue key_copy = {0, };
660   GValue value_copy = {0, };
661 
662   g_value_init (&key_copy, G_VALUE_TYPE (key_val));
663   g_value_copy (key_val, &key_copy);
664   g_value_init (&value_copy, G_VALUE_TYPE (value_val));
665   g_value_copy (value_val, &value_copy);
666 
667   _dbus_g_hash_table_insert_steal_values (table, &key_copy, &value_copy);
668 }
669 
670 static void
hashtable_foreach_copy(const GValue * key,const GValue * val,gpointer data)671 hashtable_foreach_copy (const GValue *key, const GValue *val, gpointer data)
672 {
673   hashtable_insert_values ((GHashTable *) data, key, val);
674 }
675 
676 static gpointer
hashtable_copy(GType type,gpointer src)677 hashtable_copy (GType type, gpointer src)
678 {
679   GHashTable *ghash;
680   GHashTable *ret;
681   GValue hashval = {0,};
682 
683   ghash = src;
684 
685   ret = hashtable_constructor (type);
686 
687   g_value_init (&hashval, type);
688   g_value_set_static_boxed (&hashval, ghash);
689   dbus_g_type_map_value_iterate (&hashval, hashtable_foreach_copy, ret);
690   return ret;
691 }
692 
693 /* we leave this here for backwards compatibility - any hash tables nested
694  * inside hash tables will use this as their free function if users were
695  * already relying on it, but dbus-glib itself will never use it directly as
696  * hashtable_free is also defined. */
697 static void
hashtable_simple_free(gpointer val)698 hashtable_simple_free (gpointer val)
699 {
700   g_hash_table_unref (val);
701 }
702 
703 struct DBusGHashTableFreeData
704 {
705   GType key_gtype;
706   GType value_gtype;
707 };
708 
709 static gboolean
hashtable_free_foreach_steal(gpointer key,gpointer value,gpointer user_data)710 hashtable_free_foreach_steal (gpointer key,
711                               gpointer value,
712                               gpointer user_data)
713 {
714   struct DBusGHashTableFreeData *data = user_data;
715   GValue val = { 0, };
716 
717   g_value_init (&val, data->key_gtype);
718   gvalue_take_hash_value (&val, key);
719   g_value_unset (&val);
720 
721   g_value_init (&val, data->value_gtype);
722   gvalue_take_hash_value (&val, value);
723   g_value_unset (&val);
724 
725   return TRUE;
726 }
727 
728 static void
hashtable_free(GType type,gpointer val)729 hashtable_free (GType type,
730                 gpointer val)
731 {
732   struct DBusGHashTableFreeData data = { 0, };
733   GHashTable *hash = val;
734 
735   data.key_gtype = dbus_g_type_get_map_key_specialization (type);
736   data.value_gtype = dbus_g_type_get_map_value_specialization (type);
737 
738   /* wheee, fun. two cases here. either:
739    *
740    * a) the keys and value types both have simple (ie, no * GType parameter is
741    * needed to know how to free them) free functions, in which case they were
742    * set as the hash free functions when the hash table was constructed.  in
743    * this case, it's sufficient for us to unref the hash table as before. we
744    * have to keep doing this in order to maintain compatibility with the ABI
745    * which was around before hash tables could contain types which don't have
746    * simple free functions (such as GPtrArrays of other stuff). for these
747    * tables, users were able to ref the hash tables and add/remove values, and
748    * rely on meaningful free functions.
749    *
750    * b) for any other key or value types where /do/ need to know the GType in
751    * order to free it, this function is the only "right" way to free the hash
752    * table. both the key and value free functions were set to print a big nasty
753    * warning, and we free the contents of the hashtable with foreach_steal.
754    */
755   if (gtype_can_simple_free (data.key_gtype) &&
756       gtype_can_simple_free (data.value_gtype))
757     {
758       g_hash_table_unref (hash);
759     }
760   else
761     {
762       g_hash_table_foreach_steal (hash, hashtable_free_foreach_steal, &data);
763       g_hash_table_unref (hash);
764     }
765 }
766 
767 static gpointer
valuearray_constructor(GType type)768 valuearray_constructor (GType type)
769 {
770   GValueArray *ret;
771   guint size = dbus_g_type_get_struct_size (type);
772   guint i;
773   ret = g_value_array_new (size);
774   for (i=0; i < size; i++)
775     {
776       GValue val = {0,};
777       g_value_init (&val, dbus_g_type_get_struct_member_type (type, i));
778       g_value_array_append(ret, &val);
779     }
780   return (gpointer)ret;
781 }
782 
783 static gpointer
valuearray_copy(GType type,gpointer src)784 valuearray_copy (GType type, gpointer src)
785 {
786   return g_value_array_copy ((GValueArray*) src);
787 }
788 
789 static void
valuearray_simple_free(gpointer val)790 valuearray_simple_free (gpointer val)
791 {
792   g_value_array_free (val);
793 }
794 
795 static gboolean
valuearray_get_member(GType type,gpointer instance,guint member,GValue * ret)796 valuearray_get_member (GType type, gpointer instance,
797                        guint member, GValue *ret)
798 {
799   GValueArray *va = (GValueArray*) instance;
800   const GValue *val;
801   if (member < dbus_g_type_get_struct_size (type))
802     {
803       val = g_value_array_get_nth (va, member);
804       g_value_copy (val, ret);
805       return TRUE;
806     }
807   else
808     return FALSE;
809 }
810 
811 static gboolean
valuearray_set_member(GType type,gpointer instance,guint member,const GValue * member_type)812 valuearray_set_member (GType type, gpointer instance,
813                        guint member, const GValue *member_type)
814 {
815   GValueArray *va = (GValueArray*) instance;
816   GValue *vp;
817   if (member < dbus_g_type_get_struct_size (type))
818     {
819       vp = g_value_array_get_nth (va, member);
820       g_value_copy (member_type, vp);
821       return TRUE;
822     }
823   else
824     return FALSE;
825 }
826 
827 
828 static gpointer
array_constructor(GType type)829 array_constructor (GType type)
830 {
831   GArray *array;
832   guint elt_size;
833   GType elt_type;
834   gboolean zero_terminated;
835   gboolean clear;
836 
837   elt_type = dbus_g_type_get_collection_specialization (type);
838   g_assert (elt_type != G_TYPE_INVALID);
839 
840   elt_size = _dbus_g_type_fixed_get_size (elt_type);
841 
842   /* These are "safe" defaults */
843   zero_terminated = TRUE; /* ((struct _DBusGRealArray*) garray)->zero_terminated; */
844   clear = TRUE; /* ((struct _DBusGRealArray*) garray)->clear; */
845 
846   array = g_array_new (zero_terminated, clear, elt_size);
847   return array;
848 }
849 
850 static void
array_iterator(GType garray_type,gpointer instance,DBusGTypeSpecializedCollectionIterator iterator,gpointer user_data)851 array_iterator (GType garray_type,
852     gpointer instance,
853     DBusGTypeSpecializedCollectionIterator iterator,
854     gpointer user_data)
855 {
856   GArray *array;
857   GType elt_gtype;
858   guint i;
859 
860   array = instance;
861 
862   elt_gtype = dbus_g_type_get_collection_specialization (garray_type);
863 
864   for (i = 0; i < array->len; i++)
865     {
866       GValue val = {0, };
867       g_value_init (&val, elt_gtype);
868 
869       switch (elt_gtype)
870         {
871           case G_TYPE_BOOLEAN:
872             g_value_set_boolean (&val, !!g_array_index (array, gboolean, i));
873             break;
874 
875           case G_TYPE_FLOAT:
876             g_value_set_float (&val, g_array_index (array, gfloat, i));
877             break;
878 
879           case G_TYPE_DOUBLE:
880             g_value_set_double (&val, g_array_index (array, gdouble, i));
881             break;
882 
883           case G_TYPE_CHAR:
884             g_value_set_char (&val, g_array_index (array, gchar, i));
885             break;
886 
887           case G_TYPE_UCHAR:
888             g_value_set_uchar (&val, g_array_index (array, guchar, i));
889             break;
890 
891           case G_TYPE_INT:
892             g_value_set_int (&val, g_array_index (array, gint, i));
893             break;
894 
895           case G_TYPE_UINT:
896             g_value_set_uint (&val, g_array_index (array, guint, i));
897             break;
898 
899           case G_TYPE_LONG:
900             g_value_set_long (&val, g_array_index (array, glong, i));
901             break;
902 
903           case G_TYPE_ULONG:
904             g_value_set_ulong (&val, g_array_index (array, gulong, i));
905             break;
906 
907           case G_TYPE_INT64:
908             g_value_set_int64 (&val, g_array_index (array, gint64, i));
909             break;
910 
911           case G_TYPE_UINT64:
912             g_value_set_uint64 (&val, g_array_index (array, guint64, i));
913             break;
914 
915           default:
916             g_assert_not_reached ();
917         }
918 
919       iterator (&val, user_data);
920     }
921 }
922 
923 static void
array_append(DBusGTypeSpecializedAppendContext * ctx,GValue * value)924 array_append (DBusGTypeSpecializedAppendContext *ctx,
925     GValue *value)
926 {
927   GArray *array = g_value_get_boxed (ctx->val);
928   GType elt_gtype;
929   union {
930       guint64 u64;
931       gint64 i64;
932       gulong ul;
933       glong l;
934       guint u;
935       gint i;
936       guchar uc;
937       gchar c;
938       gboolean b;
939       gfloat f;
940       gdouble d;
941   } tmp;
942 
943   elt_gtype = dbus_g_type_get_collection_specialization (
944       G_VALUE_TYPE (ctx->val));
945 
946   switch (elt_gtype)
947     {
948       case G_TYPE_BOOLEAN:
949         tmp.b = g_value_get_boolean (value);
950         g_array_append_val (array, tmp.b);
951         break;
952 
953       case G_TYPE_FLOAT:
954         tmp.f = g_value_get_float (value);
955         g_array_append_val (array, tmp.f);
956         break;
957 
958       case G_TYPE_DOUBLE:
959         tmp.d = g_value_get_double (value);
960         g_array_append_val (array, tmp.d);
961         break;
962 
963       case G_TYPE_CHAR:
964         tmp.c = g_value_get_char (value);
965         g_array_append_val (array, tmp.c);
966         break;
967 
968       case G_TYPE_UCHAR:
969         tmp.uc = g_value_get_uchar (value);
970         g_array_append_val (array, tmp.uc);
971         break;
972 
973       case G_TYPE_INT:
974         tmp.i = g_value_get_int (value);
975         g_array_append_val (array, tmp.i);
976         break;
977 
978       case G_TYPE_UINT:
979         tmp.u = g_value_get_uint (value);
980         g_array_append_val (array, tmp.u);
981         break;
982 
983       case G_TYPE_LONG:
984         tmp.l = g_value_get_long (value);
985         g_array_append_val (array, tmp.l);
986         break;
987 
988       case G_TYPE_ULONG:
989         tmp.ul = g_value_get_ulong (value);
990         g_array_append_val (array, tmp.ul);
991         break;
992 
993       case G_TYPE_INT64:
994         tmp.i64 = g_value_get_int64 (value);
995         g_array_append_val (array, tmp.i64);
996         break;
997 
998       case G_TYPE_UINT64:
999         tmp.u64 = g_value_get_uint64 (value);
1000         g_array_append_val (array, tmp.u64);
1001         break;
1002 
1003       default:
1004         g_assert_not_reached ();
1005     }
1006 }
1007 
1008 static gpointer
array_copy(GType type,gpointer src)1009 array_copy (GType type, gpointer src)
1010 {
1011   GArray *garray;
1012   GArray *new;
1013 
1014   garray = src;
1015 
1016   new = array_constructor (type);
1017   g_array_append_vals (new, garray->data, garray->len);
1018 
1019   return new;
1020 }
1021 
1022 static void
array_simple_free(gpointer val)1023 array_simple_free (gpointer val)
1024 {
1025   GArray *array;
1026   array = val;
1027   g_array_free (array, TRUE);
1028 }
1029 
1030 static gboolean
array_fixed_accessor(GType type,gpointer instance,gpointer * values,guint * len)1031 array_fixed_accessor (GType type, gpointer instance, gpointer *values, guint *len)
1032 {
1033   GType elt_type;
1034   GArray *array = instance;
1035 
1036   elt_type = dbus_g_type_get_collection_specialization (type);
1037   if (!_dbus_g_type_is_fixed (elt_type))
1038     return FALSE;
1039 
1040   *values = array->data;
1041   *len = array->len;
1042   return TRUE;
1043 }
1044 
1045 static gpointer
ptrarray_constructor(GType type)1046 ptrarray_constructor (GType type)
1047 {
1048   /* Later we should determine a destructor, need g_ptr_array_destroy */
1049   return g_ptr_array_new ();
1050 }
1051 
1052 static void
gvalue_take_ptrarray_value(GValue * value,gpointer instance)1053 gvalue_take_ptrarray_value (GValue *value, gpointer instance)
1054 {
1055   switch (g_type_fundamental (G_VALUE_TYPE (value)))
1056     {
1057     case G_TYPE_STRING:
1058       g_value_take_string (value, instance);
1059       break;
1060     case G_TYPE_BOXED:
1061       g_value_take_boxed (value, instance);
1062       break;
1063     case G_TYPE_OBJECT:
1064       g_value_take_object (value, instance);
1065       break;
1066     default:
1067       g_assert_not_reached ();
1068       break;
1069     }
1070 }
1071 
1072 static gpointer
ptrarray_value_from_gvalue(const GValue * value)1073 ptrarray_value_from_gvalue (const GValue *value)
1074 {
1075   GValue tmp = {0, };
1076 
1077   /* if the NOCOPY flag is set, then value was created via set_static and hence
1078    * is not owned by us. in order to preserve the "take" semantics that the API
1079    * has in general (which avoids copying in the common case), we must copy any
1080    * static values so that we can indiscriminately free the entire collection
1081    * later. */
1082   if (value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS)
1083     {
1084       g_value_init (&tmp, G_VALUE_TYPE (value));
1085       g_value_copy (value, &tmp);
1086       value = &tmp;
1087     }
1088 
1089   switch (g_type_fundamental (G_VALUE_TYPE (value)))
1090     {
1091     case G_TYPE_STRING:
1092       return (gpointer) g_value_get_string (value);
1093       break;
1094     case G_TYPE_BOXED:
1095       return g_value_get_boxed (value);
1096       break;
1097     case G_TYPE_OBJECT:
1098       return g_value_get_object (value);
1099       break;
1100     default:
1101       g_assert_not_reached ();
1102       return NULL;
1103     }
1104 }
1105 
1106 static void
ptrarray_iterator(GType ptrarray_type,gpointer instance,DBusGTypeSpecializedCollectionIterator iterator,gpointer user_data)1107 ptrarray_iterator (GType                                   ptrarray_type,
1108 		   gpointer                                instance,
1109 		   DBusGTypeSpecializedCollectionIterator  iterator,
1110 		   gpointer                                user_data)
1111 {
1112   GPtrArray *ptrarray;
1113   GType elt_gtype;
1114   guint i;
1115 
1116   ptrarray = instance;
1117 
1118   elt_gtype = dbus_g_type_get_collection_specialization (ptrarray_type);
1119 
1120   for (i = 0; i < ptrarray->len; i++)
1121     {
1122       GValue val = {0, };
1123       g_value_init (&val, elt_gtype);
1124       gvalue_take_ptrarray_value (&val, g_ptr_array_index (ptrarray, i));
1125       iterator (&val, user_data);
1126     }
1127 }
1128 
1129 static void
ptrarray_copy_elt(const GValue * val,gpointer user_data)1130 ptrarray_copy_elt (const GValue *val, gpointer user_data)
1131 {
1132   GPtrArray *dest = user_data;
1133   GValue val_copy = {0, };
1134 
1135   g_value_init (&val_copy, G_VALUE_TYPE (val));
1136   g_value_copy (val, &val_copy);
1137 
1138   g_ptr_array_add (dest, ptrarray_value_from_gvalue (&val_copy));
1139 }
1140 
1141 static gpointer
ptrarray_copy(GType type,gpointer src)1142 ptrarray_copy (GType type, gpointer src)
1143 {
1144   GPtrArray *new;
1145   GValue array_val = {0, };
1146 
1147   g_value_init (&array_val, type);
1148   g_value_set_static_boxed (&array_val, src);
1149 
1150   new = ptrarray_constructor (type);
1151   dbus_g_type_collection_value_iterate (&array_val, ptrarray_copy_elt, new);
1152 
1153   return new;
1154 }
1155 
1156 static void
ptrarray_append(DBusGTypeSpecializedAppendContext * ctx,GValue * value)1157 ptrarray_append (DBusGTypeSpecializedAppendContext *ctx, GValue *value)
1158 {
1159   GPtrArray *array;
1160 
1161   array = g_value_get_boxed (ctx->val);
1162 
1163   g_ptr_array_add (array, ptrarray_value_from_gvalue (value));
1164 }
1165 
1166 static void
ptrarray_free(GType type,gpointer val)1167 ptrarray_free (GType type, gpointer val)
1168 {
1169   GPtrArray *array;
1170   GValue elt_val = {0, };
1171   GType elt_gtype;
1172   unsigned int i;
1173 
1174   array = val;
1175 
1176   elt_gtype = dbus_g_type_get_collection_specialization (type);
1177 
1178   for (i = 0; i < array->len; i++)
1179     {
1180       g_value_init (&elt_val, elt_gtype);
1181       gvalue_take_ptrarray_value (&elt_val, g_ptr_array_index (array, i));
1182       g_value_unset (&elt_val);
1183     }
1184 
1185   g_ptr_array_free (array, TRUE);
1186 }
1187 
1188 static gpointer
slist_constructor(GType type)1189 slist_constructor (GType type)
1190 {
1191   return NULL;
1192 }
1193 
1194 static void
slist_iterator(GType list_type,gpointer instance,DBusGTypeSpecializedCollectionIterator iterator,gpointer user_data)1195 slist_iterator (GType                                   list_type,
1196 		gpointer                                instance,
1197 		DBusGTypeSpecializedCollectionIterator  iterator,
1198 		gpointer                                user_data)
1199 {
1200   GSList *slist;
1201   GType elt_gtype;
1202 
1203   slist = instance;
1204 
1205   elt_gtype = dbus_g_type_get_collection_specialization (list_type);
1206 
1207   for (slist = instance; slist != NULL; slist = slist->next)
1208     {
1209       GValue val = {0, };
1210       g_value_init (&val, elt_gtype);
1211       gvalue_take_ptrarray_value (&val, slist->data);
1212       iterator (&val, user_data);
1213     }
1214 }
1215 
1216 static void
slist_copy_elt(const GValue * val,gpointer user_data)1217 slist_copy_elt (const GValue *val, gpointer user_data)
1218 {
1219   GSList **dest = user_data;
1220   GValue val_copy = {0, };
1221 
1222   g_value_init (&val_copy, G_VALUE_TYPE (val));
1223   g_value_copy (val, &val_copy);
1224 
1225   *dest = g_slist_append (*dest, ptrarray_value_from_gvalue (&val_copy));
1226 }
1227 
1228 static gpointer
slist_copy(GType type,gpointer src)1229 slist_copy (GType type, gpointer src)
1230 {
1231   GSList *new;
1232   GValue slist_val = {0, };
1233 
1234   g_value_init (&slist_val, type);
1235   g_value_set_static_boxed (&slist_val, src);
1236 
1237   new = slist_constructor (type);
1238   dbus_g_type_collection_value_iterate (&slist_val, slist_copy_elt, &new);
1239 
1240   return new;
1241 }
1242 
1243 static void
slist_append(DBusGTypeSpecializedAppendContext * ctx,GValue * value)1244 slist_append (DBusGTypeSpecializedAppendContext *ctx, GValue *value)
1245 {
1246   GSList *list;
1247 
1248   list = g_value_get_boxed (ctx->val);
1249   list = g_slist_prepend (list, ptrarray_value_from_gvalue (value));
1250   g_value_set_static_boxed (ctx->val, list);
1251 }
1252 
1253 static void
slist_end_append(DBusGTypeSpecializedAppendContext * ctx)1254 slist_end_append (DBusGTypeSpecializedAppendContext *ctx)
1255 {
1256   GSList *list;
1257 
1258   /* if you append multiple times to the slist, this will reverse the existing
1259    * elements... we need an init_append function */
1260   list = g_value_get_boxed (ctx->val);
1261   list = g_slist_reverse (list);
1262 
1263   g_value_take_boxed (ctx->val, list);
1264 }
1265 
1266 static void
slist_free(GType type,gpointer val)1267 slist_free (GType type, gpointer val)
1268 {
1269   GSList *list;
1270   GType elt_gtype;
1271   list = val;
1272 
1273   elt_gtype = dbus_g_type_get_collection_specialization (type);
1274 
1275   while (list != NULL)
1276     {
1277       GValue elt_val = {0, };
1278       g_value_init (&elt_val, elt_gtype);
1279       gvalue_take_ptrarray_value (&elt_val, list->data);
1280       g_value_unset (&elt_val);
1281       list = g_slist_next(list);
1282     }
1283   list=val;
1284   g_slist_free (list);
1285 }
1286 
1287 void
_dbus_g_type_specialized_builtins_init(void)1288 _dbus_g_type_specialized_builtins_init (void)
1289 {
1290   /* types with a simple_free function can be freed at run-time without
1291    * the destroy function needing to know the type, so they can be
1292    * stored in hash tables */
1293 
1294   static const DBusGTypeSpecializedCollectionVtable array_vtable = {
1295     {
1296       array_constructor,
1297       NULL,
1298       array_copy,
1299       array_simple_free,
1300       NULL,
1301       NULL,
1302     },
1303     array_fixed_accessor,
1304     array_iterator,
1305     array_append,
1306     NULL
1307   };
1308 
1309 
1310   static const DBusGTypeSpecializedCollectionVtable ptrarray_vtable = {
1311     {
1312       ptrarray_constructor,
1313       ptrarray_free,
1314       ptrarray_copy,
1315       NULL,
1316       NULL,
1317       NULL,
1318     },
1319     NULL,
1320     ptrarray_iterator,
1321     ptrarray_append,
1322     NULL,
1323   };
1324 
1325 
1326   static const DBusGTypeSpecializedCollectionVtable slist_vtable = {
1327     {
1328       slist_constructor,
1329       slist_free,
1330       slist_copy,
1331       NULL,
1332       NULL,
1333       NULL,
1334     },
1335     NULL,
1336     slist_iterator,
1337     slist_append,
1338     slist_end_append,
1339   };
1340 
1341   static const DBusGTypeSpecializedMapVtable hashtable_vtable = {
1342     {
1343       hashtable_constructor,
1344       hashtable_free,
1345       hashtable_copy,
1346       hashtable_simple_free,
1347       NULL,
1348       NULL
1349     },
1350     hashtable_iterator,
1351     hashtable_append
1352   };
1353 
1354   static const DBusGTypeSpecializedStructVtable valuearray_vtable = {
1355     {
1356       valuearray_constructor,
1357       NULL,
1358       valuearray_copy,
1359       valuearray_simple_free,
1360       NULL,
1361       NULL
1362     },
1363     valuearray_get_member,
1364     valuearray_set_member
1365   };
1366 
1367   _dbus_g_type_register_collection ("GSList", &slist_vtable, 0);
1368   _dbus_g_type_register_collection ("GArray", &array_vtable, 0);
1369   _dbus_g_type_register_collection ("GPtrArray", &ptrarray_vtable, 0);
1370   _dbus_g_type_register_map ("GHashTable", &hashtable_vtable, 0);
1371   _dbus_g_type_register_struct ("GValueArray", &valuearray_vtable, 0);
1372 }
1373