device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm-util / nm-param-spec-specialized.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
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 Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  * Boston, MA 02110-1301 USA.
18  *
19  * Copyright 2007 - 2011 Red Hat, Inc.
20  * Copyright 2007 - 2008 Novell, Inc.
21  */
22
23 #include "nm-default.h"
24
25 #include "nm-gvaluearray-compat.h"
26 #include "nm-param-spec-specialized.h"
27
28 struct _NMParamSpecSpecialized {
29         GParamSpec parent;
30 };
31
32 #include <string.h>
33 #include <math.h>
34 #include <netinet/in.h>
35 #include <dbus/dbus-glib.h>
36
37 #include "nm-dbus-glib-types.h"
38
39 /***********************************************************/
40 /* _gvalues_compare */
41
42 static gint _gvalues_compare (const GValue *value1, const GValue *value2);
43
44 static gboolean
45 type_is_fixed_size (GType type, gsize *tsize)
46 {
47         switch (type) {
48         case G_TYPE_CHAR:
49                 if (tsize) *tsize = sizeof (char);
50                 return TRUE;
51         case G_TYPE_UCHAR:
52                 if (tsize) *tsize = sizeof (guchar);
53                 return TRUE;
54         case G_TYPE_BOOLEAN:
55                 if (tsize) *tsize = sizeof (gboolean);
56                 return TRUE;
57         case G_TYPE_LONG:
58                 if (tsize) *tsize = sizeof (glong);
59                 return TRUE;
60         case G_TYPE_ULONG:
61                 if (tsize) *tsize = sizeof (gulong);
62                 return TRUE;
63         case G_TYPE_INT:
64                 if (tsize) *tsize = sizeof (gint);
65                 return TRUE;
66         case G_TYPE_UINT:
67                 if (tsize) *tsize = sizeof (guint);
68                 return TRUE;
69         case G_TYPE_INT64:
70                 if (tsize) *tsize = sizeof (gint64);
71                 return TRUE;
72         case G_TYPE_UINT64:
73                 if (tsize) *tsize = sizeof (guint64);
74                 return TRUE;
75         case G_TYPE_FLOAT:
76                 if (tsize) *tsize = sizeof (gfloat);
77                 return TRUE;
78         case G_TYPE_DOUBLE:
79                 if (tsize) *tsize = sizeof (gdouble);
80                 return TRUE;
81         default:
82                 return FALSE;
83         }
84 }
85
86 #define FLOAT_FACTOR 0.00000001
87
88 static gint
89 _gvalues_compare_fixed (const GValue *value1, const GValue *value2)
90 {
91         int ret = 0;
92
93         switch (G_VALUE_TYPE (value1)) {
94         case G_TYPE_CHAR: {
95                 gchar val1 = g_value_get_schar (value1);
96                 gchar val2 = g_value_get_schar (value2);
97                 if (val1 != val2)
98                         ret = val1 < val2 ? -1 : val1 > val2;
99                 break;
100         }
101         case G_TYPE_UCHAR: {
102                 guchar val1 = g_value_get_uchar (value1);
103                 guchar val2 = g_value_get_uchar (value2);
104                 if (val1 != val2)
105                         ret = val1 < val2 ? -1 : val1 > val2;
106                 break;
107         }
108         case G_TYPE_BOOLEAN: {
109                 gboolean val1 = g_value_get_boolean (value1);
110                 gboolean val2 = g_value_get_boolean (value2);
111                 if (val1 != val2)
112                         ret = val1 < val2 ? -1 : val1 > val2;
113                 break;
114         }
115         case G_TYPE_LONG: {
116                 glong val1 = g_value_get_long (value1);
117                 glong val2 = g_value_get_long (value2);
118                 if (val1 != val2)
119                         ret = val1 < val2 ? -1 : val1 > val2;
120                 break;
121         }
122         case G_TYPE_ULONG: {
123                 gulong val1 = g_value_get_ulong (value1);
124                 gulong val2 = g_value_get_ulong (value2);
125                 if (val1 != val2)
126                         ret = val1 < val2 ? -1 : val1 > val2;
127                 break;
128         }
129         case G_TYPE_INT: {
130                 gint val1 = g_value_get_int (value1);
131                 gint val2 = g_value_get_int (value2);
132                 if (val1 != val2)
133                         ret = val1 < val2 ? -1 : val1 > val2;
134                 break;
135         }
136         case G_TYPE_UINT: {
137                 guint val1 = g_value_get_uint (value1);
138                 guint val2 = g_value_get_uint (value2);
139                 if (val1 != val2)
140                         ret = val1 < val2 ? -1 : val1 > val2;
141                 break;
142         }
143         case G_TYPE_INT64: {
144                 gint64 val1 = g_value_get_int64 (value1);
145                 gint64 val2 = g_value_get_int64 (value2);
146                 if (val1 != val2)
147                         ret = val1 < val2 ? -1 : val1 > val2;
148                 break;
149         }
150         case G_TYPE_UINT64: {
151                 guint64 val1 = g_value_get_uint64 (value1);
152                 guint64 val2 = g_value_get_uint64 (value2);
153                 if (val1 != val2)
154                         ret = val1 < val2 ? -1 : val1 > val2;
155                 break;
156         }
157         case G_TYPE_FLOAT: {
158                 gfloat val1 = g_value_get_float (value1);
159                 gfloat val2 = g_value_get_float (value2);
160                 /* Can't use == or != here due to inexactness of FP */
161                 if (fabsf (val1 - val2) > FLOAT_FACTOR)
162                         ret = val1 < val2 ? -1 : val1 > val2;
163                 break;
164         }
165         case G_TYPE_DOUBLE: {
166                 gdouble val1 = g_value_get_double (value1);
167                 gdouble val2 = g_value_get_double (value2);
168                 if (fabs (val1 - val2) > FLOAT_FACTOR)
169                         ret = val1 < val2 ? -1 : val1 > val2;
170                 break;
171         }
172         default:
173                 g_warning ("Unhandled fixed size type '%s'", G_VALUE_TYPE_NAME (value1));
174         }
175
176         return ret;
177 }
178
179 static gint
180 _gvalues_compare_string (const GValue *value1, const GValue *value2)
181 {
182         const char *str1 = g_value_get_string (value1);
183         const char *str2 = g_value_get_string (value2);
184
185         if (str1 == str2)
186                 return 0;
187
188         if (!str1)
189                 return 1;
190         if (!str2)
191                 return -1;
192
193         return strcmp (str1, str2);
194 }
195
196 static gint
197 _gvalues_compare_strv (const GValue *value1, const GValue *value2)
198 {
199         char **strv1;
200         char **strv2;
201         gint ret;
202         guint i = 0;
203
204         strv1 = (char **) g_value_get_boxed (value1);
205         strv2 = (char **) g_value_get_boxed (value2);
206
207         while (strv1[i] && strv2[i]) {
208                 ret = strcmp (strv1[i], strv2[i]);
209                 if (ret)
210                         return ret;
211                 i++;
212         }
213
214         if (strv1[i] == NULL && strv2[i] == NULL)
215                 return 0;
216
217         if (strv1[i])
218                 return 1;
219
220         return -1;
221 }
222
223 static void
224 _gvalue_destroy (gpointer data)
225 {
226         GValue *value = (GValue *) data;
227
228         g_value_unset (value);
229         g_slice_free (GValue, value);
230 }
231
232 static GValue *
233 _gvalue_dup (const GValue *value)
234 {
235         GValue *dup;
236
237         dup = g_slice_new0 (GValue);
238         g_value_init (dup, G_VALUE_TYPE (value));
239         g_value_copy (value, dup);
240
241         return dup;
242 }
243
244 static void
245 iterate_collection (const GValue *value, gpointer user_data)
246 {
247         GSList **list = (GSList **) user_data;
248
249         *list = g_slist_prepend (*list, _gvalue_dup (value));
250 }
251
252 static gint
253 _gvalues_compare_collection (const GValue *value1, const GValue *value2)
254 {
255         gint ret;
256         guint len1;
257         guint len2;
258         GType value_type = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value1));
259         gsize element_size = 0;
260
261         if (type_is_fixed_size (value_type, &element_size)) {
262                 gpointer data1 = NULL;
263                 gpointer data2 = NULL;
264
265                 dbus_g_type_collection_get_fixed ((GValue *) value1, &data1, &len1);
266                 dbus_g_type_collection_get_fixed ((GValue *) value2, &data2, &len2);
267
268                 if (len1 != len2)
269                         ret = len1 < len2 ? -1 : len1 > len2;
270                 else
271                         ret = memcmp (data1, data2, len1 * element_size);
272         } else {
273                 GSList *list1 = NULL;
274                 GSList *list2 = NULL;
275
276                 dbus_g_type_collection_value_iterate (value1, iterate_collection, &list1);
277                 len1 = g_slist_length (list1);
278                 dbus_g_type_collection_value_iterate (value2, iterate_collection, &list2);
279                 len2 = g_slist_length (list2);
280
281                 if (len1 != len2)
282                         ret = len1 < len2 ? -1 : len1 > len2;
283                 else {
284                         GSList *iter1;
285                         GSList *iter2;
286
287                         for (iter1 = list1, iter2 = list2, ret = 0;
288                              ret == 0 && iter1 && iter2;
289                              iter1 = iter1->next, iter2 = iter2->next)
290                                 ret = _gvalues_compare ((GValue *) iter1->data, (GValue *) iter2->data);
291                 }
292
293                 g_slist_free_full (list1, _gvalue_destroy);
294                 g_slist_free_full (list2, _gvalue_destroy);
295         }
296
297         return ret;
298 }
299
300 static void
301 iterate_map (const GValue *key_val,
302              const GValue *value_val,
303              gpointer user_data)
304 {
305         GHashTable **hash = (GHashTable **) user_data;
306
307         g_hash_table_insert (*hash, g_value_dup_string (key_val), _gvalue_dup (value_val));
308 }
309
310 typedef struct {
311         GHashTable *hash2;
312         gint ret;
313 } CompareMapInfo;
314
315 static void
316 compare_one_map_item (gpointer key, gpointer val, gpointer user_data)
317 {
318         CompareMapInfo *info = (CompareMapInfo *) user_data;
319         GValue *value2;
320
321         if (info->ret)
322                 return;
323
324         value2 = (GValue *) g_hash_table_lookup (info->hash2, key);
325         if (value2)
326                 info->ret = _gvalues_compare ((GValue *) val, value2);
327         else
328                 info->ret = 1;
329 }
330
331 static gint
332 _gvalues_compare_map (const GValue *value1, const GValue *value2)
333 {
334         GHashTable *hash1 = NULL;
335         GHashTable *hash2 = NULL;
336         guint len1;
337         guint len2;
338         gint ret = 0;
339
340         if (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1)) != G_TYPE_STRING) {
341                 g_warning ("Can not compare maps with '%s' for keys",
342                            g_type_name (dbus_g_type_get_map_key_specialization (G_VALUE_TYPE (value1))));
343                 return 0;
344         }
345
346         hash1 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy);
347         dbus_g_type_map_value_iterate (value1, iterate_map, &hash1);
348         len1 = g_hash_table_size (hash1);
349
350         hash2 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, _gvalue_destroy);
351         dbus_g_type_map_value_iterate (value2, iterate_map, &hash2);
352         len2 = g_hash_table_size (hash2);
353
354         if (len1 != len2)
355                 ret = len1 < len2 ? -1 : len1 > len2;
356         else {
357                 CompareMapInfo info;
358
359                 info.ret = 0;
360                 info.hash2 = hash2;
361                 g_hash_table_foreach (hash1, compare_one_map_item, &info);
362                 ret = info.ret;
363         }
364
365         g_hash_table_destroy (hash1);
366         g_hash_table_destroy (hash2);
367
368         return ret;
369 }
370
371 static gint
372 _gvalue_ip6_address_compare (const GValue *value1, const GValue *value2)
373 {
374         GValueArray *values1, *values2;
375         GValue *tmp_val;
376         GByteArray *addr1, *addr2;
377         guint32 prefix1, prefix2;
378         GByteArray *gw1, *gw2;
379         gint ret = 0;
380         int i;
381
382         /* IP6 addresses are GValueArrays (see nm-dbus-glib-types.h) */
383         values1 = g_value_get_boxed (value1);
384         values2 = g_value_get_boxed (value2);
385
386         /* Since they are NM IPv6 address structures, we expect both
387          * to contain two elements as specified in nm-dbus-glib-types.h.
388          */
389         g_return_val_if_fail (values1->n_values == 3, 0);
390         g_return_val_if_fail (values2->n_values == 3, 0);
391
392         /* First struct IPv6 address */
393         tmp_val = g_value_array_get_nth (values1, 0);
394         addr1 = g_value_get_boxed (tmp_val);
395         /* First struct IPv6 prefix */
396         tmp_val = g_value_array_get_nth (values1, 1);
397         prefix1 = g_value_get_uint (tmp_val);
398         /* First struct IPv6 gateway */
399         tmp_val = g_value_array_get_nth (values1, 2);
400         gw1 = g_value_get_boxed (tmp_val);
401
402         /* Second struct IPv6 address */
403         tmp_val = g_value_array_get_nth (values2, 0);
404         addr2 = g_value_get_boxed (tmp_val);
405         /* Second struct IPv6 prefix */
406         tmp_val = g_value_array_get_nth (values2, 1);
407         prefix2 = g_value_get_uint (tmp_val);
408         /* Second struct IPv6 gateway */
409         tmp_val = g_value_array_get_nth (values2, 2);
410         gw2 = g_value_get_boxed (tmp_val);
411
412         /* Compare IPv6 addresses */
413         if (prefix1 != prefix2)
414                 return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
415
416         if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)addr1->data, (struct in6_addr *)addr2->data)) {
417                 for (i = 0; ret == 0 && i < addr1->len; i++)
418                         ret = addr1->data[i] < addr2->data[i] ? -1 : addr1->data[i] > addr2->data[i];
419         }
420
421         if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *) gw1->data, (struct in6_addr *) gw2->data)) {
422                 for (i = 0; ret == 0 && i < gw1->len; i++)
423                         ret = gw1->data[i] < gw2->data[i] ? -1 : gw1->data[i] > gw2->data[i];
424         }
425
426         return ret;
427 }
428
429 static gint
430 _gvalue_ip6_route_compare (const GValue *value1, const GValue *value2)
431 {
432         GValueArray *values1, *values2;
433         GValue *tmp_val;
434         GByteArray *dest1, *dest2;
435         GByteArray *next_hop1, *next_hop2;
436         guint32 prefix1, prefix2;
437         guint32 metric1, metric2;
438         gint ret = 0;
439         int i;
440
441         /* IP6 routes are GValueArrays (see nm-dbus-glib-types.h) */
442         values1 = g_value_get_boxed (value1);
443         values2 = g_value_get_boxed (value2);
444
445         /* Since they are NM IPv6 route structures, we expect both
446          * to contain 4 elements as specified in nm-dbus-glib-types.h.
447          */
448         g_return_val_if_fail (values1->n_values == 4, 0);
449         g_return_val_if_fail (values2->n_values == 4, 0);
450
451         /* First struct IPv6 route */
452         tmp_val = g_value_array_get_nth (values1, 0);
453         dest1 = g_value_get_boxed (tmp_val);
454         tmp_val = g_value_array_get_nth (values1, 1);
455         prefix1 = g_value_get_uint (tmp_val);
456         tmp_val = g_value_array_get_nth (values1, 2);
457         next_hop1 = g_value_get_boxed (tmp_val);
458         tmp_val = g_value_array_get_nth (values1, 3);
459         metric1 = g_value_get_uint (tmp_val);
460
461         /* Second struct IPv6 route */
462         tmp_val = g_value_array_get_nth (values2, 0);
463         dest2 = g_value_get_boxed (tmp_val);
464         tmp_val = g_value_array_get_nth (values2, 1);
465         prefix2 = g_value_get_uint (tmp_val);
466         tmp_val = g_value_array_get_nth (values2, 2);
467         next_hop2 = g_value_get_boxed (tmp_val);
468         tmp_val = g_value_array_get_nth (values2, 3);
469         metric2 = g_value_get_uint (tmp_val);
470
471         /* Compare the routes */
472         if (prefix1 != prefix2)
473                 return prefix1 < prefix2 ? -1 : prefix1 > prefix2;
474
475         if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)dest1->data, (struct in6_addr *)dest2->data)) {
476                 for (i = 0; ret == 0 && i < dest1->len; i++)
477                         ret = dest1->data[i] < dest2->data[i] ? -1 : dest1->data[i] > dest2->data[i];
478         }
479
480         if (!IN6_ARE_ADDR_EQUAL ((struct in6_addr *)next_hop1->data, (struct in6_addr *)next_hop2->data)) {
481                 for (i = 0; ret == 0 && i < next_hop1->len; i++)
482                         ret = next_hop1->data[i] < next_hop2->data[i] ? -1 : next_hop1->data[i] > next_hop2->data[i];
483         }
484
485         if (metric1 != metric2)
486                 ret = metric1 < metric2 ? -1 : metric1 > metric2;
487
488         return ret;
489 }
490
491 static gint
492 _gvalues_compare_struct (const GValue *value1, const GValue *value2)
493 {
494         /* value1 and value2 must contain the same type since
495          * _gvalues_compare() enforced that already.
496          */
497
498         if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ADDRESS)) {
499                 return _gvalue_ip6_address_compare (value1, value2);
500         } else if (G_VALUE_HOLDS (value1, DBUS_TYPE_G_IP6_ROUTE)) {
501                 return _gvalue_ip6_route_compare (value1, value2);
502         } else {
503                 g_warning ("Don't know how to compare structures");
504                 return (value1 == value2);
505         }
506 }
507
508 gint
509 _gvalues_compare (const GValue *value1, const GValue *value2)
510 {
511         GType type1;
512         GType type2;
513         gint ret;
514
515         if (value1 == value2)
516                 return 0;
517         if (!value1)
518                 return 1;
519         if (!value2)
520                 return -1;
521
522         type1 = G_VALUE_TYPE (value1);
523         type2 = G_VALUE_TYPE (value2);
524
525         if (type1 != type2)
526                 return type1 < type2 ? -1 : type1 > type2;
527
528         if (type_is_fixed_size (type1, NULL))
529                 ret = _gvalues_compare_fixed (value1, value2);
530         else if (type1 == G_TYPE_STRING)
531                 ret = _gvalues_compare_string (value1, value2);
532         else if (G_VALUE_HOLDS_BOXED (value1)) {
533                 gpointer p1 = g_value_get_boxed (value1);
534                 gpointer p2 = g_value_get_boxed (value2);
535
536                 if (p1 == p2)
537                         ret = 0; /* Exactly the same values */
538                 else if (!p1)
539                         ret = 1; /* The comparision functions below don't handle NULLs */
540                 else if (!p2)
541                         ret = -1; /* The comparision functions below don't handle NULLs */
542                 else if (type1 == G_TYPE_STRV)
543                         ret = _gvalues_compare_strv (value1, value2);
544                 else if (dbus_g_type_is_collection (type1))
545                         ret = _gvalues_compare_collection (value1, value2);
546                 else if (dbus_g_type_is_map (type1))
547                         ret = _gvalues_compare_map (value1, value2);
548                 else if (dbus_g_type_is_struct (type1))
549                         ret = _gvalues_compare_struct (value1, value2);
550                 else if (type1 == G_TYPE_VALUE)
551                         ret = _gvalues_compare ((GValue *) g_value_get_boxed (value1), (GValue *) g_value_get_boxed (value2));
552                 else {
553                         g_warning ("Don't know how to compare boxed types '%s'", g_type_name (type1));
554                         ret = value1 == value2;
555                 }
556         } else {
557                 g_warning ("Don't know how to compare types '%s'", g_type_name (type1));
558                 ret = value1 == value2;
559         }
560
561         return ret;
562 }
563
564 /***********************************************************/
565
566 static void
567 param_specialized_init (GParamSpec *pspec)
568 {
569 }
570
571 static void
572 param_specialized_set_default (GParamSpec *pspec, GValue *value)
573 {
574         value->data[0].v_pointer = NULL;
575 }
576
577 static gboolean
578 param_specialized_validate (GParamSpec *pspec, GValue *value)
579 {
580         NMParamSpecSpecialized *sspec = NM_PARAM_SPEC_SPECIALIZED (pspec);
581         GType value_type = G_VALUE_TYPE (value);
582         gboolean changed = FALSE;
583
584         if (!g_value_type_compatible (value_type, G_PARAM_SPEC_VALUE_TYPE (sspec))) {
585                 g_value_reset (value);
586                 changed = TRUE;
587         }
588
589         return changed;
590 }
591
592 static gint
593 param_specialized_values_cmp (GParamSpec *pspec,
594                               const GValue *value1,
595                               const GValue *value2)
596 {
597         return _gvalues_compare (value1, value2);
598 }
599
600 GType
601 _nm_param_spec_specialized_get_type (void)
602 {
603         static GType type;
604
605         if (G_UNLIKELY (type) == 0) {
606                 static const GParamSpecTypeInfo pspec_info = {
607                         sizeof (NMParamSpecSpecialized),
608                         0,
609                         param_specialized_init,
610                         G_TYPE_OBJECT, /* value_type */
611                         NULL,          /* finalize */
612                         param_specialized_set_default,
613                         param_specialized_validate,
614                         param_specialized_values_cmp,
615                 };
616                 type = g_param_type_register_static ("NMParamSpecSpecialized", &pspec_info);
617         }
618
619         return type;
620 }
621
622 GParamSpec *
623 _nm_param_spec_specialized (const char *name,
624                             const char *nick,
625                             const char *blurb,
626                             GType specialized_type,
627                             GParamFlags flags)
628 {
629         NMParamSpecSpecialized *pspec;
630
631         g_return_val_if_fail (g_type_is_a (specialized_type, G_TYPE_BOXED), NULL);
632
633         pspec = g_param_spec_internal (NM_TYPE_PARAM_SPEC_SPECIALIZED,
634                                        name, nick, blurb, flags);
635
636         G_PARAM_SPEC (pspec)->value_type = specialized_type;
637
638         return G_PARAM_SPEC (pspec);
639 }
640
641 /***********************************************************/
642 /* Tests */
643
644 #if 0
645
646 static void
647 compare_ints (void)
648 {
649         GValue value1 = G_VALUE_INIT;
650         GValue value2 = G_VALUE_INIT;
651
652         g_value_init (&value1, G_TYPE_INT);
653         g_value_init (&value2, G_TYPE_INT);
654
655         g_value_set_int (&value1, 5);
656         g_value_set_int (&value2, 5);
657         g_print ("Comparing ints 5 and 5: %d\n", _gvalues_compare (&value1, &value2));
658
659         g_value_set_int (&value2, 10);
660         g_print ("Comparing ints 5 and 10: %d\n", _gvalues_compare (&value1, &value2));
661
662         g_value_set_int (&value2, 1);
663         g_print ("Comparing ints 5 and 1: %d\n", _gvalues_compare (&value1, &value2));
664 }
665
666 static void
667 compare_strings (void)
668 {
669         GValue value1 = G_VALUE_INIT;
670         GValue value2 = G_VALUE_INIT;
671         const char *str1 = "hello";
672         const char *str2 = "world";
673
674         g_value_init (&value1, G_TYPE_STRING);
675         g_value_init (&value2, G_TYPE_STRING);
676
677         g_value_set_string (&value1, str1);
678         g_value_set_string (&value2, str1);
679         g_print ("Comparing identical strings: %d\n", _gvalues_compare (&value1, &value2));
680
681         g_value_set_string (&value2, str2);
682         g_print ("Comparing different strings: %d\n", _gvalues_compare (&value1, &value2));
683 }
684
685 static void
686 compare_strv (void)
687 {
688         GValue value1 = G_VALUE_INIT;
689         GValue value2 = G_VALUE_INIT;
690         char *strv1[] = { "foo", "bar", "baz", NULL };
691         char *strv2[] = { "foo", "bar", "bar", NULL };
692         char *strv3[] = { "foo", "bar", NULL };
693         char *strv4[] = { "foo", "bar", "baz", "bam", NULL };
694
695         g_value_init (&value1, G_TYPE_STRV);
696         g_value_init (&value2, G_TYPE_STRV);
697
698         g_value_set_boxed (&value1, strv1);
699         g_value_set_boxed (&value2, strv1);
700         g_print ("Comparing identical strv's: %d\n", _gvalues_compare (&value1, &value2));
701
702         g_value_set_boxed (&value2, strv2);
703         g_print ("Comparing different strv's: %d\n", _gvalues_compare (&value1, &value2));
704
705         g_value_set_boxed (&value2, strv3);
706         g_print ("Comparing different len (smaller) strv's: %d\n", _gvalues_compare (&value1, &value2));
707
708         g_value_set_boxed (&value2, strv4);
709         g_print ("Comparing different len (longer) strv's: %d\n", _gvalues_compare (&value1, &value2));
710 }
711
712 static void
713 compare_garrays (void)
714 {
715         GArray *array1;
716         GArray *array2;
717         GValue value1 = G_VALUE_INIT;
718         GValue value2 = G_VALUE_INIT;
719         int i;
720
721         g_value_init (&value1, DBUS_TYPE_G_UINT_ARRAY);
722         array1 = g_array_new (FALSE, FALSE, sizeof (guint32));
723
724         g_value_init (&value2, DBUS_TYPE_G_UINT_ARRAY);
725         array2 = g_array_new (FALSE, FALSE, sizeof (guint32));
726
727         for (i = 0; i < 5; i++) {
728                 g_array_append_val (array1, i);
729                 g_array_append_val (array2, i);
730         }
731
732         g_value_set_boxed (&value1, array1);
733         g_value_set_boxed (&value2, array2);
734
735         g_print ("Comparing identical arrays's: %d\n", _gvalues_compare (&value1, &value2));
736
737         g_array_remove_index (array2, 0);
738         g_value_set_boxed (&value2, array2);
739         g_print ("Comparing different length arrays's: %d\n", _gvalues_compare (&value1, &value2));
740
741         i = 7;
742         g_array_prepend_val (array2, i);
743         g_value_set_boxed (&value2, array2);
744         g_print ("Comparing different arrays's: %d\n", _gvalues_compare (&value1, &value2));
745 }
746
747 static void
748 compare_ptrarrays (void)
749 {
750         GPtrArray *array1;
751         GPtrArray *array2;
752         GValue value1 = G_VALUE_INIT;
753         GValue value2 = G_VALUE_INIT;
754
755         g_value_init (&value1, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
756         array1 = g_ptr_array_new ();
757
758         g_value_init (&value2, dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING));
759         array2 = g_ptr_array_new ();
760
761         g_ptr_array_add (array1, "hello");
762         g_ptr_array_add (array1, "world");
763         g_value_set_boxed (&value1, array1);
764
765         g_ptr_array_add (array2, "hello");
766         g_ptr_array_add (array2, "world");
767         g_value_set_boxed (&value2, array2);
768
769         g_print ("Comparing identical ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
770
771         g_ptr_array_add (array2, "boo");
772         g_value_set_boxed (&value2, array2);
773         g_print ("Comparing different len ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
774
775         g_ptr_array_add (array1, "booz");
776         g_value_set_boxed (&value1, array1);
777         g_print ("Comparing different ptr arrays's: %d\n", _gvalues_compare (&value1, &value2));
778 }
779
780 static void
781 compare_str_hash (void)
782 {
783         GHashTable *hash1;
784         GHashTable *hash2;
785         GValue value1 = G_VALUE_INIT;
786         GValue value2 = G_VALUE_INIT;
787
788         g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
789         g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING));
790
791         hash1 = g_hash_table_new (g_str_hash, g_str_equal);
792         hash2 = g_hash_table_new (g_str_hash, g_str_equal);
793
794         g_hash_table_insert (hash1, "key1", "hello");
795         g_hash_table_insert (hash1, "key2", "world");
796
797         g_hash_table_insert (hash2, "key1", "hello");
798         g_hash_table_insert (hash2, "key2", "world");
799
800         g_value_set_boxed (&value1, hash1);
801         g_value_set_boxed (&value2, hash2);
802         g_print ("Comparing identical str hashes: %d\n", _gvalues_compare (&value1, &value2));
803
804         g_hash_table_remove (hash2, "key2");
805         g_value_set_boxed (&value2, hash2);
806         g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2));
807
808         g_hash_table_insert (hash2, "key2", "moon");
809         g_value_set_boxed (&value2, hash2);
810         g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2));
811 }
812
813 static GValue *
814 str_to_gvalue (const char *str)
815 {
816         GValue *value;
817
818         value = g_slice_new0 (GValue);
819         g_value_init (value, G_TYPE_STRING);
820         g_value_set_string (value, str);
821
822         return value;
823 }
824
825 static GValue *
826 int_to_gvalue (int i)
827 {
828         GValue *value;
829
830         value = g_slice_new0 (GValue);
831         g_value_init (value, G_TYPE_INT);
832         g_value_set_int (value, i);
833
834         return value;
835 }
836
837 static void
838 compare_gvalue_hash (void)
839 {
840         GHashTable *hash1;
841         GHashTable *hash2;
842         GValue value1 = G_VALUE_INIT;
843         GValue value2 = G_VALUE_INIT;
844
845         g_value_init (&value1, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
846         g_value_init (&value2, dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE));
847
848         hash1 = g_hash_table_new (g_str_hash, g_str_equal);
849         hash2 = g_hash_table_new (g_str_hash, g_str_equal);
850
851         g_hash_table_insert (hash1, "key1", str_to_gvalue ("hello"));
852         g_hash_table_insert (hash1, "key2", int_to_gvalue (5));
853
854         g_hash_table_insert (hash2, "key1", str_to_gvalue ("hello"));
855         g_hash_table_insert (hash2, "key2", int_to_gvalue (5));
856
857         g_value_set_boxed (&value1, hash1);
858         g_value_set_boxed (&value2, hash2);
859         g_print ("Comparing identical gvalue hashes: %d\n", _gvalues_compare (&value1, &value2));
860
861         g_hash_table_remove (hash2, "key2");
862         g_value_set_boxed (&value2, hash2);
863         g_print ("Comparing different length str hashes: %d\n", _gvalues_compare (&value1, &value2));
864
865         g_hash_table_insert (hash2, "key2", str_to_gvalue ("moon"));
866         g_value_set_boxed (&value2, hash2);
867         g_print ("Comparing different str hashes: %d\n", _gvalues_compare (&value1, &value2));
868 }
869
870 static void
871 compare_ip6_addresses (void)
872 {
873         GValueArray *array1;
874         GValueArray *array2;
875         GValueArray *array3;
876         GByteArray *ba1;
877         GByteArray *ba2;
878         GByteArray *ba3;
879         GValue element = G_VALUE_INIT;
880         GValue value1 = G_VALUE_INIT;
881         GValue value2 = G_VALUE_INIT;
882         struct in6_addr addr1;
883         struct in6_addr addr2;
884         struct in6_addr addr3;
885         guint32 prefix1 = 64;
886         guint32 prefix2 = 64;
887         guint32 prefix3 = 0;
888
889         inet_pton (AF_INET6, "1:2:3:4:5:6:7:8", &addr1, sizeof (struct in6_addr));
890         inet_pton (AF_INET6, "ffff:2:3:4:5:6:7:8", &addr2, sizeof (struct in6_addr));
891         inet_pton (AF_INET6, "::", &addr3, sizeof (struct in6_addr));
892
893         /* address 1 */
894         ba1 = g_byte_array_new ();
895         array1 = g_value_array_new (2);
896         g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
897         g_byte_array_append (ba1, (guint8 *) addr1.s6_addr, 16);
898         g_value_take_boxed (&element, ba1);
899         g_value_array_append (array1, &element);
900         g_value_unset (&element);
901
902         g_value_init (&element, G_TYPE_UINT);
903         g_value_set_uint (&element, prefix1);
904         g_value_array_append (array1, &element);
905         g_value_unset (&element);
906
907         /* address 2 */
908         ba2 = g_byte_array_new ();
909         array2 = g_value_array_new (2);
910         g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
911         g_byte_array_append (ba2, (guint8 *) addr2.s6_addr, 16);
912         g_value_take_boxed (&element, ba2);
913         g_value_array_append (array2, &element);
914         g_value_unset (&element);
915
916         g_value_init (&element, G_TYPE_UINT);
917         g_value_set_uint (&element, prefix2);
918         g_value_array_append (array2, &element);
919         g_value_unset (&element);
920
921         /* address 3 */
922         ba3 = g_byte_array_new ();
923         array3 = g_value_array_new (2);
924         g_value_init (&element, DBUS_TYPE_G_UCHAR_ARRAY);
925         g_byte_array_append (ba3, (guint8 *) addr3.s6_addr, 16);
926         g_value_take_boxed (&element, ba3);
927         g_value_array_append (array3, &element);
928         g_value_unset (&element);
929
930         g_value_init (&element, G_TYPE_UINT);
931         g_value_set_uint (&element, prefix3);
932         g_value_array_append (array3, &element);
933         g_value_unset (&element);
934
935         g_value_init (&value1, DBUS_TYPE_G_IP6_ADDRESS);
936         g_value_init (&value2, DBUS_TYPE_G_IP6_ADDRESS);
937
938         g_value_set_boxed (&value1, array1);
939         g_value_set_boxed (&value2, array1);
940         g_print ("Comparing identical IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
941
942         g_value_set_boxed (&value1, array1);
943         g_value_set_boxed (&value2, array2);
944         g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
945
946         g_value_set_boxed (&value1, array1);
947         g_value_set_boxed (&value2, array3);
948         g_print ("Comparing different IPv6 address structures: %d\n", _gvalues_compare (&value1, &value2));
949 }
950
951 int
952 main (int argc, char *argv[])
953 {
954         DBusGConnection *bus;
955
956         nm_g_type_init ();
957
958         bus = dbus_g_bus_get (DBUS_BUS_SESSION, NULL);
959
960         compare_ints ();
961         compare_strings ();
962         compare_strv ();
963         compare_garrays ();
964         compare_ptrarrays ();
965         compare_str_hash ();
966         compare_gvalue_hash ();
967         compare_ip6_addresses ();
968
969         return 0;
970 }
971
972 #endif