2009-06-11 Mark Doffman <mark.doffman@codethink.co.uk>
[platform/core/uifw/at-spi2-atk.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 (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 #if DBIND_ALIGNOF_DBIND_STRUCT > 1
85                 retval = MAX (retval, DBIND_ALIGNOF_DBIND_STRUCT);
86 #endif
87         while (**type != DBUS_STRUCT_END_CHAR) {
88             int elem_align = dbind_find_c_alignment_r (type);
89                         retval = MAX (retval, elem_align);
90         }
91         (*type)++;
92         return retval;
93     case DBUS_TYPE_STRUCT:
94     case DBUS_TYPE_DICT_ENTRY:
95         warn_braces ();
96         return DBIND_ALIGNOF_DBIND_POINTER;
97     case '\0':
98         g_assert_not_reached();
99         break;
100     default:
101                 return 1;
102   }
103 }
104
105 /*---------------------------------------------------------------------------*/
106
107 /* gather immediate allocation information for this type */
108 static size_t
109 dbind_gather_alloc_info_r (char **type)
110 {
111   char t = **type;
112   (*type)++;
113   if (t == DBUS_TYPE_ARRAY)
114     {
115       switch (**type)
116         {
117           case DBUS_STRUCT_BEGIN_CHAR:
118               while (**type != DBUS_STRUCT_END_CHAR && **type != '\0') (*type)++;
119               if (**type != '\0') (*type)++;
120               break;
121           case '\0':
122               break;
123           default:
124               (*type)++;
125               break;
126         }
127     }
128
129   switch (t) {
130     case DBUS_TYPE_BYTE:
131         return sizeof (char);
132     case DBUS_TYPE_BOOLEAN:
133         return sizeof (dbus_bool_t);
134     case DBUS_TYPE_INT16:
135     case DBUS_TYPE_UINT16:
136         return sizeof (dbus_int16_t);
137     case DBUS_TYPE_INT32:
138     case DBUS_TYPE_UINT32:
139         return sizeof (dbus_int32_t);
140     case DBUS_TYPE_INT64:
141     case DBUS_TYPE_UINT64:
142         return sizeof (dbus_int64_t);
143     case DBUS_TYPE_DOUBLE:
144         return sizeof (double);
145     /* ptr types */
146     case DBUS_TYPE_STRING:
147     case DBUS_TYPE_OBJECT_PATH:
148     case DBUS_TYPE_SIGNATURE:
149     case DBUS_TYPE_ARRAY:
150         return sizeof (void *);
151     case DBUS_STRUCT_BEGIN_CHAR: {
152                 int sum = 0, stralign;
153
154         stralign = dbind_find_c_alignment (*type - 1);
155
156         while (**type != DBUS_STRUCT_END_CHAR) {
157                         sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type));
158                         sum += dbind_gather_alloc_info_r (type);
159         }
160                 sum = ALIGN_VALUE (sum, stralign);
161
162         g_assert (**type == DBUS_STRUCT_END_CHAR);
163         (*type)++;
164
165                 return sum;
166     }
167     case DBUS_TYPE_STRUCT:
168     case DBUS_TYPE_DICT_ENTRY:
169         warn_braces ();
170     default:
171         return 0;
172   }
173 }
174
175 static size_t
176 dbind_gather_alloc_info (char *type)
177 {
178   return dbind_gather_alloc_info_r (&type);
179 }
180
181 /*---------------------------------------------------------------------------*/
182
183 static void
184 dbind_any_free_r (char **type, void **data)
185 {
186 #ifdef DEBUG
187     fprintf (stderr, "any free '%c' to %p\n", **type, *data);
188 #endif
189
190     switch (**type) {
191     case DBIND_POD_CASES:
192         *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
193         (*type)++;
194         break;
195     case DBUS_TYPE_STRING:
196     case DBUS_TYPE_OBJECT_PATH:
197     case DBUS_TYPE_SIGNATURE:
198 #ifdef DEBUG
199         fprintf (stderr, "string free %p\n", **(void ***)data);
200 #endif
201         g_free (**(void ***)data);
202         *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
203         (*type)++;
204         break;
205     case DBUS_TYPE_ARRAY: {
206         int i;
207         GArray *vals = **(void ***)data;
208         size_t elem_size, elem_align;
209         char *saved_child_type;
210
211         (*type)++;
212         saved_child_type = *type;
213
214         elem_size = dbind_gather_alloc_info (*type);
215         elem_align = dbind_find_c_alignment_r (type); 
216
217         for (i = 0; i < vals->len; i++) {
218             void *ptr = vals->data + elem_size * i;
219             *type = saved_child_type; /* rewind type info */
220             ptr = ALIGN_ADDRESS (ptr, elem_align);
221             dbind_any_free_r (type, &ptr);
222         }
223         g_array_free (vals, TRUE);
224         break;
225     }
226     case DBUS_STRUCT_BEGIN_CHAR: {
227                 gconstpointer data0 = *data;
228                 int offset = 0, stralign;
229
230         stralign = dbind_find_c_alignment (*type);
231         (*type)++;
232
233         offset = 0 ;
234         while (**type != DBUS_STRUCT_END_CHAR) {
235             char *subt = *type;
236                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
237                         *data = PTR_PLUS (data0, offset);
238             dbind_any_free_r (type, data);
239             offset += dbind_gather_alloc_info (subt);
240         }
241
242                 offset = ALIGN_VALUE (offset, stralign);
243                 *data = PTR_PLUS (data0, offset);
244
245         g_assert (**type == DBUS_STRUCT_END_CHAR);
246         (*type)++;
247
248         break;
249     }
250     case DBUS_TYPE_STRUCT:
251     case DBUS_TYPE_DICT_ENTRY:
252         warn_braces ();
253         break;
254     }
255 }
256
257 /*---------------------------------------------------------------------------*/
258
259 void
260 dbind_any_marshal (DBusMessageIter *iter,
261                    char           **type,
262                    void           **data)
263 {
264     size_t len;
265
266 #ifdef DEBUG
267     fprintf (stderr, "any marshal '%c' to %p\n", **type, *data);
268 #endif
269
270     switch (**type) {
271     case DBIND_POD_CASES:
272     case DBUS_TYPE_STRING:
273     case DBUS_TYPE_OBJECT_PATH:
274     case DBUS_TYPE_SIGNATURE:
275         len = dbind_gather_alloc_info (*type);
276         dbus_message_iter_append_basic (iter, **type, *data);
277         *data = ((guchar *)*data) + len;
278         (*type)++;
279         break;
280     case DBUS_TYPE_ARRAY: {
281         int i;
282         GArray *vals = **(void ***)data;
283         size_t elem_size, elem_align;
284         DBusMessageIter sub;
285         char *saved_child_type, *child_type_string;
286
287         (*type)++;
288         saved_child_type = *type;
289
290         elem_size = dbind_gather_alloc_info (*type);
291         elem_align = dbind_find_c_alignment_r (type); 
292
293         /* wow this part of the API sucks too ... */
294         child_type_string = g_strndup (saved_child_type, *type - saved_child_type);
295         /* fprintf (stderr, "array child type '%s'\n", child_type_string); */
296         dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
297                                           child_type_string, &sub);
298         for (i = 0; i < vals->len; i++) {
299             void *ptr = vals->data + elem_size * i;
300             *type = saved_child_type; /* rewind type info */
301             ptr = ALIGN_ADDRESS (ptr, elem_align);
302             dbind_any_marshal (&sub, type, &ptr);
303         }
304
305         dbus_message_iter_close_container (iter, &sub);
306         g_free (child_type_string);
307         break;
308     }
309     case DBUS_STRUCT_BEGIN_CHAR: {
310                 gconstpointer data0 = *data;
311                 int offset = 0, stralign;
312         DBusMessageIter sub;
313
314         stralign = dbind_find_c_alignment (*type);
315
316         (*type)++;
317
318         dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &sub);
319
320         offset = 0 ;
321         while (**type != DBUS_STRUCT_END_CHAR) {
322             char *subt = *type;
323                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
324                         *data = PTR_PLUS (data0, offset);
325             dbind_any_marshal (&sub, type, data);
326             offset += dbind_gather_alloc_info (subt);
327         }
328
329                 offset = ALIGN_VALUE (offset, stralign);
330                 *data = PTR_PLUS (data0, offset);
331
332         dbus_message_iter_close_container (iter, &sub);
333
334         g_assert (**type == DBUS_STRUCT_END_CHAR);
335         (*type)++;
336
337         break;
338     }
339     case DBUS_TYPE_STRUCT:
340     case DBUS_TYPE_DICT_ENTRY:
341         warn_braces ();
342         break;
343     }
344 }
345
346 /*---------------------------------------------------------------------------*/
347
348 void
349 dbind_any_marshal_va (DBusMessageIter *iter,
350                       char           **arg_types,
351                       va_list          args)
352 {
353     char *p = *arg_types;
354     {
355         /* special case base-types since we need to walk the stack worse-luck */
356         for (;*p != '\0' && *p != '=';) {
357             int intarg;
358             void *ptrarg;
359             double doublearg;
360             dbus_int64_t int64arg;
361             void *arg = NULL;
362
363             switch (*p) {
364             case DBUS_TYPE_BYTE:
365             case DBUS_TYPE_BOOLEAN:
366             case DBUS_TYPE_INT16:
367             case DBUS_TYPE_UINT16:
368             case DBUS_TYPE_INT32:
369             case DBUS_TYPE_UINT32:
370                 intarg = va_arg (args, int);
371                 arg = &intarg;
372                 break;
373             case DBUS_TYPE_INT64:
374             case DBUS_TYPE_UINT64:
375                 int64arg = va_arg (args, dbus_int64_t);
376                 arg = &int64arg;
377                 break;
378             case DBUS_TYPE_DOUBLE:
379                 doublearg = va_arg (args, double);
380                 arg = &doublearg;
381                 break;
382             /* ptr types */
383             case DBUS_TYPE_STRING:
384             case DBUS_TYPE_OBJECT_PATH:
385             case DBUS_TYPE_SIGNATURE:
386             case DBUS_TYPE_ARRAY:
387             case DBUS_TYPE_DICT_ENTRY:
388                 ptrarg = va_arg (args, void *);
389                 arg = &ptrarg;
390                 break;
391             case DBUS_STRUCT_BEGIN_CHAR:
392                 ptrarg = va_arg (args, void *);
393                 arg = ptrarg;
394                 break;
395
396             case DBUS_TYPE_VARIANT:
397                 fprintf (stderr, "No variant support yet - very toolkit specific\n");
398                 ptrarg = va_arg (args, void *);
399                 arg = &ptrarg;
400                 break;
401             default:
402                 fprintf (stderr, "Unknown / invalid arg type %c\n", *p);
403                 break;
404             }
405             if (arg != NULL)
406                 dbind_any_marshal (iter, &p, &arg);
407             }
408     }
409 }
410
411 /*---------------------------------------------------------------------------*/
412
413 void
414 dbind_any_demarshal (DBusMessageIter *iter,
415                      char           **type,
416                      void           **data)
417 {
418     size_t len;
419
420 #ifdef DEBUG
421     fprintf (stderr, "any demarshal '%c' to %p\n", **type, *data);
422 #endif
423
424     switch (**type) {
425     case DBIND_POD_CASES:
426         len = dbind_gather_alloc_info (*type);
427         dbus_message_iter_get_basic (iter, *data);
428         *data = ((guchar *)*data) + len;
429         (*type)++;
430         break;
431     case DBUS_TYPE_STRING:
432     case DBUS_TYPE_OBJECT_PATH:
433     case DBUS_TYPE_SIGNATURE:
434         len = dbind_gather_alloc_info (*type);
435         dbus_message_iter_get_basic (iter, *data);
436 #ifdef DEBUG
437         fprintf (stderr, "dup string '%s' (%p)\n", **(void ***)data, **(void ***)data);
438 #endif
439         **(void ***)data = g_strdup (**(void ***)data);
440         *data = ((guchar *)*data) + len;
441         (*type)++;
442         break;
443     case DBUS_TYPE_ARRAY: {
444         GArray *vals;
445         DBusMessageIter child;
446         size_t elem_size, elem_align;
447         char *stored_child_type;
448         int i;
449
450         (*type)++;
451         stored_child_type = *type;
452
453         elem_size = dbind_gather_alloc_info (*type);
454         elem_align = dbind_find_c_alignment_r (type);
455         vals = g_array_new (FALSE, FALSE, elem_size);
456         (**(void ***)data) = vals;
457         *data = ((guchar *)*data) + sizeof (void *);
458
459         i = 0;
460         dbus_message_iter_recurse (iter, &child);
461         while (dbus_message_iter_get_arg_type (&child) != DBUS_TYPE_INVALID) {
462             void *ptr;
463             char *subt = stored_child_type;
464             g_array_set_size (vals, i + 1);
465             ptr = vals->data + elem_size * i;
466             ptr = ALIGN_ADDRESS (ptr, elem_align);
467             dbind_any_demarshal (&child, &subt, &ptr);
468             i++;
469         };
470         break;
471     }
472     case DBUS_STRUCT_BEGIN_CHAR: {
473                 gconstpointer data0 = *data;
474                 int offset = 0, stralign;
475         DBusMessageIter child;
476
477         stralign = dbind_find_c_alignment (*type);
478
479         (*type)++;
480
481         dbus_message_iter_recurse (iter, &child);
482
483         while (**type != DBUS_STRUCT_END_CHAR) {
484             char *subt = *type;
485                         offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
486                         *data = PTR_PLUS (data0, offset);
487             dbind_any_demarshal (&child, type, data);
488             offset += dbind_gather_alloc_info (subt);
489         }
490
491                 offset = ALIGN_VALUE (offset, stralign);
492                 *data = PTR_PLUS (data0, offset);
493
494         g_assert (**type == DBUS_STRUCT_END_CHAR);
495         (*type)++;
496
497         break;
498     }
499     case DBUS_TYPE_STRUCT:
500     case DBUS_TYPE_DICT_ENTRY:
501         warn_braces ();
502         break;
503     }
504     dbus_message_iter_next (iter);
505 }
506
507 /*---------------------------------------------------------------------------*/
508
509 void
510 dbind_any_demarshal_va (DBusMessageIter *iter,
511                         char           **arg_types,
512                         va_list          args)
513 {
514     char *p = *arg_types;
515     for (;*p != '\0';) {
516         void *arg = va_arg (args, void *);
517         dbind_any_demarshal (iter, &p, &arg);
518     }
519 }
520
521 /*---------------------------------------------------------------------------*/
522
523 /* nice deep free ... */
524 void
525 dbind_any_free (char *type,
526                 void *ptr)
527 {
528     dbind_any_free_r (&type, &ptr);
529 }
530
531 /* should this be the default normalization ? */
532 void
533 dbind_any_free_ptr (char *type, void *ptr)
534 {
535     dbind_any_free (type, &ptr);
536 }
537
538 /*---------------------------------------------------------------------------*/
539
540 unsigned int
541 dbind_find_c_alignment (char *type)
542 {
543     return dbind_find_c_alignment_r (&type);
544 }
545
546 /*END------------------------------------------------------------------------*/