diff --git a/glib/gstrfuncs.c b/glib/gstrfuncs.c index 8525e4692..665b2071b 100644 --- a/glib/gstrfuncs.c +++ b/glib/gstrfuncs.c @@ -352,12 +352,12 @@ get_C_locale (void) * Returns: a newly-allocated copy of @str */ gchar* -g_strdup (const gchar *str) +(g_strdup) (const gchar *str) { gchar *new_str; gsize length; - if (str) + if G_LIKELY (str) { length = strlen (str) + 1; new_str = g_new (char, length); diff --git a/glib/gstrfuncs.h b/glib/gstrfuncs.h index 56d78cf87..569f96977 100644 --- a/glib/gstrfuncs.h +++ b/glib/gstrfuncs.h @@ -153,7 +153,7 @@ gboolean (g_str_has_prefix) (const gchar *str, * Without it, it thinks strlen and memcmp may be getting passed NULL * despite the explicit check for NULL right above the calls. */ -#define _G_STR_NONNULL(x) (x + !x) +#define _G_STR_NONNULL(x) ((x) + !(x)) #define g_str_has_prefix(STR, PREFIX) \ (__builtin_constant_p (PREFIX)? \ @@ -203,6 +203,23 @@ gboolean (g_str_has_prefix) (const gchar *str, (g_str_has_suffix) (STR, SUFFIX) \ ) +#define g_strdup(STR) \ + (__builtin_constant_p ((STR)) ? \ + (G_LIKELY ((STR) != NULL) ? \ + G_GNUC_EXTENSION ({ \ + const char *const ___str = ((STR)); \ + const char *const __str = _G_STR_NONNULL (___str); \ + const size_t __str_len = strlen (__str) + 1; \ + char *__dup_str = (char *) g_malloc (__str_len); \ + (char *) memcpy (__dup_str, __str, __str_len); \ + }) \ + : \ + (char *) (NULL) \ + ) \ + : \ + (g_strdup) ((STR)) \ + ) + #endif /* !defined (__GTK_DOC_IGNORE__) && !defined (__GI_SCANNER__) */ #endif /* G_GNUC_CHECK_VERSION (2, 0) */ @@ -279,7 +296,7 @@ gchar* g_strup (gchar *string); * ought to be freed with g_free from the caller at some point. */ GLIB_AVAILABLE_IN_ALL -gchar* g_strdup (const gchar *str) G_GNUC_MALLOC; +gchar* (g_strdup) (const gchar *str) G_GNUC_MALLOC; GLIB_AVAILABLE_IN_ALL gchar* g_strdup_printf (const gchar *format, ...) G_GNUC_PRINTF (1, 2) G_GNUC_MALLOC; diff --git a/glib/tests/cxx.cpp b/glib/tests/cxx.cpp index 6e4727f2a..8616a18db 100644 --- a/glib/tests/cxx.cpp +++ b/glib/tests/cxx.cpp @@ -304,6 +304,7 @@ test_str_equal (void) { const char *str_a = "a"; char *str_b = g_strdup ("b"); + char *str_null = g_strdup (NULL); gconstpointer str_a_ptr = str_a, str_b_ptr = str_b; const unsigned char *str_c = (const unsigned char *) "c"; @@ -317,10 +318,36 @@ test_str_equal (void) g_assert_true (g_str_equal (str_a, str_a_ptr)); g_assert_false (g_str_equal (str_a_ptr, str_b_ptr)); g_assert_false (g_str_equal (str_c, str_b)); + g_assert_cmpstr (str_b, !=, str_null); g_free (str_b); } +static void +test_strdup (void) +{ + gchar *str; + + g_assert_null ((g_strdup) (NULL)); + + str = (g_strdup) ("C++ is cool too!"); + g_assert_nonnull (str); + g_assert_cmpstr (str, ==, "C++ is cool too!"); + g_free (str); +} + +static void +test_strdup_macro (void) +{ + gchar *str; + + g_assert_null (g_strdup (NULL)); + + str = g_strdup ("C++ is cool too!"); + g_assert_nonnull (str); + g_assert_cmpstr (str, ==, "C++ is cool too!"); + g_free (str); +} static void test_string_append (void) @@ -444,6 +471,8 @@ main (int argc, char *argv[]) g_test_add_func ("/C++/clear-pointer", test_clear_pointer); g_test_add_func ("/C++/steal-pointer", test_steal_pointer); g_test_add_func ("/C++/str-equal", test_str_equal); + g_test_add_func ("/C++/strdup", test_strdup); + g_test_add_func ("/C++/strdup/macro", test_strdup_macro); g_test_add_func ("/C++/string-append", test_string_append); return g_test_run (); diff --git a/glib/tests/strfuncs.c b/glib/tests/strfuncs.c index 4551f1c82..96f378815 100644 --- a/glib/tests/strfuncs.c +++ b/glib/tests/strfuncs.c @@ -499,12 +499,53 @@ test_strdup (void) { gchar *str; + g_assert_null ((g_strdup) (NULL)); + + str = (g_strdup) (GLIB_TEST_STRING); + g_assert_nonnull (str); + g_assert_cmpstr (str, ==, GLIB_TEST_STRING); + + char *other_str = (g_strdup) (str); + g_free (str); + + g_assert_nonnull (other_str); + g_assert_cmpstr (other_str, ==, GLIB_TEST_STRING); + g_clear_pointer (&other_str, g_free); + + str = (g_strdup) (""); + g_assert_cmpint (str[0], ==, '\0'); + g_assert_cmpstr (str, ==, ""); + g_clear_pointer (&str, g_free); +} + +static void +test_strdup_inline (void) +{ + gchar *str; + + #if G_GNUC_CHECK_VERSION (2, 0) + #ifndef g_strdup + #error g_strdup() should be defined as a macro in this platform! + #endif + #else + g_test_incomplete ("g_strdup() is not inlined in this platform"); + #endif + + /* Testing inline version of g_strdup() function with various positive and + * negative cases */ + g_assert_null (g_strdup (NULL)); str = g_strdup (GLIB_TEST_STRING); g_assert_nonnull (str); g_assert_cmpstr (str, ==, GLIB_TEST_STRING); - g_free (str); + + char *other_str = g_strdup (str); + g_clear_pointer (&str, g_free); + + g_assert_nonnull (other_str); + g_assert_cmpstr (other_str, ==, GLIB_TEST_STRING); + g_clear_pointer (&other_str, g_free); str = g_strdup (""); g_assert_cmpint (str[0], ==, '\0'); @@ -2698,6 +2739,7 @@ main (int argc, g_test_add_func ("/strfuncs/strconcat", test_strconcat); g_test_add_func ("/strfuncs/strdelimit", test_strdelimit); g_test_add_func ("/strfuncs/strdup", test_strdup); + g_test_add_func ("/strfuncs/strdup/inline", test_strdup_inline); g_test_add_func ("/strfuncs/strdup-printf", test_strdup_printf); g_test_add_func ("/strfuncs/strdupv", test_strdupv); g_test_add_func ("/strfuncs/strerror", test_strerror);