From 0e9c283e45eacfb10d0136d8f5acfd53c5220563 Mon Sep 17 00:00:00 2001 From: Ryan Lortie Date: Fri, 28 Nov 2014 14:23:20 -0500 Subject: [PATCH] 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. --- glib/Makefile.am | 2 + glib/gvariant-vectors.c | 234 ++++++++++++++++++++++++++++++++++++++++++++++++ glib/gvariant-vectors.h | 60 +++++++++++++ 3 files changed, 296 insertions(+) create mode 100644 glib/gvariant-vectors.c create mode 100644 glib/gvariant-vectors.h diff --git a/glib/Makefile.am b/glib/Makefile.am index 1b9e081..2c4b878 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 0000000..a0bbf9a --- /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 0000000..b1f0519 --- /dev/null +++ b/glib/gvariant-vectors.h @@ -0,0 +1,60 @@ + +#ifndef __G_VARIANT_VECTORS_H__ +#define __G_VARIANT_VECTORS_H__ + +#include + +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__ */ -- 2.7.4