[kdbus] KDBUS_ITEM_PAYLOAD_OFF items are (once again) relative to msg header
[platform/upstream/glib.git] / glib / gvariant-core.c
index 1a0c041..a9db9a1 100644 (file)
@@ -482,7 +482,7 @@ g_variant_to_vectors (GVariant        *value,
 
 /* < private >
  * g_variant_alloc:
- * @type: the type of the new instance
+ * @type_info: (transfer full) the type info of the new instance
  * @serialised: if the instance will be in serialised form
  * @trusted: if the instance will be trusted
  *
@@ -493,14 +493,14 @@ g_variant_to_vectors (GVariant        *value,
  * Returns: a new #GVariant with a floating reference
  */
 static GVariant *
-g_variant_alloc (const GVariantType *type,
-                 gboolean            serialised,
-                 gboolean            trusted)
+g_variant_alloc (GVariantTypeInfo *type_info,
+                 gboolean          serialised,
+                 gboolean          trusted)
 {
   GVariant *value;
 
   value = g_slice_new (GVariant);
-  value->type_info = g_variant_type_info_get (type);
+  value->type_info = type_info;
   value->state = (serialised ? STATE_SERIALISED : 0) |
                  (trusted ? STATE_TRUSTED : 0) |
                  STATE_FLOATING;
@@ -513,7 +513,7 @@ g_variant_alloc (const GVariantType *type,
 
 /* < internal >
  * g_variant_new_from_children:
- * @type: a #GVariantType
+ * @type_info: (transfer full) a #GVariantTypeInfo
  * @children: an array of #GVariant pointers.  Consumed.
  * @n_children: the length of @children
  * @trusted: %TRUE if every child in @children in trusted
@@ -528,14 +528,14 @@ g_variant_alloc (const GVariantType *type,
  * Returns: a new #GVariant with a floating reference
  */
 GVariant *
-g_variant_new_from_children (const GVariantType  *type,
-                             GVariant           **children,
-                             gsize                n_children,
-                             gboolean             trusted)
+g_variant_new_from_children (GVariantTypeInfo  *type_info,
+                             GVariant         **children,
+                             gsize              n_children,
+                             gboolean           trusted)
 {
   GVariant *value;
 
-  value = g_variant_alloc (type, FALSE, trusted);
+  value = g_variant_alloc (type_info, FALSE, trusted);
   value->contents.tree.children = children;
   value->contents.tree.n_children = n_children;
   value->size = g_variant_serialiser_needed_size (value->type_info, g_variant_fill_gvs,
@@ -546,8 +546,8 @@ g_variant_new_from_children (const GVariantType  *type,
 
 /* < internal >
  * g_variant_new_serialised:
- * @type: a #GVariantType
- * @bytes: the #GBytes holding @data
+ * @type_info: (transfer full): a #GVariantTypeInfo
+ * @bytes: (transfer full): the #GBytes holding @data
  * @data: a pointer to the serialised data
  * @size: the size of @data, in bytes
  * @trusted: %TRUE if @data is trusted
@@ -562,16 +562,16 @@ g_variant_new_from_children (const GVariantType  *type,
  * Returns: a new #GVariant with a floating reference
  */
 GVariant *
-g_variant_new_serialised (const GVariantType *type,
-                          GBytes             *bytes,
-                          gconstpointer       data,
-                          gsize               size,
-                          gboolean            trusted)
+g_variant_new_serialised (GVariantTypeInfo *type_info,
+                          GBytes           *bytes,
+                          gconstpointer     data,
+                          gsize             size,
+                          gboolean          trusted)
 {
   GVariant *value;
   gsize fixed_size;
 
-  value = g_variant_alloc (type, TRUE, trusted);
+  value = g_variant_alloc (type_info, TRUE, trusted);
   value->contents.serialised.bytes = bytes;
   value->contents.serialised.data = data;
   value->size = size;
@@ -665,6 +665,165 @@ g_variant_get_serialised (GVariant  *value,
   return value->contents.serialised.data;
 }
 
+static GVariant *
+g_variant_vector_deserialise (GVariantTypeInfo *type_info,
+                              GVariantVector   *first_vector,
+                              GVariantVector   *last_vector,
+                              gsize             size,
+                              gboolean          trusted,
+                              GArray           *unpacked_children)
+{
+  g_assert (size > 0);
+
+  if (first_vector < last_vector)
+    {
+      GVariantVector *vector = first_vector;
+      const guchar *end_pointer;
+      GVariant **children;
+      guint save_point;
+      guint n_children;
+      gboolean failed;
+      guint i;
+
+      end_pointer = last_vector->data.pointer + last_vector->size;
+      save_point = unpacked_children->len;
+
+      if (!g_variant_serialiser_unpack_all (type_info, end_pointer, last_vector->size, size, unpacked_children))
+        {
+          for (i = save_point; i < unpacked_children->len; i++)
+            g_variant_type_info_unref (g_array_index (unpacked_children, GVariantUnpacked, i).type_info);
+          g_array_set_size (unpacked_children, save_point);
+
+          g_variant_type_info_unref (type_info);
+
+          return NULL;
+        }
+
+      n_children = unpacked_children->len - save_point;
+      children = g_new (GVariant *, n_children);
+      failed = FALSE;
+
+      for (i = 0; i < n_children; i++)
+        {
+          GVariantUnpacked *unpacked = &g_array_index (unpacked_children, GVariantUnpacked, save_point + i);
+          const guchar *resume_at_data;
+          gsize resume_at_size;
+          GVariantVector *fv;
+
+          /* Skip the alignment.
+           *
+           * We can destroy vectors because we won't be going back.
+           *
+           * We do a >= compare because we want to go to the next vector
+           * if it is the start of our child.
+           */
+          while (unpacked->skip >= vector->size)
+            {
+              unpacked->skip -= vector->size;
+              vector++;
+            }
+          g_assert (vector <= last_vector);
+
+          fv = vector;
+          fv->data.pointer += unpacked->skip;
+          fv->size -= unpacked->skip;
+
+          if (unpacked->size == 0)
+            {
+              children[i] = g_variant_new_serialised (unpacked->type_info, g_bytes_new (NULL, 0), NULL, 0, trusted);
+              g_variant_ref_sink (children[i]);
+              continue;
+            }
+
+          /* Now skip to the end, according to 'size'.
+           *
+           * We cannot destroy everything here because we will probably
+           * end up reusing the last one.
+           *
+           * We do a > compare because we want to stay on this vector if
+           * it is the end of our child.
+           */
+          size = unpacked->size;
+          while (unpacked->size > vector->size)
+            {
+              unpacked->size -= vector->size;
+              vector++;
+            }
+          g_assert (vector <= last_vector);
+
+          /* We have to modify the vectors for the benefit of the
+           * recursive step.  We also have to remember where we left
+           * off, keeping in mind that the recursive step may itself
+           * modify the vectors.
+           */
+          resume_at_data = vector->data.pointer + unpacked->size;
+          resume_at_size = vector->size - unpacked->size;
+          vector->size = unpacked->size;
+
+          children[i] = g_variant_vector_deserialise (unpacked->type_info, fv, vector,
+                                                      size, trusted, unpacked_children);
+
+          vector->data.pointer = resume_at_data;
+          vector->size = resume_at_size;
+
+          if (children[i])
+            g_variant_ref_sink (children[i]);
+          else
+            failed = TRUE;
+        }
+
+      /* We consumed all the type infos */
+      g_array_set_size (unpacked_children, save_point);
+
+      if G_UNLIKELY (failed)
+        {
+          for (i = 0; i < n_children; i++)
+            if (children[i])
+              g_variant_unref (children[i]);
+
+          g_variant_type_info_unref (type_info);
+          g_free (children);
+
+          return NULL;
+        }
+
+      return g_variant_new_from_children (type_info, children, n_children, trusted);
+    }
+  else
+    {
+      g_assert (first_vector == last_vector);
+      g_assert (size == first_vector->size);
+
+      return g_variant_new_serialised (type_info, g_bytes_ref (first_vector->gbytes),
+                                       first_vector->data.pointer, size, trusted);
+    }
+}
+
+GVariant *
+g_variant_from_vectors (const GVariantType *type,
+                        GVariantVector     *vectors,
+                        gsize               n_vectors,
+                        gsize               size,
+                        gboolean            trusted)
+{
+  GVariant *result;
+  GArray *tmp;
+
+  g_return_val_if_fail (vectors != NULL || n_vectors == 0, NULL);
+
+  if (size == 0)
+    return g_variant_new_serialised (g_variant_type_info_get (type), g_bytes_new (NULL, 0), NULL, 0, trusted);
+
+  tmp = g_array_new (FALSE, FALSE, sizeof (GVariantUnpacked));
+  result = g_variant_vector_deserialise (g_variant_type_info_get (type),
+                                         vectors, vectors + n_vectors - 1, size, trusted, tmp);
+  if (result)
+    g_variant_ref_sink (result);
+  g_array_free (tmp, TRUE);
+
+  return result;
+}
+
 /* -- public -- */
 
 /**
@@ -1012,15 +1171,11 @@ g_variant_get_child_value (GVariant *value,
       s_child = g_variant_serialised_get_child (serialised, index_);
 
       /* create a new serialised instance out of it */
-      child = g_slice_new (GVariant);
-      child->type_info = s_child.type_info;
-      child->state = (value->state & STATE_TRUSTED) |
-                     STATE_SERIALISED;
-      child->size = s_child.size;
-      child->ref_count = 1;
-      child->contents.serialised.bytes =
-        g_bytes_ref (value->contents.serialised.bytes);
-      child->contents.serialised.data = s_child.data;
+      child = g_variant_new_serialised (s_child.type_info,
+                                        g_bytes_ref (value->contents.serialised.bytes),
+                                        s_child.data, s_child.size,
+                                        value->state & STATE_TRUSTED);
+      child->state &= ~STATE_FLOATING;
     }
 
   return child;