diff options
-rw-r--r-- | docs/reference/glib/glib-sections.txt.in | 2 | ||||
-rw-r--r-- | glib/gstring.c | 73 | ||||
-rw-r--r-- | glib/gstring.h | 50 | ||||
-rw-r--r-- | glib/tests/string.c | 39 |
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(); } |