summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Lortie <desrt@desrt.ca>2014-11-28 14:23:20 -0500
committerRyan Lortie <desrt@desrt.ca>2014-12-02 16:30:21 -0500
commit025e6708106372d40937bef3b3d54b00189959a6 (patch)
tree7b627dd0c220fb110fa0c6b4f5ee47ad3aa95a4e
parent87226d3b2fe626d4e914de1b1171010e95be30d9 (diff)
downloadglib-025e6708106372d40937bef3b3d54b00189959a6.tar.gz
GVariant: add GVariantVectors utility struct
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.
-rw-r--r--glib/Makefile.am2
-rw-r--r--glib/gvariant-vectors.c234
-rw-r--r--glib/gvariant-vectors.h60
3 files changed, 296 insertions, 0 deletions
diff --git a/glib/Makefile.am b/glib/Makefile.am
index 1b9e08134..2c4b878ed 100644
--- a/glib/Makefile.am
+++ b/glib/Makefile.am
@@ -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
index 000000000..a0bbf9a6b
--- /dev/null
+++ b/glib/gvariant-vectors.c
@@ -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
index 000000000..b1f0519f9
--- /dev/null
+++ b/glib/gvariant-vectors.h
@@ -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__ */