[tracker/wip/carlosg/sparql-parser-ng: 3/3] libtracker-data: Add TrackerStringBuilder



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]