GString: Support on-stack use

Move the preallocation into the GString struct,
and add g_string_init and g_string_clear to
enable on-stack use of GString.
This commit is contained in:
Matthias Clasen 2023-01-17 17:56:41 -05:00
parent 1aa5304109
commit 1e38e6f53f
4 changed files with 149 additions and 15 deletions

View File

@ -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

View File

@ -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
@ -100,6 +115,23 @@ g_string_maybe_expand (GString *string,
g_string_expand (string, len);
}
/**
* 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;
}
@ -189,6 +217,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
@ -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);

View File

@ -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,9 +167,54 @@ 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,
@ -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)

View File

@ -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();
}