diff --git a/glib/gstrfuncs.c b/glib/gstrfuncs.c index ee934e5d7..3030114e3 100644 --- a/glib/gstrfuncs.c +++ b/glib/gstrfuncs.c @@ -2928,9 +2928,8 @@ g_strrstr_len (const gchar *haystack, * * Since: 2.2 */ -gboolean -g_str_has_suffix (const gchar *str, - const gchar *suffix) +gboolean (g_str_has_suffix) (const gchar *str, + const gchar *suffix) { gsize str_len; gsize suffix_len; @@ -2958,9 +2957,8 @@ g_str_has_suffix (const gchar *str, * * Since: 2.2 */ -gboolean -g_str_has_prefix (const gchar *str, - const gchar *prefix) +gboolean (g_str_has_prefix) (const gchar *str, + const gchar *prefix) { g_return_val_if_fail (str != NULL, FALSE); g_return_val_if_fail (prefix != NULL, FALSE); diff --git a/glib/gstrfuncs.h b/glib/gstrfuncs.h index 3c1dc45ec..8c2f59cf5 100644 --- a/glib/gstrfuncs.h +++ b/glib/gstrfuncs.h @@ -33,6 +33,7 @@ #include #include + #include #include #include @@ -139,11 +140,49 @@ gchar * g_strrstr_len (const gchar *haystack, const gchar *needle); GLIB_AVAILABLE_IN_ALL -gboolean g_str_has_suffix (const gchar *str, - const gchar *suffix); +gboolean (g_str_has_suffix) (const gchar *str, + const gchar *suffix); GLIB_AVAILABLE_IN_ALL -gboolean g_str_has_prefix (const gchar *str, - const gchar *prefix); +gboolean (g_str_has_prefix) (const gchar *str, + const gchar *prefix); + +#if G_GNUC_CHECK_VERSION (2, 0) + +/* A note on the 'x + !x' terms: These are added to workaround a false + * warning in gcc 10< which is ignoring the check 'x != NULL' that is + * set outside of the G_GNUC_EXTENSION scope. Without 'x + !x' it + * would complain that x may be NULL where strlen() and memcmp() + * both require non-null arguments. */ + +#define g_str_has_prefix(STR, PREFIX) \ + ((STR != NULL && PREFIX != NULL && __builtin_constant_p (PREFIX)) ? \ + G_GNUC_EXTENSION ({ \ + const char *const __str = STR; \ + const char *const __prefix = PREFIX; \ + const size_t __str_len = strlen (__str + !__str); \ + const size_t __prefix_len = strlen (__prefix + !__prefix); \ + (__str_len >= __prefix_len) ? \ + memcmp (__str + !__str, \ + __prefix + !__prefix, __prefix_len) == 0 : FALSE; \ + }) \ + : \ + (g_str_has_prefix) (STR, PREFIX)) + +#define g_str_has_suffix(STR, SUFFIX) \ + ((STR != NULL && SUFFIX != NULL && __builtin_constant_p (SUFFIX)) ? \ + G_GNUC_EXTENSION ({ \ + const char *const __str = STR; \ + const char *const __suffix = SUFFIX; \ + const size_t __str_len = strlen (__str + !__str); \ + const size_t __suffix_len = strlen (__suffix + !__suffix); \ + (__str_len >= __suffix_len) ? \ + memcmp (__str + !__str + __str_len - __suffix_len, \ + __suffix + !__suffix, __suffix_len) == 0 : FALSE; \ + }) \ + : \ + (g_str_has_suffix) (STR, SUFFIX)) + +#endif /* G_GNUC_CHECK_VERSION (2, 0) */ /* String to/from double conversion functions */ diff --git a/glib/tests/strfuncs.c b/glib/tests/strfuncs.c index 0ae3f89c1..6c0d43090 100644 --- a/glib/tests/strfuncs.c +++ b/glib/tests/strfuncs.c @@ -1200,89 +1200,132 @@ test_strdelimit (void) g_free (string); } -/* Testing g_str_has_prefix() */ +/* Testing g_str_has_prefix() function avoiding the optimizing macro */ static void test_has_prefix (void) { - gboolean res; - if (g_test_undefined ()) { g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*!= NULL*"); - res = g_str_has_prefix ("foo", NULL); + g_assert_false ((g_str_has_prefix) ("foo", NULL)); g_test_assert_expected_messages (); - g_assert_false (res); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*!= NULL*"); - res = g_str_has_prefix (NULL, "foo"); + g_assert_false ((g_str_has_prefix) (NULL, "foo")); g_test_assert_expected_messages (); - g_assert_false (res); } - res = g_str_has_prefix ("foo", "bar"); - g_assert_cmpint (res, ==, FALSE); + /* Having a string smaller than the prefix */ + g_assert_false ((g_str_has_prefix) ("aa", "aaa")); - res = g_str_has_prefix ("foo", "foobar"); - g_assert_cmpint (res, ==, FALSE); + /* Negative tests */ + g_assert_false ((g_str_has_prefix) ("foo", "bar")); + g_assert_false ((g_str_has_prefix) ("foo", "foobar")); + g_assert_false ((g_str_has_prefix) ("foobar", "bar")); - res = g_str_has_prefix ("foobar", "bar"); - g_assert_cmpint (res, ==, FALSE); - - res = g_str_has_prefix ("foobar", "foo"); - g_assert_cmpint (res, ==, TRUE); - - res = g_str_has_prefix ("foo", ""); - g_assert_cmpint (res, ==, TRUE); - - res = g_str_has_prefix ("foo", "foo"); - g_assert_cmpint (res, ==, TRUE); - - res = g_str_has_prefix ("", ""); - g_assert_cmpint (res, ==, TRUE); + /* Positive tests */ + g_assert_true ((g_str_has_prefix) ("foobar", "foo")); + g_assert_true ((g_str_has_prefix) ("foo", "")); + g_assert_true ((g_str_has_prefix) ("foo", "foo")); + g_assert_true ((g_str_has_prefix) ("", "")); } +/* Testing g_str_has_prefix() optimized macro */ +static void +test_has_prefix_macro (void) +{ + if (g_test_undefined ()) + { + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*!= NULL*"); + g_assert_false (g_str_has_prefix ("foo", NULL)); + g_test_assert_expected_messages (); + + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*!= NULL*"); + g_assert_false (g_str_has_prefix (NULL, "foo")); + g_test_assert_expected_messages (); + } + + /* Having a string smaller than the prefix */ + g_assert_false (g_str_has_prefix ("aa", "aaa")); + + /* Negative tests */ + g_assert_false (g_str_has_prefix ("foo", "bar")); + g_assert_false (g_str_has_prefix ("foo", "foobar")); + g_assert_false (g_str_has_prefix ("foobar", "bar")); + + /* Positive tests */ + g_assert_true (g_str_has_prefix ("foobar", "foo")); + g_assert_true (g_str_has_prefix ("foo", "")); + g_assert_true (g_str_has_prefix ("foo", "foo")); + g_assert_true (g_str_has_prefix ("", "")); +} + +/* Testing g_str_has_suffix() function avoiding the optimizing macro */ static void test_has_suffix (void) { - gboolean res; - if (g_test_undefined ()) { g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*!= NULL*"); - res = g_str_has_suffix ("foo", NULL); + g_assert_false ((g_str_has_suffix) ("foo", NULL)); g_test_assert_expected_messages (); - g_assert_false (res); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, "*assertion*!= NULL*"); - res = g_str_has_suffix (NULL, "foo"); + g_assert_false ((g_str_has_suffix) (NULL, "foo")); g_test_assert_expected_messages (); - g_assert_false (res); } - res = g_str_has_suffix ("foo", "bar"); - g_assert_false (res); + /* Having a string smaller than the suffix */ + g_assert_false ((g_str_has_suffix) ("aa", "aaa")); - res = g_str_has_suffix ("bar", "foobar"); - g_assert_false (res); + /* Negative tests */ + g_assert_false ((g_str_has_suffix) ("foo", "bar")); + g_assert_false ((g_str_has_suffix) ("bar", "foobar")); + g_assert_false ((g_str_has_suffix) ("foobar", "foo")); - res = g_str_has_suffix ("foobar", "foo"); - g_assert_false (res); + /* Positive tests */ + g_assert_true ((g_str_has_suffix) ("foobar", "bar")); + g_assert_true ((g_str_has_suffix) ("foo", "")); + g_assert_true ((g_str_has_suffix) ("foo", "foo")); + g_assert_true ((g_str_has_suffix) ("", "")); +} - res = g_str_has_suffix ("foobar", "bar"); - g_assert_true (res); +/* Testing g_str_has_prefix() optimized macro */ +static void +test_has_suffix_macro (void) +{ + if (g_test_undefined ()) + { + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*!= NULL*"); + g_assert_false (g_str_has_suffix ("foo", NULL)); + g_test_assert_expected_messages (); - res = g_str_has_suffix ("foo", ""); - g_assert_true (res); + g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_CRITICAL, + "*assertion*!= NULL*"); + g_assert_false (g_str_has_suffix (NULL, "foo")); + g_test_assert_expected_messages (); + } - res = g_str_has_suffix ("foo", "foo"); - g_assert_true (res); + /* Having a string smaller than the suffix */ + g_assert_false (g_str_has_suffix ("aa", "aaa")); - res = g_str_has_suffix ("", ""); - g_assert_true (res); + /* Negative tests */ + g_assert_false (g_str_has_suffix ("foo", "bar")); + g_assert_false (g_str_has_suffix ("bar", "foobar")); + g_assert_false (g_str_has_suffix ("foobar", "foo")); + + /* Positive tests */ + g_assert_true (g_str_has_suffix ("foobar", "bar")); + g_assert_true (g_str_has_suffix ("foo", "")); + g_assert_true (g_str_has_suffix ("foo", "foo")); + g_assert_true (g_str_has_suffix ("", "")); } static void @@ -2587,7 +2630,9 @@ main (int argc, g_test_add_func ("/strfuncs/ascii_strtod", test_ascii_strtod); g_test_add_func ("/strfuncs/bounds-check", test_bounds); g_test_add_func ("/strfuncs/has-prefix", test_has_prefix); + g_test_add_func ("/strfuncs/has-prefix-macro", test_has_prefix_macro); g_test_add_func ("/strfuncs/has-suffix", test_has_suffix); + g_test_add_func ("/strfuncs/has-suffix-macro", test_has_suffix_macro); g_test_add_func ("/strfuncs/memdup", test_memdup); g_test_add_func ("/strfuncs/memdup2", test_memdup2); g_test_add_func ("/strfuncs/set_str", test_set_str);