Add a g_ref_string_new_len() to allow creating from non-NUL byte arrays

A lot of GLib APIs provide a string length and explicitly say that the strings
are not NUL terminated. For instance, parsing XML using GMarkupParser or
reading packed binary strings from mmapped data files.
This commit is contained in:
Richard Hughes 2018-07-09 11:57:41 +01:00
parent 2585099a5d
commit dad58d7392
4 changed files with 76 additions and 0 deletions

View File

@ -3497,6 +3497,7 @@ g_arc_box_get_size
<FILE>refstring</FILE>
g_ref_string_new
g_ref_string_new_intern
g_ref_string_new_len
g_ref_string_acquire
g_ref_string_release
g_ref_string_length

View File

@ -130,6 +130,39 @@ g_ref_string_new (const char *str)
return res;
}
/**
* g_ref_string_new_len:
* @str: (not nullable): a string
* @len: length of @str to use, or -1 if @str is nul-terminated
*
* Creates a new reference counted string and copies the contents of @str
* into it, up to @len bytes.
*
* Since this function does not stop at nul bytes, it is the caller's
* responsibility to ensure that @str has at least @len addressable bytes.
*
* Returns: (transfer full) (not nullable): the newly created reference counted string
*
* Since: 2.58
*/
char *
g_ref_string_new_len (const char *str, gssize len)
{
char *res;
g_return_val_if_fail (str != NULL, NULL);
if (len < 0)
return g_ref_string_new (str);
/* allocate then copy as str[len] may not be readable */
res = (char *) g_atomic_rc_box_alloc ((gsize) len + 1);
memcpy (res, str, len);
res[len] = '\0';
return res;
}
/* interned_str_equal: variant of g_str_equal() that compares
* pointers as well as contents; this avoids running strcmp()
* on arbitrarily long strings, as it's more likely to have

View File

@ -26,6 +26,9 @@ G_BEGIN_DECLS
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_new (const char *str);
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_new_len (const char *str,
gssize len);
GLIB_AVAILABLE_IN_2_58
char * g_ref_string_new_intern (const char *str);
GLIB_AVAILABLE_IN_2_58

View File

@ -36,6 +36,42 @@ test_refstring_base (void)
g_ref_string_release (s);
}
/* test_refstring_length: Test the _len variant */
static void
test_refstring_length (void)
{
char buf[] = {'h', 'e', 'l', 'l', 'o'}; /* no NUL */
char *s = g_ref_string_new_len (buf, 5);
g_assert_cmpstr (s, ==, "hello");
g_assert_cmpint (strlen (s), ==, strlen ("hello"));
g_assert_cmpuint (g_ref_string_length (s), ==, strlen ("hello"));
g_ref_string_release (s);
}
/* test_refstring_length: Test the _len variant with no size set */
static void
test_refstring_length_auto (void)
{
char *s = g_ref_string_new_len ("hello", -1);
g_assert_cmpstr (s, ==, "hello");
g_assert_cmpuint (g_ref_string_length (s), ==, strlen ("hello"));
g_ref_string_release (s);
}
/* test_refstring_length_nuls: Test the _len variant */
static void
test_refstring_length_nuls (void)
{
char buf[] = {'h', 'e', '\0', 'l', 'o'}; /* no NUL */
char *s = g_ref_string_new_len (buf, 5);
g_assert_cmpstr (s, ==, "he");
g_assert_cmpint (memcmp (s, "he\0lo", 5), ==, 0);
g_assert_cmpuint (g_ref_string_length (s), ==, 5);
g_ref_string_release (s);
}
/* test_refstring_intern: Test the interning API of GRefString */
static void
test_refstring_intern (void)
@ -76,6 +112,9 @@ main (int argc,
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/refstring/base", test_refstring_base);
g_test_add_func ("/refstring/length", test_refstring_length);
g_test_add_func ("/refstring/length-auto", test_refstring_length_auto);
g_test_add_func ("/refstring/length-nuls", test_refstring_length_nuls);
g_test_add_func ("/refstring/intern", test_refstring_intern);
return g_test_run ();