GVariant: add GVariantVectors utility struct
authorRyan Lortie <desrt@desrt.ca>
Fri, 28 Nov 2014 19:23:20 +0000 (14:23 -0500)
committerMaciej Wereski <m.wereski@partner.samsung.com>
Fri, 10 Jul 2015 09:47:42 +0000 (11:47 +0200)
This structure (and its associated functions) will be used as an
intermediate step for serialising GVariant instance onto kdbus without
copying large amounts of data.

glib/Makefile.am
glib/gvariant-vectors.c [new file with mode: 0644]
glib/gvariant-vectors.h [new file with mode: 0644]

index 1b9e081..2c4b878 100644 (file)
@@ -194,6 +194,8 @@ libglib_2_0_la_SOURCES =    \
        gvariant-parser.c       \
        gvariant-serialiser.h   \
        gvariant-serialiser.c   \
+       gvariant-vectors.h      \
+       gvariant-vectors.c      \
        gvarianttypeinfo.h      \
        gvarianttypeinfo.c      \
        gvarianttype.c          \
diff --git a/glib/gvariant-vectors.c b/glib/gvariant-vectors.c
new file mode 100644 (file)
index 0000000..a0bbf9a
--- /dev/null
@@ -0,0 +1,234 @@
+#include "config.h"
+
+#include "gvariant-vectors.h"
+#include "gtestutils.h"
+
+static void
+append_zeros (GByteArray *array,
+              guint       n)
+{
+  guchar zeros[8] = "";
+
+  g_byte_array_append (array, zeros, n);
+}
+
+void
+g_variant_vectors_init (GVariantVectors *vectors)
+{
+
+  /* The first 8 bytes of 'extra_bytes' is always 0.  We use this for
+   * inserting padding in between two GBytes records.
+   */
+  vectors->extra_bytes = g_byte_array_new ();
+  append_zeros (vectors->extra_bytes, 8);
+
+  vectors->vectors = g_array_new (FALSE, FALSE, sizeof (GVariantVector));
+
+  vectors->offsets = g_byte_array_new ();
+}
+
+gsize
+g_variant_vectors_append_pad (GVariantVectors *vectors,
+                              gsize            padding)
+{
+  /* If the last vector that we stored was 'pad' or 'copy' then we will
+   * expand it instead of adding a new one.
+   */
+  if (vectors->vectors->len)
+    {
+      GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
+
+      if (expand_vector->gbytes == NULL)
+        {
+          expand_vector->size += padding;
+
+          /* If the vector points to data, we need to add the padding to
+           * the end of that data.  If it points to the zero bytes at
+           * the start then we can just grow it (but we must ensure that
+           * it doesn't get too large).
+           */
+          if (expand_vector->data.offset)
+            append_zeros (vectors->extra_bytes, padding);
+          else
+            g_assert (expand_vector->size < 8);
+
+          return padding;
+        }
+
+      /* If the last vector was a GBytes then fall through */
+    }
+
+  /* Otherwise, record a new vector pointing to the padding bytes at the
+   * start.
+   */
+  {
+    GVariantVector v;
+
+    v.gbytes = NULL;
+    v.data.offset = 0;
+    v.size = padding;
+
+    g_array_append_val (vectors->vectors, v);
+  }
+
+  return padding;
+}
+
+void
+g_variant_vectors_append_copy (GVariantVectors *vectors,
+                               gconstpointer    data,
+                               gsize            size)
+{
+  /* If the last vector that we stored was 'pad' or 'copy' then we will
+   * expand it instead of adding a new one.
+   */
+  if (vectors->vectors->len)
+    {
+      GVariantVector *expand_vector = &g_array_index (vectors->vectors, GVariantVector, vectors->vectors->len - 1);
+
+      if (expand_vector->gbytes == NULL)
+        {
+          /* If this was a padding vector then we must convert it to
+           * data first.
+           */
+          if (expand_vector->data.offset == 0)
+            {
+              expand_vector->data.offset = vectors->extra_bytes->len;
+              append_zeros (vectors->extra_bytes, expand_vector->size);
+            }
+
+          /* We have a vector pointing to data at the end of the
+           * extra_bytes array, so just append there and grow the
+           * vector.
+           */
+          g_byte_array_append (vectors->extra_bytes, data, size);
+          expand_vector->size += size;
+          return;
+        }
+
+      /* If the last vector was a GBytes then fall through */
+    }
+
+  /* Otherwise, copy the data and record a new vector. */
+  {
+    GVariantVector v;
+
+    v.gbytes = NULL;
+    v.data.offset = vectors->extra_bytes->len;
+    v.size = size;
+
+    g_byte_array_append (vectors->extra_bytes, data, size);
+    g_array_append_val (vectors->vectors, v);
+  }
+}
+
+void
+g_variant_vectors_append_gbytes (GVariantVectors *vectors,
+                                 GBytes          *gbytes,
+                                 gconstpointer    data,
+                                 gsize            size)
+{
+  GVariantVector v;
+
+  /* Some very rough profiling has indicated that the trade-off for
+   * overhead on the atomic operations involved in the ref/unref on the
+   * GBytes is larger than the cost of the copy at ~128 bytes.
+   */
+  if (size < 128)
+    {
+      g_variant_vectors_append_copy (vectors, data, size);
+      return;
+    }
+
+  v.gbytes = g_bytes_ref (gbytes);
+  v.data.pointer = data;
+  v.size = size;
+
+  g_array_append_val (vectors->vectors, v);
+}
+
+typedef void (* WriteFunction) (gpointer base, gsize offset, gsize value);
+static void write_1 (gpointer base, gsize offset, gsize value) { ((guint8 *) base)[offset] = value; }
+static void write_2 (gpointer base, gsize offset, gsize value) { ((guint16 *) base)[offset] = GUINT16_TO_LE (value); }
+static void write_4 (gpointer base, gsize offset, gsize value) { ((guint32 *) base)[offset] = GUINT32_TO_LE (value); }
+static void write_8 (gpointer base, gsize offset, gsize value) { ((guint64 *) base)[offset] = GUINT64_TO_LE (value); }
+
+typedef struct
+{
+  gsize         size;
+  WriteFunction func;
+} OffsetsHeader;
+
+gsize
+g_variant_vectors_reserve_offsets (GVariantVectors *vectors,
+                                   guint            n_offsets,
+                                   guint            offset_size)
+{
+  OffsetsHeader *header;
+  gsize total_size;
+  gsize add_size;
+  guint key;
+
+  total_size = n_offsets * offset_size;
+
+  /* Add room for the metadata and round up to multiple of 8 */
+  add_size = (sizeof (OffsetsHeader) + total_size + 7) & ~7ull;
+  key = vectors->offsets->len;
+  g_byte_array_set_size (vectors->offsets, key + add_size);
+  header = (OffsetsHeader *) (vectors->offsets->data + key);
+  key += sizeof (OffsetsHeader);
+  header->size = total_size;
+
+  switch (offset_size)
+    {
+    case 1:
+      header->func = write_1;
+      break;
+
+    case 2:
+      header->func = write_2;
+      break;
+
+    case 4:
+      header->func = write_4;
+      break;
+
+    case 8:
+      header->func = write_8;
+      break;
+
+    default:
+      g_assert_not_reached ();
+    }
+
+  return key;
+}
+
+void
+g_variant_vectors_write_to_offsets (GVariantVectors *vectors,
+                                    gsize            offset,
+                                    gsize            value,
+                                    gsize            key)
+{
+  OffsetsHeader *header;
+  guchar *offsets;
+
+  offsets = vectors->offsets->data + key;
+  header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
+
+  header->func (offsets, offset, value);
+}
+
+void
+g_variant_vectors_commit_offsets (GVariantVectors *vectors,
+                                  gsize            key)
+{
+  OffsetsHeader *header;
+  guchar *offsets;
+
+  offsets = vectors->offsets->data + key;
+  header = (OffsetsHeader *) (offsets - sizeof (OffsetsHeader));
+
+  g_variant_vectors_append_copy (vectors, offsets, header->size);
+  g_byte_array_set_size (vectors->offsets, key - sizeof (OffsetsHeader));
+}
diff --git a/glib/gvariant-vectors.h b/glib/gvariant-vectors.h
new file mode 100644 (file)
index 0000000..b1f0519
--- /dev/null
@@ -0,0 +1,60 @@
+
+#ifndef __G_VARIANT_VECTORS_H__
+#define __G_VARIANT_VECTORS_H__
+
+#include <glib/garray.h>
+
+typedef struct
+{
+  GByteArray *extra_bytes;
+  GArray     *vectors;
+  GByteArray *offsets;
+} GVariantVectors;
+
+
+/* If ->bytes is NULL then offset/size point inside of extra_bytes,
+ * otherwise pointer/size point to memory owned by the GBytes.
+ */
+typedef struct
+{
+  GBytes *gbytes;
+  union {
+    gconstpointer pointer;
+    gsize         offset;
+  } data;
+  gsize    size;
+} GVariantVector;
+
+void                    g_variant_vectors_init                          (GVariantVectors *vectors);
+
+
+gsize                   g_variant_vectors_append_pad                    (GVariantVectors *vectors,
+                                                                         gsize            padding);
+
+
+void                    g_variant_vectors_append_copy                   (GVariantVectors *vectors,
+                                                                         gconstpointer    data,
+                                                                         gsize            size);
+
+
+void                    g_variant_vectors_append_gbytes                 (GVariantVectors *vectors,
+                                                                         GBytes          *gbytes,
+                                                                         gconstpointer    data,
+                                                                         gsize            size);
+
+
+gsize                   g_variant_vectors_reserve_offsets               (GVariantVectors *vectors,
+                                                                         guint            n_offsets,
+                                                                         guint            offset_size);
+
+
+void                    g_variant_vectors_write_to_offsets              (GVariantVectors *vectors,
+                                                                         gsize            offset,
+                                                                         gsize            value,
+                                                                         gsize            offset_key);
+
+
+void                    g_variant_vectors_commit_offsets                (GVariantVectors *vectors,
+                                                                         gsize            offset_key);
+
+#endif /* __G_GVARIANT_VECTORS_H__ */