[tracker/wip/carlosg/sparql-parser-ng: 3/3] libtracker-data: Add TrackerStringBuilder
- From: Carlos Garnacho <carlosg src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/wip/carlosg/sparql-parser-ng: 3/3] libtracker-data: Add TrackerStringBuilder
- Date: Thu, 28 Dec 2017 23:11:29 +0000 (UTC)
commit 285653650f41fcdc5cec56f58e0c20ab8b767b17
Author: Carlos Garnacho <carlosg gnome org>
Date: Fri Dec 29 00:05:07 2017 +0100
libtracker-data: Add TrackerStringBuilder
This is somewhat similar to GString, except it allows for adding
extension points at random places that allow for inserting stuff
mid-string without relocating the whole thing.
It is expected that SQL query construction will use this facility.
src/libtracker-data/Makefile.am | 2 +
src/libtracker-data/meson.build | 1 +
src/libtracker-data/tracker-string-builder.c | 287 ++++++++++++++++++++++++++
src/libtracker-data/tracker-string-builder.h | 30 +++
4 files changed, 320 insertions(+), 0 deletions(-)
---
diff --git a/src/libtracker-data/Makefile.am b/src/libtracker-data/Makefile.am
index cf277f5..791a8e3 100644
--- a/src/libtracker-data/Makefile.am
+++ b/src/libtracker-data/Makefile.am
@@ -53,6 +53,7 @@ libtracker_data_la_SOURCES = \
tracker-ontology.c \
tracker-ontologies.c \
tracker-property.c \
+ tracker-string-builder.c \
tracker-sparql-parser.c
libtracker_data_la_LIBADD = \
@@ -86,6 +87,7 @@ noinst_HEADERS = \
tracker-ontology.h \
tracker-ontologies.h \
tracker-property.h \
+ tracker-string-builder.h \
tracker-sparql-parser.h \
tracker-sparql-query.h
diff --git a/src/libtracker-data/meson.build b/src/libtracker-data/meson.build
index 5368b3d..16e5911 100644
--- a/src/libtracker-data/meson.build
+++ b/src/libtracker-data/meson.build
@@ -57,6 +57,7 @@ libtracker_data = library('tracker-data',
'tracker-ontology.c',
'tracker-ontologies.c',
'tracker-property.c',
+ 'tracker-string-builder.c',
'tracker-sparql-parser.c',
tracker_common_enum_header, tracker_common_parser_sha1_header,
tracker_data_enums[0],
diff --git a/src/libtracker-data/tracker-string-builder.c b/src/libtracker-data/tracker-string-builder.c
new file mode 100644
index 0000000..d1e388c
--- /dev/null
+++ b/src/libtracker-data/tracker-string-builder.c
@@ -0,0 +1,287 @@
+#include "config.h"
+
+#include <string.h>
+
+#include "tracker-string-builder.h"
+
+typedef struct _TrackerStringChunk TrackerStringChunk;
+typedef struct _TrackerStringElement TrackerStringElement;
+
+struct _TrackerStringChunk
+{
+ gchar *string;
+ gsize allocated_size;
+ gsize len;
+};
+
+enum {
+ ELEM_TYPE_STRING,
+ ELEM_TYPE_BUILDER
+};
+
+struct _TrackerStringElement
+{
+ guint type;
+ union {
+ TrackerStringChunk *chunk;
+ TrackerStringBuilder *builder;
+ } data;
+};
+
+struct _TrackerStringBuilder
+{
+ GObject parent_instance;
+ GArray *elems;
+};
+
+struct _TrackerStringBuilderClass
+{
+ GObjectClass parent_class;
+};
+
+G_DEFINE_TYPE (TrackerStringBuilder, tracker_string_builder, G_TYPE_OBJECT)
+
+static void
+tracker_string_builder_finalize (GObject *object)
+{
+ TrackerStringBuilder *builder = TRACKER_STRING_BUILDER (object);
+
+ g_array_free (builder->elems, TRUE);
+
+ G_OBJECT_CLASS (tracker_string_builder_parent_class)->finalize (object);
+}
+
+static void
+tracker_string_builder_class_init (TrackerStringBuilderClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = tracker_string_builder_finalize;
+}
+
+static void
+free_string_chunk (TrackerStringChunk *chunk)
+{
+ g_free (chunk->string);
+ g_free (chunk);
+}
+
+static void
+free_string_element (gpointer data)
+{
+ TrackerStringElement *elem = data;
+
+ if (elem->type == ELEM_TYPE_STRING)
+ free_string_chunk (elem->data.chunk);
+ else if (elem->type == ELEM_TYPE_BUILDER)
+ g_object_unref (elem->data.builder);
+}
+
+static void
+tracker_string_builder_init (TrackerStringBuilder *builder)
+{
+ builder->elems = g_array_new (FALSE, TRUE, sizeof (TrackerStringElement));
+ g_array_set_clear_func (builder->elems, free_string_element);
+}
+
+TrackerStringBuilder *
+tracker_string_builder_new (void)
+{
+ return g_object_new (TRACKER_TYPE_STRING_BUILDER, NULL);
+}
+
+TrackerStringBuilder *
+tracker_string_builder_append_placeholder (TrackerStringBuilder *builder)
+{
+ TrackerStringBuilder *child;
+ TrackerStringElement elem;
+
+ child = tracker_string_builder_new ();
+
+ elem.type = ELEM_TYPE_BUILDER;
+ elem.data.builder = child;
+ g_array_append_val (builder->elems, elem);
+
+ return g_object_ref (child);
+}
+
+TrackerStringBuilder *
+tracker_string_builder_prepend_placeholder (TrackerStringBuilder *builder)
+{
+ TrackerStringBuilder *child;
+ TrackerStringElement elem;
+
+ child = tracker_string_builder_new ();
+
+ elem.type = ELEM_TYPE_BUILDER;
+ elem.data.builder = child;
+ g_array_prepend_val (builder->elems, elem);
+
+ return g_object_ref (child);
+}
+
+static TrackerStringChunk *
+ensure_last_chunk (TrackerStringBuilder *builder)
+{
+ TrackerStringElement elem;
+ TrackerStringChunk *chunk;
+
+ if (builder->elems->len > 0) {
+ TrackerStringElement *last;
+
+ last = &g_array_index (builder->elems, TrackerStringElement,
+ builder->elems->len - 1);
+ if (last->type == ELEM_TYPE_STRING)
+ return last->data.chunk;
+ }
+
+ chunk = g_new0 (TrackerStringChunk, 1);
+
+ elem.type = ELEM_TYPE_STRING;
+ elem.data.chunk = chunk;
+ g_array_append_val (builder->elems, elem);
+
+ return chunk;
+}
+
+static TrackerStringChunk *
+ensure_first_chunk (TrackerStringBuilder *builder)
+{
+ TrackerStringElement elem;
+ TrackerStringChunk *chunk;
+
+ /* Always create a new element instead of trying to prepend on
+ * the first string chunk. Between memory relocations and memory
+ * fragmentation, we choose the latter. This object is short lived
+ * anyway.
+ */
+ chunk = g_new0 (TrackerStringChunk, 1);
+
+ elem.type = ELEM_TYPE_STRING;
+ elem.data.chunk = chunk;
+ g_array_prepend_val (builder->elems, elem);
+
+ return chunk;
+}
+
+static inline gsize
+fitting_power_of_two (gsize string_len)
+{
+ gsize s = 1;
+
+ while (s < string_len)
+ s <<= 1;
+
+ return s;
+}
+
+static void
+string_chunk_append (TrackerStringChunk *chunk,
+ const gchar *str,
+ gssize len)
+{
+ if (len < 0)
+ len = strlen (str);
+
+ if (chunk->len + len > chunk->allocated_size) {
+ /* Expand size */
+ gssize new_size = fitting_power_of_two (chunk->len + len);
+
+ g_assert (new_size > chunk->allocated_size);
+ chunk->string = g_realloc (chunk->string, new_size);
+ chunk->allocated_size = new_size;
+ }
+
+ /* String (now) fits in allocated size */
+ strncpy (&chunk->string[chunk->len], str, len);
+}
+
+void
+tracker_string_builder_append (TrackerStringBuilder *builder,
+ const gchar *string,
+ gssize len)
+{
+ TrackerStringChunk *chunk;
+
+ chunk = ensure_last_chunk (builder);
+ string_chunk_append (chunk, string, len);
+}
+
+void
+tracker_string_builder_prepend (TrackerStringBuilder *builder,
+ const gchar *string,
+ gssize len)
+{
+ TrackerStringChunk *chunk;
+
+ chunk = ensure_first_chunk (builder);
+ string_chunk_append (chunk, string, len);
+}
+
+void
+tracker_string_builder_append_printf (TrackerStringBuilder *builder,
+ const gchar *format,
+ ...)
+{
+ TrackerStringChunk *chunk;
+ va_list varargs;
+ gchar *str;
+
+ va_start (varargs, format);
+ str = g_strdup_vprintf (format, varargs);
+ va_end (varargs);
+
+ chunk = ensure_last_chunk (builder);
+ string_chunk_append (chunk, str, -1);
+ g_free (str);
+}
+
+void
+tracker_string_builder_prepend_printf (TrackerStringBuilder *builder,
+ const gchar *format,
+ ...)
+{
+ TrackerStringChunk *chunk;
+ va_list varargs;
+ gchar *str;
+
+ va_start (varargs, format);
+ str = g_strdup_vprintf (format, varargs);
+ va_end (varargs);
+
+ chunk = ensure_first_chunk (builder);
+ string_chunk_append (chunk, str, -1);
+ g_free (str);
+}
+
+static void
+tracker_string_builder_to_gstring (TrackerStringBuilder *builder,
+ GString *str)
+{
+ guint i;
+
+ for (i = 0; i < builder->elems->len; i++) {
+ TrackerStringElement *elem;
+
+ elem = &g_array_index (builder->elems, TrackerStringElement, i);
+
+ if (elem->type == ELEM_TYPE_STRING) {
+ g_string_append_len (str,
+ elem->data.chunk->string,
+ elem->data.chunk->len);
+ } else if (elem->type == ELEM_TYPE_BUILDER) {
+ tracker_string_builder_to_gstring (elem->data.builder,
+ str);
+ }
+ }
+}
+
+gchar *
+tracker_string_builder_to_string (TrackerStringBuilder *builder)
+{
+ GString *str = g_string_new (NULL);
+
+ tracker_string_builder_to_gstring (builder, str);
+
+ return g_string_free (str, FALSE);
+}
diff --git a/src/libtracker-data/tracker-string-builder.h b/src/libtracker-data/tracker-string-builder.h
new file mode 100644
index 0000000..6d39e14
--- /dev/null
+++ b/src/libtracker-data/tracker-string-builder.h
@@ -0,0 +1,30 @@
+#ifndef __TRACKER_STRING_BUILDER_H__
+#define __TRACKER_STRING_BUILDER_H__
+
+#include <glib-object.h>
+
+#define TRACKER_TYPE_STRING_BUILDER (tracker_string_builder_get_type ())
+
+G_DECLARE_FINAL_TYPE (TrackerStringBuilder, tracker_string_builder,
+ TRACKER, STRING_BUILDER, GObject)
+
+TrackerStringBuilder * tracker_string_builder_new (void);
+TrackerStringBuilder * tracker_string_builder_append_placeholder (TrackerStringBuilder *builder);
+TrackerStringBuilder * tracker_string_builder_prepend_placeholder (TrackerStringBuilder *builder);
+
+void tracker_string_builder_append (TrackerStringBuilder *builder,
+ const gchar *string,
+ gssize len);
+void tracker_string_builder_prepend (TrackerStringBuilder *builder,
+ const gchar *string,
+ gssize len);
+void tracker_string_builder_append_printf (TrackerStringBuilder *builder,
+ const gchar *format,
+ ...);
+void tracker_string_builder_prepend_printf (TrackerStringBuilder *builder,
+ const gchar *format,
+ ...);
+
+gchar * tracker_string_builder_to_string (TrackerStringBuilder *builder);
+
+#endif /* __TRACKER_STRING_BUILDER_H__ */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]