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



commit 975aca22e2fb25fa7b7edaee275e3afc959a9b72
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/meson.build              |   1 +
 src/libtracker-data/tracker-string-builder.c | 314 +++++++++++++++++++++++++++
 src/libtracker-data/tracker-string-builder.h |  37 ++++
 3 files changed, 352 insertions(+)
---
diff --git a/src/libtracker-data/meson.build b/src/libtracker-data/meson.build
index daaf40fb1..8fd8ea158 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_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 000000000..b43d1a29c
--- /dev/null
+++ b/src/libtracker-data/tracker-string-builder.c
@@ -0,0 +1,314 @@
+/*
+ * Copyright (C) 2008-2010, Nokia
+ * Copyright (C) 2017-2018, Carlos Garnacho
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ *
+ * Author: Carlos Garnacho <carlosg gnome org>
+ */
+#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
+{
+       GArray *elems;
+};
+
+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)
+               tracker_string_builder_free (elem->data.builder);
+}
+
+TrackerStringBuilder *
+tracker_string_builder_new (void)
+{
+       TrackerStringBuilder *builder;
+
+       builder = g_slice_new0 (TrackerStringBuilder);
+       builder->elems = g_array_new (FALSE, TRUE, sizeof (TrackerStringElement));
+       g_array_set_clear_func (builder->elems, free_string_element);
+
+       return builder;
+}
+
+void
+tracker_string_builder_free (TrackerStringBuilder *builder)
+{
+       g_array_free (builder->elems, TRUE);
+       g_slice_free (TrackerStringBuilder, builder);
+}
+
+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 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 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);
+       chunk->len += len;
+       g_assert (chunk->len <= chunk->allocated_size);
+}
+
+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_valist (TrackerStringBuilder *builder,
+                                      const gchar          *format,
+                                      va_list               args)
+{
+       TrackerStringChunk *chunk;
+       gchar *str;
+
+       str = g_strdup_vprintf (format, args);
+
+       chunk = ensure_last_chunk (builder);
+       string_chunk_append (chunk, str, -1);
+       g_free (str);
+}
+
+void
+tracker_string_builder_prepend_valist (TrackerStringBuilder *builder,
+                                       const gchar          *format,
+                                       va_list               args)
+{
+       TrackerStringChunk *chunk;
+       gchar *str;
+
+       str = g_strdup_vprintf (format, args);
+
+       chunk = ensure_first_chunk (builder);
+       string_chunk_append (chunk, str, -1);
+       g_free (str);
+}
+
+void
+tracker_string_builder_append_printf (TrackerStringBuilder *builder,
+                                      const gchar          *format,
+                                      ...)
+{
+       va_list varargs;
+
+       va_start (varargs, format);
+       tracker_string_builder_append_valist (builder, format, varargs);
+       va_end (varargs);
+}
+
+void
+tracker_string_builder_prepend_printf (TrackerStringBuilder *builder,
+                                       const gchar          *format,
+                                       ...)
+{
+       va_list varargs;
+
+       va_start (varargs, format);
+       tracker_string_builder_prepend_valist (builder, format, varargs);
+       va_end (varargs);
+}
+
+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);
+}
+
+gboolean
+tracker_string_builder_is_empty (TrackerStringBuilder *builder)
+{
+       return builder->elems->len == 0;
+}
diff --git a/src/libtracker-data/tracker-string-builder.h b/src/libtracker-data/tracker-string-builder.h
new file mode 100644
index 000000000..dd04ee190
--- /dev/null
+++ b/src/libtracker-data/tracker-string-builder.h
@@ -0,0 +1,37 @@
+#ifndef __TRACKER_STRING_BUILDER_H__
+#define __TRACKER_STRING_BUILDER_H__
+
+#include <glib.h>
+
+typedef struct _TrackerStringBuilder TrackerStringBuilder;
+
+TrackerStringBuilder * tracker_string_builder_new  (void);
+void                   tracker_string_builder_free (TrackerStringBuilder *builder);
+
+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_prepend_valist (TrackerStringBuilder *builder,
+                                            const gchar          *format,
+                                            va_list               args);
+void tracker_string_builder_append_valist  (TrackerStringBuilder *builder,
+                                            const gchar          *format,
+                                            va_list               args);
+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);
+
+gboolean tracker_string_builder_is_empty (TrackerStringBuilder *builder);
+
+#endif /* __TRACKER_STRING_BUILDER_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]