/* * Copyright 2008-2011 Novell, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ /* type driven marshalling */ #include #include #include "config.h" #include "dbind-any.h" #undef DEBUG /* Align a value upward to a boundary, expressed as a number of bytes. * E.g. align to an 8-byte boundary with argument of 8. * * (this + boundary - 1) * & * ~(boundary - 1) */ #define ALIGN_VALUE(this, boundary) \ ((((gulong) (this)) + (((gulong) (boundary)) - 1)) & (~(((gulong) (boundary)) - 1))) #define ALIGN_ADDRESS(this, boundary) \ ((gpointer) ALIGN_VALUE (this, boundary)) #define PTR_PLUS(ptr, offset) \ ((gpointer) (((guchar *) (ptr)) + (offset))) #define DBIND_POD_CASES \ DBUS_TYPE_BYTE: \ case DBUS_TYPE_INT16: \ case DBUS_TYPE_UINT16: \ case DBUS_TYPE_INT32: \ case DBUS_TYPE_UINT32: \ case DBUS_TYPE_BOOLEAN: \ case DBUS_TYPE_INT64: \ case DBUS_TYPE_UINT64: \ case DBUS_TYPE_DOUBLE /*---------------------------------------------------------------------------*/ static void warn_braces () { fprintf (stderr, "Error: dbus flags structures & dicts with braces rather than " " an explicit type member of 'struct'\n"); } /*---------------------------------------------------------------------------*/ static unsigned int dbind_find_c_alignment_r (const char **type) { unsigned int retval = 1; char t = **type; (*type)++; #ifdef DEBUG fprintf (stderr, "\tfind align for %c (0x%x)\n", t, t); #endif switch (t) { case DBUS_TYPE_BYTE: return ALIGNOF_CHAR; case DBUS_TYPE_BOOLEAN: return ALIGNOF_DBUS_BOOL_T; case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return ALIGNOF_DBUS_INT16_T; case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return ALIGNOF_DBUS_INT32_T; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return ALIGNOF_DBUS_INT64_T; case DBUS_TYPE_DOUBLE: return ALIGNOF_DOUBLE; /* ptr types */ case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: case DBUS_TYPE_ARRAY: return ALIGNOF_DBIND_POINTER; case DBUS_STRUCT_BEGIN_CHAR: /* TODO: I think this would break with a nested struct */ #if ALIGNOF_DBIND_STRUCT > 1 retval = MAX (retval, ALIGNOF_DBIND_STRUCT); #endif while (**type != DBUS_STRUCT_END_CHAR) { int elem_align = dbind_find_c_alignment_r (type); retval = MAX (retval, elem_align); } (*type)++; return retval; case DBUS_DICT_ENTRY_BEGIN_CHAR: #if ALIGNOF_DBIND_STRUCT > 1 retval = MAX (retval, ALIGNOF_DBIND_STRUCT); #endif while (**type != DBUS_DICT_ENTRY_END_CHAR) { int elem_align = dbind_find_c_alignment_r (type); retval = MAX (retval, elem_align); } (*type)++; return retval; case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: warn_braces (); return ALIGNOF_DBIND_POINTER; case '\0': g_assert_not_reached (); break; default: return 1; } } /*---------------------------------------------------------------------------*/ /* gather immediate allocation information for this type */ static size_t dbind_gather_alloc_info_r (const char **type) { char t = **type; (*type)++; if (t == DBUS_TYPE_ARRAY) { switch (**type) { case DBUS_STRUCT_BEGIN_CHAR: while (**type != DBUS_STRUCT_END_CHAR && **type != '\0') (*type)++; if (**type != '\0') (*type)++; break; case DBUS_DICT_ENTRY_BEGIN_CHAR: while (**type != DBUS_DICT_ENTRY_END_CHAR && **type != '\0') (*type)++; if (**type != '\0') (*type)++; break; case '\0': break; default: (*type)++; break; } } switch (t) { case DBUS_TYPE_BYTE: return sizeof (char); case DBUS_TYPE_BOOLEAN: return sizeof (dbus_bool_t); case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: return sizeof (dbus_int16_t); case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: return sizeof (dbus_int32_t); case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: return sizeof (dbus_int64_t); case DBUS_TYPE_DOUBLE: return sizeof (double); /* ptr types */ case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: case DBUS_TYPE_ARRAY: return sizeof (void *); case DBUS_STRUCT_BEGIN_CHAR: { int sum = 0, stralign; stralign = dbind_find_c_alignment (*type - 1); while (**type != DBUS_STRUCT_END_CHAR) { sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type)); sum += dbind_gather_alloc_info_r (type); } sum = ALIGN_VALUE (sum, stralign); g_assert (**type == DBUS_STRUCT_END_CHAR); (*type)++; return sum; } case DBUS_DICT_ENTRY_BEGIN_CHAR: { int sum = 0, stralign; stralign = dbind_find_c_alignment (*type - 1); while (**type != DBUS_DICT_ENTRY_END_CHAR) { sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type)); sum += dbind_gather_alloc_info_r (type); } sum = ALIGN_VALUE (sum, stralign); g_assert (**type == DBUS_DICT_ENTRY_END_CHAR); (*type)++; return sum; } case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: warn_braces (); default: return 0; } } static size_t dbind_gather_alloc_info (const char *type) { return dbind_gather_alloc_info_r (&type); } /*---------------------------------------------------------------------------*/ static void dbind_any_free_r (const char **type, void **data) { #ifdef DEBUG fprintf (stderr, "any free '%c' to %p\n", **type, *data); #endif switch (**type) { case DBIND_POD_CASES: *data = ((guchar *) *data) + dbind_gather_alloc_info (*type); (*type)++; break; case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: #ifdef DEBUG fprintf (stderr, "string free %p\n", **(void ***) data); #endif g_free (**(void ***) data); *data = ((guchar *) *data) + dbind_gather_alloc_info (*type); (*type)++; break; case DBUS_TYPE_ARRAY: { int i; GArray *vals = **(void ***) data; size_t elem_size, elem_align; const char *saved_child_type; (*type)++; saved_child_type = *type; elem_size = dbind_gather_alloc_info (*type); elem_align = dbind_find_c_alignment_r (type); for (i = 0; i < vals->len; i++) { void *ptr = vals->data + elem_size * i; *type = saved_child_type; /* rewind type info */ ptr = ALIGN_ADDRESS (ptr, elem_align); dbind_any_free_r (type, &ptr); } g_array_free (vals, TRUE); break; } case DBUS_STRUCT_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; stralign = dbind_find_c_alignment (*type); (*type)++; offset = 0; while (**type != DBUS_STRUCT_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_free_r (type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); g_assert (**type == DBUS_STRUCT_END_CHAR); (*type)++; break; } case DBUS_DICT_ENTRY_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; stralign = dbind_find_c_alignment (*type); (*type)++; offset = 0; while (**type != DBUS_DICT_ENTRY_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_free_r (type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); g_assert (**type == DBUS_DICT_ENTRY_END_CHAR); (*type)++; break; } case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: warn_braces (); break; } } /*---------------------------------------------------------------------------*/ void dbind_any_marshal (DBusMessageIter *iter, const char **type, void **data) { size_t len; #ifdef DEBUG fprintf (stderr, "any marshal '%c' to %p\n", **type, *data); #endif switch (**type) { case DBIND_POD_CASES: case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: len = dbind_gather_alloc_info (*type); dbus_message_iter_append_basic (iter, **type, *data); *data = ((guchar *) *data) + len; (*type)++; break; case DBUS_TYPE_ARRAY: { int i; GArray *vals = **(void ***) data; size_t elem_size, elem_align; DBusMessageIter sub; const char *saved_child_type; char *child_type_string; (*type)++; saved_child_type = *type; elem_size = dbind_gather_alloc_info (*type); elem_align = dbind_find_c_alignment_r (type); /* wow this part of the API sucks too ... */ child_type_string = g_strndup (saved_child_type, *type - saved_child_type); /* fprintf (stderr, "array child type '%s'\n", child_type_string); */ dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, child_type_string, &sub); for (i = 0; i < vals->len; i++) { void *ptr = vals->data + elem_size * i; *type = saved_child_type; /* rewind type info */ ptr = ALIGN_ADDRESS (ptr, elem_align); dbind_any_marshal (&sub, type, &ptr); } dbus_message_iter_close_container (iter, &sub); g_free (child_type_string); break; } case DBUS_STRUCT_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; DBusMessageIter sub; stralign = dbind_find_c_alignment (*type); (*type)++; dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &sub); offset = 0; while (**type != DBUS_STRUCT_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_marshal (&sub, type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); dbus_message_iter_close_container (iter, &sub); g_assert (**type == DBUS_STRUCT_END_CHAR); (*type)++; break; } case DBUS_DICT_ENTRY_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; DBusMessageIter sub; stralign = dbind_find_c_alignment (*type); (*type)++; dbus_message_iter_open_container (iter, DBUS_TYPE_DICT_ENTRY, NULL, &sub); offset = 0; while (**type != DBUS_DICT_ENTRY_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_marshal (&sub, type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); dbus_message_iter_close_container (iter, &sub); g_assert (**type == DBUS_DICT_ENTRY_END_CHAR); (*type)++; break; } case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: warn_braces (); break; } } /*---------------------------------------------------------------------------*/ void dbind_any_marshal_va (DBusMessageIter *iter, const char **arg_types, va_list args) { const char *p = *arg_types; /* Guard against null arg types Fix for - http://bugs.freedesktop.org/show_bug.cgi?id=23027 */ if (p == NULL) p = ""; { /* special case base-types since we need to walk the stack worse-luck */ for (; *p != '\0' && *p != '=';) { int intarg; void *ptrarg; double doublearg; dbus_int64_t int64arg; void *arg = NULL; switch (*p) { case DBUS_TYPE_BYTE: case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: intarg = va_arg (args, int); arg = &intarg; break; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: int64arg = va_arg (args, dbus_int64_t); arg = &int64arg; break; case DBUS_TYPE_DOUBLE: doublearg = va_arg (args, double); arg = &doublearg; break; /* ptr types */ case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: case DBUS_TYPE_ARRAY: case DBUS_TYPE_DICT_ENTRY: ptrarg = va_arg (args, void *); arg = &ptrarg; break; case DBUS_STRUCT_BEGIN_CHAR: ptrarg = va_arg (args, void *); arg = ptrarg; break; case DBUS_DICT_ENTRY_BEGIN_CHAR: ptrarg = va_arg (args, void *); arg = ptrarg; break; case DBUS_TYPE_VARIANT: fprintf (stderr, "No variant support yet - very toolkit specific\n"); ptrarg = va_arg (args, void *); arg = &ptrarg; break; default: fprintf (stderr, "Unknown / invalid arg type %c\n", *p); break; } if (arg != NULL) dbind_any_marshal (iter, &p, &arg); } if (*arg_types) *arg_types = p; } } /*---------------------------------------------------------------------------*/ void dbind_any_demarshal (DBusMessageIter *iter, const char **type, void **data) { size_t len; #ifdef DEBUG fprintf (stderr, "any demarshal '%c' to %p\n", **type, *data); #endif switch (**type) { case DBIND_POD_CASES: len = dbind_gather_alloc_info (*type); dbus_message_iter_get_basic (iter, *data); *data = ((guchar *) *data) + len; (*type)++; break; case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: len = dbind_gather_alloc_info (*type); dbus_message_iter_get_basic (iter, *data); #ifdef DEBUG fprintf (stderr, "dup string '%s' (%p)\n", **(void ***) data, **(void ***) data); #endif **(void ***) data = g_strdup (**(void ***) data); *data = ((guchar *) *data) + len; (*type)++; break; case DBUS_TYPE_ARRAY: { GArray *vals; DBusMessageIter child; size_t elem_size, elem_align; const char *stored_child_type; int i; (*type)++; stored_child_type = *type; elem_size = dbind_gather_alloc_info (*type); elem_align = dbind_find_c_alignment_r (type); vals = g_array_new (FALSE, FALSE, elem_size); (**(void ***) data) = vals; *data = ((guchar *) *data) + sizeof (void *); i = 0; dbus_message_iter_recurse (iter, &child); while (dbus_message_iter_get_arg_type (&child) != DBUS_TYPE_INVALID) { void *ptr; const char *subt = stored_child_type; g_array_set_size (vals, i + 1); ptr = vals->data + elem_size * i; ptr = ALIGN_ADDRESS (ptr, elem_align); dbind_any_demarshal (&child, &subt, &ptr); i++; }; break; } case DBUS_STRUCT_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; DBusMessageIter child; stralign = dbind_find_c_alignment (*type); (*type)++; dbus_message_iter_recurse (iter, &child); while (**type != DBUS_STRUCT_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_demarshal (&child, type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); g_assert (**type == DBUS_STRUCT_END_CHAR); (*type)++; break; } case DBUS_DICT_ENTRY_BEGIN_CHAR: { gconstpointer data0 = *data; int offset = 0, stralign; DBusMessageIter child; stralign = dbind_find_c_alignment (*type); (*type)++; dbus_message_iter_recurse (iter, &child); while (**type != DBUS_DICT_ENTRY_END_CHAR) { const char *subt = *type; offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type)); *data = PTR_PLUS (data0, offset); dbind_any_demarshal (&child, type, data); offset += dbind_gather_alloc_info (subt); } offset = ALIGN_VALUE (offset, stralign); *data = PTR_PLUS (data0, offset); g_assert (**type == DBUS_DICT_ENTRY_END_CHAR); (*type)++; break; case DBUS_TYPE_VARIANT: /* skip; unimplemented for now */ (*type)++; break; } case DBUS_TYPE_STRUCT: case DBUS_TYPE_DICT_ENTRY: warn_braces (); break; } dbus_message_iter_next (iter); } static const char * pass_complex_arg (const char *p, char begin, char end) { int level = 1; p++; while (*p && level > 0) { if (*p == begin) level++; else if (*p == end) level--; p++; } if (*p == end) p++; return p; } static const char * pass_arg (const char *p) { switch (*p) { case '(': return pass_complex_arg (p, '(', ')'); case '{': return pass_complex_arg (p, '{', '}'); case 'a': return pass_arg (p + 1); default: return p + 1; } } /*---------------------------------------------------------------------------*/ void dbind_any_demarshal_va (DBusMessageIter *iter, const char **arg_types, va_list args) { const char *p = *arg_types; /* Just consume the in args without doing anything to them */ for (; *p != '\0' && *p != '=';) { switch (*p) { case DBUS_TYPE_BYTE: case DBUS_TYPE_BOOLEAN: case DBUS_TYPE_INT16: case DBUS_TYPE_UINT16: case DBUS_TYPE_INT32: case DBUS_TYPE_UINT32: va_arg (args, int); break; case DBUS_TYPE_INT64: case DBUS_TYPE_UINT64: va_arg (args, dbus_int64_t); break; case DBUS_TYPE_DOUBLE: va_arg (args, double); break; /* ptr types */ case DBUS_TYPE_STRING: case DBUS_TYPE_OBJECT_PATH: case DBUS_TYPE_SIGNATURE: case DBUS_TYPE_ARRAY: case DBUS_TYPE_DICT_ENTRY: va_arg (args, void *); break; case DBUS_STRUCT_BEGIN_CHAR: va_arg (args, void *); break; case DBUS_DICT_ENTRY_BEGIN_CHAR: va_arg (args, void *); break; case DBUS_TYPE_VARIANT: fprintf (stderr, "No variant support yet - very toolkit specific\n"); va_arg (args, void *); break; default: fprintf (stderr, "Unknown / invalid arg type %c\n", *p); break; } p = pass_arg (p); } if (p[0] == '=' && p[1] == '>') p += 2; for (; *p != '\0';) { void *arg = va_arg (args, void *); dbind_any_demarshal (iter, &p, &arg); } } /*---------------------------------------------------------------------------*/ /* nice deep free ... */ void dbind_any_free (const char *type, void *ptr) { dbind_any_free_r (&type, &ptr); } /* should this be the default normalization ? */ void dbind_any_free_ptr (const char *type, void *ptr) { dbind_any_free (type, &ptr); } /*---------------------------------------------------------------------------*/ unsigned int dbind_find_c_alignment (const char *type) { return dbind_find_c_alignment_r (&type); } /*END------------------------------------------------------------------------*/