Initial commit, not even close to being usable yet
[platform/upstream/at-spi2-core.git] / dbind / dbind-any.c
1 /* type driven marshalling */
2 #include <stdio.h>
3 #include <glib.h>
4
5 #include "config.h"
6 #include "dbind-config.h"
7 #include "dbind-any.h"
8
9 #undef DEBUG
10
11 /*  Align a value upward to a boundary, expressed as a number of bytes.
12  *  E.g. align to an 8-byte boundary with argument of 8.
13  *
14  *   (this + boundary - 1)
15  *          &
16  *    ~(boundary - 1)
17  */
18 #define ALIGN_VALUE(this, boundary) \
19   (( ((gulong)(this)) + (((gulong)(boundary)) -1)) & (~(((gulong)(boundary))-1)))
20
21 #define ALIGN_ADDRESS(this, boundary) \
22   ((gpointer)ALIGN_VALUE(this, boundary))
23
24 #define PTR_PLUS(ptr, offset) \
25         ((gpointer) (((guchar *)(ptr)) + (offset)))
26
27 #define DBIND_POD_CASES \
28          DBUS_TYPE_BYTE: \
29     case DBUS_TYPE_INT16: \
30     case DBUS_TYPE_UINT16: \
31     case DBUS_TYPE_INT32: \
32     case DBUS_TYPE_UINT32: \
33     case DBUS_TYPE_BOOLEAN: \
34     case DBUS_TYPE_INT64: \
35     case DBUS_TYPE_UINT64: \
36     case DBUS_TYPE_DOUBLE
37
38 /*---------------------------------------------------------------------------*/
39
40 static void
41 warn_braces ()
42 {
43     fprintf (stderr, "Error: dbus flags structures & dicts with braces rather than "
44              " an explicit type member of 'struct'\n");
45 }
46
47 /*---------------------------------------------------------------------------*/
48
49 static unsigned int
50 dbind_find_c_alignment_r (const char **type)
51 {
52     unsigned int retval = 1;
53
54     char t = **type;
55     (*type)++;
56
57 #ifdef DEBUG
58     fprintf (stderr, "\tfind align for %c (0x%x)\n", t, t);
59 #endif
60
61         switch (t) {
62     case DBUS_TYPE_BYTE:
63         return DBIND_ALIGNOF_CHAR;
64     case DBUS_TYPE_BOOLEAN:
65         return DBIND_ALIGNOF_DBUS_BOOL_T;
66     case DBUS_TYPE_INT16:
67     case DBUS_TYPE_UINT16:
68         return DBIND_ALIGNOF_DBUS_INT16_T;
69     case DBUS_TYPE_INT32:
70     case DBUS_TYPE_UINT32:
71         return DBIND_ALIGNOF_DBUS_INT32_T;
72     case DBUS_TYPE_INT64:
73     case DBUS_TYPE_UINT64:
74         return DBIND_ALIGNOF_DBUS_INT64_T;
75     case DBUS_TYPE_DOUBLE:
76         return DBIND_ALIGNOF_DOUBLE;
77     /* ptr types */
78     case DBUS_TYPE_STRING:
79     case DBUS_TYPE_OBJECT_PATH:
80     case DBUS_TYPE_SIGNATURE:
81     case DBUS_TYPE_ARRAY:
82         return DBIND_ALIGNOF_DBIND_POINTER;
83     case DBUS_STRUCT_BEGIN_CHAR:
84       /* TODO: I think this would break with a nested struct */
85 #if DBIND_ALIGNOF_DBIND_STRUCT > 1
86                 retval = MAX (retval, DBIND_ALIGNOF_DBIND_STRUCT);
87 #endif
88         while (**type != DBUS_STRUCT_END_CHAR) {
89             int elem_align = dbind_find_c_alignment_r (type);
90                         retval = MAX (retval, elem_align);
91         }
92         (*type)++;
93         return retval;
94     case DBUS_DICT_ENTRY_BEGIN_CHAR:
95 #if DBIND_ALIGNOF_DBIND_STRUCT > 1
96                 retval = MAX (retval, DBIND_ALIGNOF_DBIND_STRUCT);
97 #endif
98         while (**type != DBUS_DICT_ENTRY_END_CHAR) {
99             int elem_align = dbind_find_c_alignment_r (type);
100                         retval = MAX (retval, elem_align);
101         }
102         (*type)++;
103         return retval;
104     case DBUS_TYPE_STRUCT:
105     case DBUS_TYPE_DICT_ENTRY:
106         warn_braces ();
107         return DBIND_ALIGNOF_DBIND_POINTER;
108     case '\0':
109         g_assert_not_reached();
110         break;
111     default:
112                 return 1;
113   }
114 }
115
116 /*---------------------------------------------------------------------------*/
117
118 /* gather immediate allocation information for this type */
119 static size_t
120 dbind_gather_alloc_info_r (const char **type)
121 {
122   char t = **type;
123   (*type)++;
124   if (t == DBUS_TYPE_ARRAY)
125     {
126       switch (**type)
127         {
128           case DBUS_STRUCT_BEGIN_CHAR:
129               while (**type != DBUS_STRUCT_END_CHAR && **type != '\0') (*type)++;
130               if (**type != '\0') (*type)++;
131               break;
132           case DBUS_DICT_ENTRY_BEGIN_CHAR:
133               while (**type != DBUS_DICT_ENTRY_END_CHAR && **type != '\0') (*type)++;
134               if (**type != '\0') (*type)++;
135               break;
136           case '\0':
137               break;
138           default:
139               (*type)++;
140               break;
141         }
142     }
143
144   switch (t) {
145     case DBUS_TYPE_BYTE:
146         return sizeof (char);
147     case DBUS_TYPE_BOOLEAN:
148         return sizeof (dbus_bool_t);
149     case DBUS_TYPE_INT16:
150     case DBUS_TYPE_UINT16:
151         return sizeof (dbus_int16_t);
152     case DBUS_TYPE_INT32:
153     case DBUS_TYPE_UINT32:
154         return sizeof (dbus_int32_t);
155     case DBUS_TYPE_INT64:
156     case DBUS_TYPE_UINT64:
157         return sizeof (dbus_int64_t);
158     case DBUS_TYPE_DOUBLE:
159         return sizeof (double);
160     /* ptr types */
161     case DBUS_TYPE_STRING:
162     case DBUS_TYPE_OBJECT_PATH:
163     case DBUS_TYPE_SIGNATURE:
164     case DBUS_TYPE_ARRAY:
165         return sizeof (void *);
166     case DBUS_STRUCT_BEGIN_CHAR: {
167                 int sum = 0, stralign;
168
169         stralign = dbind_find_c_alignment (*type - 1);
170
171         while (**type != DBUS_STRUCT_END_CHAR) {
172                         sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type));
173                         sum += dbind_gather_alloc_info_r (type);
174         }
175                 sum = ALIGN_VALUE (sum, stralign);
176
177         g_assert (**type == DBUS_STRUCT_END_CHAR);
178         (*type)++;
179
180                 return sum;
181     }
182     case DBUS_DICT_ENTRY_BEGIN_CHAR: {
183                 int sum = 0, stralign;
184
185         stralign = dbind_find_c_alignment (*type - 1);
186
187         while (**type != DBUS_DICT_ENTRY_END_CHAR) {
188                         sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type));
189                         sum += dbind_gather_alloc_info_r (type);
190         }
191                 sum = ALIGN_VALUE (sum, stralign);
192
193         g_assert (**type == DBUS_DICT_ENTRY_END_CHAR);
194         (*type)++;
195
196                 return sum;
197     }
198     case DBUS_TYPE_STRUCT:
199     case DBUS_TYPE_DICT_ENTRY:
200         warn_braces ();
201     default:
202         return 0;
203   }
204 }
205
206 static size_t
207 dbind_gather_alloc_info (const char *type)
208 {
209   return dbind_gather_alloc_info_r (&type);
210 }
211
212 /*---------------------------------------------------------------------------*/
213
214 static void
215 dbind_any_free_r (const char **type, void **data)
216 {
217 #ifdef DEBUG
218     fprintf (stderr, "any free '%c' to %p\n", **type, *data);
219 #endif
220
221     switch (**type) {
222     case DBIND_POD_CASES:
223         *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
224         (*type)++;
225         break;
226     case DBUS_TYPE_STRING:
227     case DBUS_TYPE_OBJECT_PATH:
228     case DBUS_TYPE_SIGNATURE:
229 #ifdef DEBUG
230         fprintf (stderr, "string free %p\n", **(void ***)data);
231 #endif
232         g_free (**(void ***)data);
233         *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
234         (*type)++;
235         break;
236     case DBUS_TYPE_ARRAY: {
237         int i;
238         GArray *vals = **(void ***)data;
239         size_t elem_size, elem_align;
240         const char *saved_child_type;
241
242         (*type)++;
243         saved_child_type = *type;
244
245         elem_size = dbind_gather_alloc_info (*type);
246         elem_align = dbind_find_c_alignment_r (type); 
247
248         for (i = 0; i < vals->len; i++) {
249             void *ptr = vals->data + elem_size * i;
250             *type = saved_child_type; /* rewind type info */
251             ptr = ALIGN_ADDRESS (ptr, elem_align);
252             dbind_any_free_r (type, &ptr);
253         }
254         g_array_free (vals, TRUE);
255         break;
256     }
257     case DBUS_STRUCT_BEGIN_CHAR: {
258                 gconstpointer data0 = *data;
259                 int offset = 0, stralign;
260
261         stralign = dbind_find_c_alignment (*type);
262         (*type)++;
263
264         offset = 0 ;
265         while (**type != DBUS_STRUCT_END_CHAR) {
266             const char *subt = *type;
267                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
268                         *data = PTR_PLUS (data0, offset);
269             dbind_any_free_r (type, data);
270             offset += dbind_gather_alloc_info (subt);
271         }
272
273                 offset = ALIGN_VALUE (offset, stralign);
274                 *data = PTR_PLUS (data0, offset);
275
276         g_assert (**type == DBUS_STRUCT_END_CHAR);
277         (*type)++;
278
279         break;
280     }
281     case DBUS_DICT_ENTRY_BEGIN_CHAR: {
282                 gconstpointer data0 = *data;
283                 int offset = 0, stralign;
284
285         stralign = dbind_find_c_alignment (*type);
286         (*type)++;
287
288         offset = 0 ;
289         while (**type != DBUS_DICT_ENTRY_END_CHAR) {
290             char *subt = *type;
291                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
292                         *data = PTR_PLUS (data0, offset);
293             dbind_any_free_r (type, data);
294             offset += dbind_gather_alloc_info (subt);
295         }
296
297                 offset = ALIGN_VALUE (offset, stralign);
298                 *data = PTR_PLUS (data0, offset);
299
300         g_assert (**type == DBUS_DICT_ENTRY_END_CHAR);
301         (*type)++;
302
303         break;
304     }
305     case DBUS_TYPE_STRUCT:
306     case DBUS_TYPE_DICT_ENTRY:
307         warn_braces ();
308         break;
309     }
310 }
311
312 /*---------------------------------------------------------------------------*/
313
314 void
315 dbind_any_marshal (DBusMessageIter *iter,
316                    const char           **type,
317                    void           **data)
318 {
319     size_t len;
320
321 #ifdef DEBUG
322     fprintf (stderr, "any marshal '%c' to %p\n", **type, *data);
323 #endif
324
325     switch (**type) {
326     case DBIND_POD_CASES:
327     case DBUS_TYPE_STRING:
328     case DBUS_TYPE_OBJECT_PATH:
329     case DBUS_TYPE_SIGNATURE:
330         len = dbind_gather_alloc_info (*type);
331         dbus_message_iter_append_basic (iter, **type, *data);
332         *data = ((guchar *)*data) + len;
333         (*type)++;
334         break;
335     case DBUS_TYPE_ARRAY: {
336         int i;
337         GArray *vals = **(void ***)data;
338         size_t elem_size, elem_align;
339         DBusMessageIter sub;
340         const char *saved_child_type;
341         char *child_type_string;
342
343         (*type)++;
344         saved_child_type = *type;
345
346         elem_size = dbind_gather_alloc_info (*type);
347         elem_align = dbind_find_c_alignment_r (type); 
348
349         /* wow this part of the API sucks too ... */
350         child_type_string = g_strndup (saved_child_type, *type - saved_child_type);
351         /* fprintf (stderr, "array child type '%s'\n", child_type_string); */
352         dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
353                                           child_type_string, &sub);
354         for (i = 0; i < vals->len; i++) {
355             void *ptr = vals->data + elem_size * i;
356             *type = saved_child_type; /* rewind type info */
357             ptr = ALIGN_ADDRESS (ptr, elem_align);
358             dbind_any_marshal (&sub, type, &ptr);
359         }
360
361         dbus_message_iter_close_container (iter, &sub);
362         g_free (child_type_string);
363         break;
364     }
365     case DBUS_STRUCT_BEGIN_CHAR: {
366                 gconstpointer data0 = *data;
367                 int offset = 0, stralign;
368         DBusMessageIter sub;
369
370         stralign = dbind_find_c_alignment (*type);
371
372         (*type)++;
373
374         dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &sub);
375
376         offset = 0 ;
377         while (**type != DBUS_STRUCT_END_CHAR) {
378             const char *subt = *type;
379                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
380                         *data = PTR_PLUS (data0, offset);
381             dbind_any_marshal (&sub, type, data);
382             offset += dbind_gather_alloc_info (subt);
383         }
384
385                 offset = ALIGN_VALUE (offset, stralign);
386                 *data = PTR_PLUS (data0, offset);
387
388         dbus_message_iter_close_container (iter, &sub);
389
390         g_assert (**type == DBUS_STRUCT_END_CHAR);
391         (*type)++;
392
393         break;
394     }
395     case DBUS_DICT_ENTRY_BEGIN_CHAR: {
396                 gconstpointer data0 = *data;
397                 int offset = 0, stralign;
398         DBusMessageIter sub;
399
400         stralign = dbind_find_c_alignment (*type);
401
402         (*type)++;
403
404         dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub);
405
406         offset = 0 ;
407         while (**type != DBUS_DICT_ENTRY_END_CHAR) {
408             char *subt = *type;
409                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
410                         *data = PTR_PLUS (data0, offset);
411             dbind_any_marshal (&sub, type, data);
412             offset += dbind_gather_alloc_info (subt);
413         }
414
415                 offset = ALIGN_VALUE (offset, stralign);
416                 *data = PTR_PLUS (data0, offset);
417
418         dbus_message_iter_close_container (iter, &sub);
419
420         g_assert (**type == DBUS_DICT_ENTRY_END_CHAR);
421         (*type)++;
422
423         break;
424     }
425     case DBUS_TYPE_STRUCT:
426     case DBUS_TYPE_DICT_ENTRY:
427         warn_braces ();
428         break;
429     }
430 }
431
432 /*---------------------------------------------------------------------------*/
433
434 void
435 dbind_any_marshal_va (DBusMessageIter *iter,
436                       const char           **arg_types,
437                       va_list          args)
438 {
439     const char *p = *arg_types;
440
441     /* Guard against null arg types 
442        Fix for - http://bugs.freedesktop.org/show_bug.cgi?id=23027
443      */
444     if (p == NULL)
445         p = "";
446
447     {
448         /* special case base-types since we need to walk the stack worse-luck */
449         for (;*p != '\0' && *p != '=';) {
450             int intarg;
451             void *ptrarg;
452             double doublearg;
453             dbus_int64_t int64arg;
454             void *arg = NULL;
455
456             switch (*p) {
457             case DBUS_TYPE_BYTE:
458             case DBUS_TYPE_BOOLEAN:
459             case DBUS_TYPE_INT16:
460             case DBUS_TYPE_UINT16:
461             case DBUS_TYPE_INT32:
462             case DBUS_TYPE_UINT32:
463                 intarg = va_arg (args, int);
464                 arg = &intarg;
465                 break;
466             case DBUS_TYPE_INT64:
467             case DBUS_TYPE_UINT64:
468                 int64arg = va_arg (args, dbus_int64_t);
469                 arg = &int64arg;
470                 break;
471             case DBUS_TYPE_DOUBLE:
472                 doublearg = va_arg (args, double);
473                 arg = &doublearg;
474                 break;
475             /* ptr types */
476             case DBUS_TYPE_STRING:
477             case DBUS_TYPE_OBJECT_PATH:
478             case DBUS_TYPE_SIGNATURE:
479             case DBUS_TYPE_ARRAY:
480             case DBUS_TYPE_DICT_ENTRY:
481                 ptrarg = va_arg (args, void *);
482                 arg = &ptrarg;
483                 break;
484             case DBUS_STRUCT_BEGIN_CHAR:
485                 ptrarg = va_arg (args, void *);
486                 arg = ptrarg;
487                 break;
488             case DBUS_DICT_ENTRY_BEGIN_CHAR:
489                 ptrarg = va_arg (args, void *);
490                 arg = ptrarg;
491                 break;
492
493             case DBUS_TYPE_VARIANT:
494                 fprintf (stderr, "No variant support yet - very toolkit specific\n");
495                 ptrarg = va_arg (args, void *);
496                 arg = &ptrarg;
497                 break;
498             default:
499                 fprintf (stderr, "Unknown / invalid arg type %c\n", *p);
500                 break;
501             }
502             if (arg != NULL)
503                 dbind_any_marshal (iter, &p, &arg);
504             }
505     }
506 }
507
508 /*---------------------------------------------------------------------------*/
509
510 void
511 dbind_any_demarshal (DBusMessageIter *iter,
512                      const char           **type,
513                      void           **data)
514 {
515     size_t len;
516
517 #ifdef DEBUG
518     fprintf (stderr, "any demarshal '%c' to %p\n", **type, *data);
519 #endif
520
521     switch (**type) {
522     case DBIND_POD_CASES:
523         len = dbind_gather_alloc_info (*type);
524         dbus_message_iter_get_basic (iter, *data);
525         *data = ((guchar *)*data) + len;
526         (*type)++;
527         break;
528     case DBUS_TYPE_STRING:
529     case DBUS_TYPE_OBJECT_PATH:
530     case DBUS_TYPE_SIGNATURE:
531         len = dbind_gather_alloc_info (*type);
532         dbus_message_iter_get_basic (iter, *data);
533 #ifdef DEBUG
534         fprintf (stderr, "dup string '%s' (%p)\n", **(void ***)data, **(void ***)data);
535 #endif
536         **(void ***)data = g_strdup (**(void ***)data);
537         *data = ((guchar *)*data) + len;
538         (*type)++;
539         break;
540     case DBUS_TYPE_ARRAY: {
541         GArray *vals;
542         DBusMessageIter child;
543         size_t elem_size, elem_align;
544         const char *stored_child_type;
545         int i;
546
547         (*type)++;
548         stored_child_type = *type;
549
550         elem_size = dbind_gather_alloc_info (*type);
551         elem_align = dbind_find_c_alignment_r (type);
552         vals = g_array_new (FALSE, FALSE, elem_size);
553         (**(void ***)data) = vals;
554         *data = ((guchar *)*data) + sizeof (void *);
555
556         i = 0;
557         dbus_message_iter_recurse (iter, &child);
558         while (dbus_message_iter_get_arg_type (&child) != DBUS_TYPE_INVALID) {
559             void *ptr;
560             const char *subt = stored_child_type;
561             g_array_set_size (vals, i + 1);
562             ptr = vals->data + elem_size * i;
563             ptr = ALIGN_ADDRESS (ptr, elem_align);
564             dbind_any_demarshal (&child, &subt, &ptr);
565             i++;
566         };
567         break;
568     }
569     case DBUS_STRUCT_BEGIN_CHAR: {
570                 gconstpointer data0 = *data;
571                 int offset = 0, stralign;
572         DBusMessageIter child;
573
574         stralign = dbind_find_c_alignment (*type);
575
576         (*type)++;
577
578         dbus_message_iter_recurse (iter, &child);
579
580         while (**type != DBUS_STRUCT_END_CHAR) {
581             const char *subt = *type;
582                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
583                         *data = PTR_PLUS (data0, offset);
584             dbind_any_demarshal (&child, type, data);
585             offset += dbind_gather_alloc_info (subt);
586         }
587
588                 offset = ALIGN_VALUE (offset, stralign);
589                 *data = PTR_PLUS (data0, offset);
590
591         g_assert (**type == DBUS_STRUCT_END_CHAR);
592         (*type)++;
593
594         break;
595     }
596     case DBUS_DICT_ENTRY_BEGIN_CHAR: {
597                 gconstpointer data0 = *data;
598                 int offset = 0, stralign;
599         DBusMessageIter child;
600
601         stralign = dbind_find_c_alignment (*type);
602
603         (*type)++;
604
605         dbus_message_iter_recurse (iter, &child);
606
607         while (**type != DBUS_DICT_ENTRY_END_CHAR) {
608             char *subt = *type;
609                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
610                         *data = PTR_PLUS (data0, offset);
611             dbind_any_demarshal (&child, type, data);
612             offset += dbind_gather_alloc_info (subt);
613         }
614
615                 offset = ALIGN_VALUE (offset, stralign);
616                 *data = PTR_PLUS (data0, offset);
617
618         g_assert (**type == DBUS_DICT_ENTRY_END_CHAR);
619         (*type)++;
620
621         break;
622     }
623     case DBUS_TYPE_STRUCT:
624     case DBUS_TYPE_DICT_ENTRY:
625         warn_braces ();
626         break;
627     }
628     dbus_message_iter_next (iter);
629 }
630
631 /*---------------------------------------------------------------------------*/
632
633 void
634 dbind_any_demarshal_va (DBusMessageIter *iter,
635                         const char           **arg_types,
636                         va_list          args)
637 {
638     const char *p = *arg_types;
639     for (;*p != '\0';) {
640         void *arg = va_arg (args, void *);
641         dbind_any_demarshal (iter, &p, &arg);
642     }
643 }
644
645 /*---------------------------------------------------------------------------*/
646
647 /* nice deep free ... */
648 void
649 dbind_any_free (const char *type,
650                 void *ptr)
651 {
652     dbind_any_free_r (&type, &ptr);
653 }
654
655 /* should this be the default normalization ? */
656 void
657 dbind_any_free_ptr (const char *type, void *ptr)
658 {
659     dbind_any_free (type, &ptr);
660 }
661
662 /*---------------------------------------------------------------------------*/
663
664 unsigned int
665 dbind_find_c_alignment (const char *type)
666 {
667     return dbind_find_c_alignment_r (&type);
668 }
669
670 /*END------------------------------------------------------------------------*/