diff --git a/glib/gstring.c b/glib/gstring.c index 2a399ee21..645746c9a 100644 --- a/glib/gstring.c +++ b/glib/gstring.c @@ -214,6 +214,38 @@ g_string_new_len (const gchar *init, } } +/** + * g_string_copy: + * @string: a string + * + * Copies the [struct@GLib.String] instance and its contents. + * + * This will preserve the allocation length of the [struct@GLib.String] in the + * copy. + * + * Returns: (transfer full): a copy of @string + * Since: 2.86 + */ +GString * +g_string_copy (GString *string) +{ + GString *copy = NULL; + + g_return_val_if_fail (string != NULL, NULL); + + copy = g_slice_new (GString); + copy->allocated_len = string->allocated_len; + copy->len = string->len; + + /* We can’t just strdup(string->str) here because it may contain embedded nuls. */ + copy->str = g_malloc (string->allocated_len); + if (string->str != NULL && string->len > 0) + memcpy (copy->str, string->str, string->len); + copy->str[copy->len] = '\0'; + + return g_steal_pointer (©); +} + /** * g_string_free: * @string: (transfer full): a #GString diff --git a/glib/gstring.h b/glib/gstring.h index 3e66367a0..e817176c9 100644 --- a/glib/gstring.h +++ b/glib/gstring.h @@ -58,6 +58,8 @@ GString* g_string_new_len (const gchar *init, gssize len); GLIB_AVAILABLE_IN_ALL GString* g_string_sized_new (gsize dfl_size); +GLIB_AVAILABLE_IN_2_86 +GString *g_string_copy (GString *string); GLIB_AVAILABLE_IN_ALL gchar* (g_string_free) (GString *string, gboolean free_segment); diff --git a/glib/tests/string.c b/glib/tests/string.c index ed7179eb3..fc9d8458a 100644 --- a/glib/tests/string.c +++ b/glib/tests/string.c @@ -767,6 +767,40 @@ test_string_new_take_null (void) g_string_free (g_steal_pointer (&string), TRUE); } +static void +test_string_copy (void) +{ + GString *string1 = NULL, *string2 = NULL; + + string1 = g_string_new ("hello"); + string2 = g_string_copy (string1); + g_assert_cmpstr (string1->str, ==, string2->str); + g_assert_true (string1->str != string2->str); + g_assert_cmpuint (string1->len, ==, string2->len); + g_assert_cmpuint (string2->allocated_len, ==, string2->allocated_len); + g_string_free (string1, TRUE); + g_string_free (string2, TRUE); + + string1 = g_string_sized_new (100); + string2 = g_string_copy (string1); + g_assert_cmpstr (string1->str, ==, string2->str); + g_assert_true (string1->str != string2->str); + g_assert_cmpuint (string1->len, ==, string2->len); + g_assert_cmpuint (string2->allocated_len, ==, string2->allocated_len); + g_string_free (string1, TRUE); + g_string_free (string2, TRUE); + + string1 = g_string_sized_new (200); + g_string_append_len (string1, "test with embedded\0nuls", 25); + string2 = g_string_copy (string1); + g_assert_cmpmem (string1->str, string1->len, string2->str, string2->len); + g_assert_true (string1->str != string2->str); + g_assert_cmpuint (string1->len, ==, string2->len); + g_assert_cmpuint (string2->allocated_len, ==, string2->allocated_len); + g_string_free (string1, TRUE); + g_string_free (string2, TRUE); +} + int main (int argc, char *argv[]) @@ -796,6 +830,7 @@ main (int argc, g_test_add_func ("/string/steal", test_string_steal); g_test_add_func ("/string/new-take", test_string_new_take); g_test_add_func ("/string/new-take/null", test_string_new_take_null); + g_test_add_func ("/string/copy", test_string_copy); return g_test_run(); }