* 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., 59 Temple Place - Suite 330,
- * Boston, MA 02111-1307, USA.
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
*
* Author: Ryan Lortie <desrt@desrt.ca>
*/
#include <string.h>
-#include "galias.h"
/* GVariantSerialiser
*
* values is permitted (eg: 0 to 255 is a valid byte). Special checks
* need to be performed for booleans (only 0 or 1 allowed), strings
* (properly nul-terminated) and object paths and signature strings
- * (meeting the DBus specification requirements).
+ * (meeting the D-Bus specification requirements).
*/
/* < private >
* GVariantSerialised:
* @type_info: the #GVariantTypeInfo of this value
- * @data: the serialised data of this value, or %NULL
+ * @data: (allow-none): the serialised data of this value, or %NULL
* @size: the size of this value
*
* A structure representing a GVariant in serialised form. This
else
g_assert (serialised.size == 0 || serialised.data != NULL);
+ /* Depending on the native alignment requirements of the machine, the
+ * compiler will insert either 3 or 7 padding bytes after the char.
+ * This will result in the sizeof() the struct being 12 or 16.
+ * Subtract 9 to get 3 or 7 which is a nice bitmask to apply to get
+ * the alignment bits that we "care about" being zero: in the
+ * 4-aligned case, we care about 2 bits, and in the 8-aligned case, we
+ * care about 3 bits.
+ */
+ alignment &= sizeof (struct {
+ char a;
+ union {
+ guint64 x;
+ void *y;
+ gdouble z;
+ } b;
+ }
+ ) - 9;
+
+ /* Some OSes (FreeBSD is a known example) have a malloc() that returns
+ * unaligned memory if you request small sizes. 'malloc (1);', for
+ * example, has been seen to return pointers aligned to 6 mod 16.
+ *
+ * Check if this is a small allocation and return without enforcing
+ * the alignment assertion if this is the case.
+ */
+ if (serialised.size <= alignment)
+ return;
+
g_assert_cmpint (alignment & (gsize) serialised.data, ==, 0);
}
}
}
+static gsize
+gvs_fixed_sized_maybe_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ if (!n_children)
+ return 0;
+
+ return g_variant_callback_write_to_vectors (vectors, children[0], NULL);
+}
+
static gboolean
gvs_fixed_sized_maybe_is_normal (GVariantSerialised value)
{
{
if (n_children)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gvs_filler (&child, children[0]);
}
}
+static void
+gvs_variable_sized_maybe_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ if (n_children)
+ {
+ g_variant_callback_write_to_vectors (vectors, children[0], NULL);
+ g_variant_vectors_append_copy (vectors, "", 1);
+ }
+}
+
static gboolean
gvs_variable_sized_maybe_is_normal (GVariantSerialised value)
{
gvs_fixed_sized_array_get_child (GVariantSerialised value,
gsize index_)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_query (child.type_info, NULL, &child.size);
const gpointer *children,
gsize n_children)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gsize i;
child.type_info = g_variant_type_info_element (value.type_info);
}
}
+static void
+gvs_fixed_sized_array_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ gsize i;
+
+ for (i = 0; i < n_children; i++)
+ g_variant_callback_write_to_vectors (vectors, children[i], NULL);
+}
+
static gboolean
gvs_fixed_sized_array_is_normal (GVariantSerialised value)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
child.type_info = g_variant_type_info_element (value.type_info);
g_variant_type_info_query (child.type_info, NULL, &child.size);
* Variable sized arrays, containing variable-sized elements, must be
* able to determine the boundaries between the elements. The items
* cannot simply be concatenated. Additionally, we are faced with the
- * fact that non-fixed-sized values do not neccessarily have a size that
+ * fact that non-fixed-sized values do not necessarily have a size that
* is a multiple of their alignment requirement, so we may need to
* insert zero-filled padding.
*
* normal form and that is the one that the serialiser must produce.
*/
+/* bytes may be NULL if (size == 0). */
static inline gsize
gvs_read_unaligned_le (guchar *bytes,
guint size)
} tmpvalue;
tmpvalue.integer = 0;
- memcpy (&tmpvalue.bytes, bytes, size);
+ if (bytes != NULL)
+ memcpy (&tmpvalue.bytes, bytes, size);
return GSIZE_FROM_LE (tmpvalue.integer);
}
gvs_variable_sized_array_get_child (GVariantSerialised value,
gsize index_)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gsize offset_size;
gsize last_end;
gsize start;
for (i = 0; i < n_children; i++)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
offset += (-offset) & alignment;
gvs_filler (&child, children[i]);
for (i = 0; i < n_children; i++)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
while (offset & alignment)
value.data[offset++] = '\0';
}
}
+static void
+gvs_variable_sized_array_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ guint offset_key;
+ guint alignment;
+ gsize offset;
+ gsize i;
+
+ if (n_children == 0)
+ return;
+
+ offset_key = g_variant_vectors_reserve_offsets (vectors, n_children, gvs_get_offset_size (size));
+ g_variant_type_info_query (type_info, &alignment, NULL);
+ offset = 0;
+
+ for (i = 0; i < n_children; i++)
+ {
+ if ((-offset) & alignment)
+ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
+
+ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
+
+ g_variant_vectors_write_to_offsets (vectors, i, offset, offset_key);
+ }
+
+ g_variant_vectors_commit_offsets (vectors, offset_key);
+}
+
static gboolean
gvs_variable_sized_array_is_normal (GVariantSerialised value)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gsize offsets_array_size;
guchar *offsets_array;
guint offset_size;
gsize index_)
{
const GVariantMemberInfo *member_info;
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gsize offset_size;
gsize start, end;
/* tuples are the only (potentially) fixed-sized containers, so the
* only ones that have to deal with the possibility of having %NULL
- * data with a non-zero %size if errors occured elsewhere.
+ * data with a non-zero %size if errors occurred elsewhere.
*/
if G_UNLIKELY (value.data == NULL && value.size != 0)
{
child.size = fixed_size;
}
- else /* G_VARIANT_MEMEBER_ENDING_OFFSET */
+ else /* G_VARIANT_MEMBER_ENDING_OFFSET */
end = gvs_read_unaligned_le (value.data + value.size -
offset_size * (member_info->i + 2),
offset_size);
offset += fixed_size;
else
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
gvs_filler (&child, children[i]);
offset += child.size;
for (i = 0; i < n_children; i++)
{
const GVariantMemberInfo *member_info;
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
guint alignment;
member_info = g_variant_type_info_member_info (value.type_info, i);
value.data[offset++] = '\0';
}
+
+static void
+gvs_tuple_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ const GVariantMemberInfo *member_info = NULL;
+ gsize fixed_size;
+ gsize offset;
+ gsize i;
+
+ if (n_children == 0)
+ {
+ g_variant_vectors_append_copy (vectors, "", 1);
+ return;
+ }
+
+ g_variant_type_info_query (type_info, NULL, &fixed_size);
+ offset = 0;
+
+ if (!fixed_size)
+ {
+ gsize n_offsets;
+
+ member_info = g_variant_type_info_member_info (type_info, n_children - 1);
+ n_offsets = member_info->i + 1;
+
+ if (n_offsets)
+ {
+ gsize offset_key = 0;
+
+ offset_key = g_variant_vectors_reserve_offsets (vectors, n_offsets, gvs_get_offset_size (size));
+
+ for (i = 0; i < n_children; i++)
+ {
+ guint alignment;
+
+ member_info = g_variant_type_info_member_info (type_info, i);
+ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
+
+ if ((-offset) & alignment)
+ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
+
+ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
+
+ if (member_info->ending_type == G_VARIANT_MEMBER_ENDING_OFFSET)
+ g_variant_vectors_write_to_offsets (vectors, --n_offsets, offset, offset_key);
+ }
+
+ g_variant_vectors_commit_offsets (vectors, offset_key);
+ }
+ else
+ {
+ for (i = 0; i < n_children; i++)
+ {
+ guint alignment;
+
+ member_info = g_variant_type_info_member_info (type_info, i);
+ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
+
+ if ((-offset) & alignment)
+ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
+
+ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
+ }
+ }
+ }
+ else
+ {
+ for (i = 0; i < n_children; i++)
+ {
+ guint alignment;
+
+ member_info = g_variant_type_info_member_info (type_info, i);
+ g_variant_type_info_query (member_info->type_info, &alignment, NULL);
+
+ if ((-offset) & alignment)
+ offset += g_variant_vectors_append_pad (vectors, (-offset) & alignment);
+
+ offset += g_variant_callback_write_to_vectors (vectors, children[i], NULL);
+ }
+
+ g_assert (fixed_size - offset < 8);
+ g_variant_vectors_append_pad (vectors, fixed_size - offset);
+ }
+}
+
static gboolean
gvs_tuple_is_normal (GVariantSerialised value)
{
gsize offset;
gsize i;
+ /* as per the comment in gvs_tuple_get_child() */
+ if G_UNLIKELY (value.data == NULL && value.size != 0)
+ return FALSE;
+
offset_size = gvs_get_offset_size (value.size);
length = g_variant_type_info_n_members (value.type_info);
offset_ptr = value.size;
end = gvs_read_unaligned_le (value.data + offset_ptr, offset_size);
break;
+
+ default:
+ g_assert_not_reached ();
}
if (end < offset || end > offset_ptr)
gvs_variant_get_child (GVariantSerialised value,
gsize index_)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
/* NOTE: not O(1) and impossible for it to be... */
if (value.size)
const gpointer *children,
gsize n_children)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
const gchar *type_string;
gvs_filler (&child, children[0]);
const gpointer *children,
gsize n_children)
{
- GVariantSerialised child = { };
+ GVariantSerialised child = { 0, };
const gchar *type_string;
child.data = value.data;
memcpy (value.data + child.size + 1, type_string, strlen (type_string));
}
+static void
+gvs_variant_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ GVariantTypeInfo *child_type_info;
+ const gchar *type_string;
+
+ g_variant_callback_write_to_vectors (vectors, children[0], &child_type_info);
+ type_string = g_variant_type_info_get_type_string (child_type_info);
+
+ g_variant_vectors_append_copy (vectors, "", 1);
+ g_variant_vectors_append_copy (vectors, type_string, strlen (type_string));
+}
+
static inline gboolean
gvs_variant_is_normal (GVariantSerialised value)
{
/* < private >
* g_variant_serialised_n_children:
* @serialised: a #GVariantSerialised
- * @returns: the number of children
*
* For serialised data that represents a container value (maybes,
* tuples, arrays, variants), determine how many child items are inside
* that container.
+ *
+ * Returns: the number of children
*/
gsize
g_variant_serialised_n_children (GVariantSerialised serialised)
* g_variant_serialised_get_child:
* @serialised: a #GVariantSerialised
* @index_: the index of the child to fetch
- * @returns: a #GVariantSerialised for the child
*
* Extracts a child from a serialised data representing a container
* value.
* item of a variable-sized type is being returned.
*
* .data is never non-%NULL if size is 0.
+ *
+ * Returns: a #GVariantSerialised for the child
*/
GVariantSerialised
g_variant_serialised_get_child (GVariantSerialised serialised,
return gvs_/**/,/**/_needed_size (type_info, gvs_filler,
children, n_children);
+ )
+ g_assert_not_reached ();
+}
+
+void
+g_variant_serialiser_write_to_vectors (GVariantVectors *vectors,
+ GVariantTypeInfo *type_info,
+ gsize size,
+ const gpointer *children,
+ gsize n_children)
+{
+ DISPATCH_CASES (type_info,
+ gvs_/**/,/**/_write_to_vectors (vectors, type_info, size, children, n_children);
+ return;
)
g_assert_not_reached ();
}
)
+ if (serialised.data == NULL)
+ return FALSE;
+
/* some hard-coded terminal cases */
switch (g_variant_type_info_get_type_char (serialised.type_info))
{
g_variant_serialiser_is_string (gconstpointer data,
gsize size)
{
- const gchar *string = data;
+ const gchar *expected_end;
+ const gchar *end;
if (size == 0)
return FALSE;
- if (string[size - 1] != '\0')
+ expected_end = ((gchar *) data) + size - 1;
+
+ if (*expected_end != '\0')
return FALSE;
- return strlen (string) == size - 1;
+ g_utf8_validate (data, size, &end);
+
+ return end == expected_end;
}
/* < private >
* g_variant_serialiser_is_object_path:
- * @data: a possible DBus object path
+ * @data: a possible D-Bus object path
* @size: the size of @data
*
* Performs the checks for being a valid string.
*
- * Also, ensures that @data is a valid DBus object path, as per the DBus
+ * Also, ensures that @data is a valid DBus object path, as per the D-Bus
* specification.
*/
gboolean
/* < private >
* g_variant_serialiser_is_signature:
- * @data: a possible DBus signature
+ * @data: a possible D-Bus signature
* @size: the size of @data
*
* Performs the checks for being a valid string.
*
- * Also, ensures that @data is a valid DBus type signature, as per the
- * DBus specification.
+ * Also, ensures that @data is a valid D-Bus type signature, as per the
+ * D-Bus specification.
*/
gboolean
g_variant_serialiser_is_signature (gconstpointer data,
return TRUE;
}
+/* Epilogue {{{1 */
/* vim:set foldmethod=marker: */