summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/reference/glib/glib-sections.txt.in2
-rw-r--r--glib/gstring.c73
-rw-r--r--glib/gstring.h50
-rw-r--r--glib/tests/string.c39
4 files changed, 149 insertions, 15 deletions
diff --git a/docs/reference/glib/glib-sections.txt.in b/docs/reference/glib/glib-sections.txt.in
index 6795a141f..4fdfa570c 100644
--- a/docs/reference/glib/glib-sections.txt.in
+++ b/docs/reference/glib/glib-sections.txt.in
@@ -2652,6 +2652,8 @@ g_string_truncate
g_string_set_size
g_string_free
g_string_free_to_bytes
+g_string_init
+g_string_free
<SUBSECTION>
g_string_up
diff --git a/glib/gstring.c b/glib/gstring.c
index 8967ce12b..86869d950 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -59,6 +59,10 @@
* characters in the data. Conceptually then, #GString is like a
* #GByteArray with the addition of many convenience methods for text,
* and a guaranteed nul terminator.
+ *
+ * GString can be used as a heap-allocated object, using g_string_new()
+ * and g_string_free(), or as a stack-allocated struct, using g_string_init()
+ * and g_string_clear().
*/
/**
@@ -70,6 +74,7 @@
* terminating nul byte.
* @allocated_len: the number of bytes that can be stored in the
* string before it needs to be reallocated. May be larger than @len.
+ * @buf: preallocated memory for @str
*
* The GString struct contains the public fields of a GString.
*/
@@ -82,6 +87,9 @@ g_string_expand (GString *string,
if G_UNLIKELY ((G_MAXSIZE - string->len - 1) < len)
g_error ("adding %" G_GSIZE_FORMAT " to string would overflow", len);
+ if (string->str == NULL)
+ g_string_init (string);
+
string->allocated_len = g_nearest_pow (string->len + len + 1);
/* If the new size is bigger than G_MAXSIZE / 2, only allocate enough
* memory for this string and don't over-allocate.
@@ -89,7 +97,14 @@ g_string_expand (GString *string,
if (string->allocated_len == 0)
string->allocated_len = string->len + len + 1;
- string->str = g_realloc (string->str, string->allocated_len);
+ if (string->str != string->buf)
+ string->str = g_realloc (string->str, string->allocated_len);
+ else
+ {
+ string->str = g_malloc (string->allocated_len);
+ memcpy (string->str, string->buf, string->len);
+ string->str[string->len] = 0;
+ }
}
static inline void
@@ -101,6 +116,23 @@ g_string_maybe_expand (GString *string,
}
/**
+ * g_string_init:
+ * @string: an uninitialized #GString
+ *
+ * Initializes @string.
+ *
+ * This function should be used to initialize a stack-allocated
+ * GString struct.
+ *
+ * Since: 2.76
+ */
+void
+(g_string_init) (GString *string)
+{
+ g_string_init_inline (string);
+}
+
+/**
* g_string_sized_new: (constructor)
* @dfl_size: the default size of the space allocated to hold the string
*
@@ -116,12 +148,8 @@ g_string_sized_new (gsize dfl_size)
{
GString *string = g_slice_new (GString);
- string->allocated_len = 0;
- string->len = 0;
- string->str = NULL;
-
- g_string_expand (string, MAX (dfl_size, 64));
- string->str[0] = 0;
+ g_string_init (string);
+ g_string_maybe_expand (string, dfl_size);
return string;
}
@@ -190,6 +218,29 @@ g_string_new_len (const gchar *init,
}
/**
+ * g_string_clear:
+ * @string: (transfer full): a #GString
+ * @free_segment: if %TRUE, the actual character data is freed as well
+ *
+ * Clears a stack-allocated GString struct.
+ *
+ * If @free_segment is %TRUE it also frees the character data.
+ * If it's %FALSE, the caller gains ownership of the buffer and
+ * must free it after use with g_free().
+ *
+ * Returns: (nullable): the character data of @string
+ * (i.e. %NULL if @free_segment is %TRUE)
+ *
+ * Since: 2.76
+ */
+char *
+(g_string_clear) (GString *string,
+ gboolean free_segment)
+{
+ return g_string_clear_inline (string, free_segment);
+}
+
+/**
* g_string_free:
* @string: (transfer full): a #GString
* @free_segment: if %TRUE, the actual character data is freed as well
@@ -210,13 +261,7 @@ g_string_free (GString *string,
g_return_val_if_fail (string != NULL, NULL);
- if (free_segment)
- {
- g_free (string->str);
- segment = NULL;
- }
- else
- segment = string->str;
+ segment = g_string_clear (string, free_segment);
g_slice_free (GString, string);
diff --git a/glib/gstring.h b/glib/gstring.h
index 7b8455360..f615bd340 100644
--- a/glib/gstring.h
+++ b/glib/gstring.h
@@ -35,10 +35,13 @@
#include <glib/gunicode.h>
#include <glib/gbytes.h>
#include <glib/gutils.h> /* for G_CAN_INLINE */
+#include <glib/gstrfuncs.h>
#include <string.h>
G_BEGIN_DECLS
+#define G_STRING_PREALLOC 64
+
typedef struct _GString GString;
struct _GString
@@ -46,6 +49,7 @@ struct _GString
gchar *str;
gsize len;
gsize allocated_len;
+ char buf[G_STRING_PREALLOC];
};
GLIB_AVAILABLE_IN_ALL
@@ -163,10 +167,55 @@ GString* g_string_append_uri_escaped (GString *string,
const gchar *reserved_chars_allowed,
gboolean allow_utf8);
+GLIB_AVAILABLE_IN_2_76
+void g_string_init (GString *string);
+
+GLIB_AVAILABLE_IN_2_76
+char * g_string_clear (GString *string,
+ gboolean free_segment);
+
#ifndef __GTK_DOC_IGNORE__
#ifdef G_CAN_INLINE
G_ALWAYS_INLINE
+static inline void
+g_string_init_inline (GString *string)
+{
+ string->str = string->buf;
+ string->len = 0;
+ string->allocated_len = G_STRING_PREALLOC;
+ string->str[0] = 0;
+}
+#define g_string_init(gstr) g_string_init_inline (gstr)
+
+G_ALWAYS_INLINE
+static inline char *
+g_string_clear_inline (GString *string,
+ gboolean free_segment)
+{
+ char *segment;
+
+ if (free_segment)
+ {
+ if (string->str != string->buf)
+ g_free (string->str);
+ segment = NULL;
+ }
+ else
+ {
+ if (string->str != string->buf)
+ segment = string->str;
+ else
+ segment = (char *) g_memdup2 (string->str, string->len + 1);
+ }
+
+ g_string_init (string);
+
+ return segment;
+}
+#define g_string_clear(gstr, free_segment) g_string_clear_inline (gstr, free_segment)
+
+G_ALWAYS_INLINE
static inline GString*
g_string_append_c_inline (GString *gstring,
gchar c)
@@ -216,7 +265,6 @@ g_string_truncate_inline (GString *gstring,
gstring->str[gstring->len] = '\0';
return gstring;
}
-
#define g_string_truncate(gstr,len) g_string_truncate_inline (gstr, len)
#if G_GNUC_CHECK_VERSION (2, 0)
diff --git a/glib/tests/string.c b/glib/tests/string.c
index 23f44015f..3136f690b 100644
--- a/glib/tests/string.c
+++ b/glib/tests/string.c
@@ -629,6 +629,44 @@ test_string_replace (void)
}
}
+static void
+test_string_on_stack (void)
+{
+ GString string;
+ char *s;
+
+ g_string_init (&string);
+
+ g_assert_cmpstr (string.str, ==, "");
+ g_assert_cmpint (string.len, ==, 0);
+ g_assert_true (string.str == string.buf);
+
+ g_string_append_printf (&string, "Three %s", "cheese");
+ g_assert_cmpstr (string.str, ==, "Three cheese");
+ g_string_append (&string, " and a big banana");
+ g_assert_cmpstr (string.str, ==, "Three cheese and a big banana");
+
+ g_string_assign (&string, "On a hot summer night, would you offer your throat to the wolf with red roses?");
+ g_assert_cmpstr (string.str, ==, "On a hot summer night, would you offer your throat to the wolf with red roses?");
+
+ g_string_clear (&string, TRUE);
+
+ g_assert_cmpstr (string.str, ==, "");
+ g_assert_cmpint (string.len, ==, 0);
+ g_assert_true (string.str == string.buf);
+
+ g_string_append_printf (&string, "Three %s", "cheese");
+ g_assert_cmpstr (string.str, ==, "Three cheese");
+ g_string_append (&string, " and a big banana");
+ g_assert_cmpstr (string.str, ==, "Three cheese and a big banana");
+
+ s = (g_string_clear) (&string, FALSE);
+
+ g_assert_cmpstr (s, ==, "Three cheese and a big banana");
+
+ g_free (s);
+}
+
int
main (int argc,
char *argv[])
@@ -655,6 +693,7 @@ main (int argc,
g_test_add_func ("/string/test-string-set-size", test_string_set_size);
g_test_add_func ("/string/test-string-to-bytes", test_string_to_bytes);
g_test_add_func ("/string/test-string-replace", test_string_replace);
+ g_test_add_func ("/string/test-string-on-stack", test_string_on_stack);
return g_test_run();
}