[gnc-glib-utils] gnc_g_list_stringjoin to join a GList of strings

It traverses the GList twice (once to calculate the length) but
allocates only once.

Includes snippet from
https://www.joelonsoftware.com/2001/12/11/back-to-basics/
This commit is contained in:
Christopher Lam 2021-08-10 12:55:47 +08:00
parent b1c1272f35
commit e84549926b
3 changed files with 105 additions and 0 deletions

View File

@ -327,3 +327,35 @@ void gnc_gpid_kill(GPid pid)
}
#endif /* G_OS_WIN32 */
}
static inline char*
gnc_strcat (char* dest, const char* src)
{
while (*dest) dest++;
while ((*dest++ = *src++));
return --dest;
}
gchar *
gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep)
{
gint seplen = sep ? strlen(sep) : 0;
gint length = -seplen;
gchar *retval, *p;
if (!list_of_strings)
return NULL;
for (GList *n = list_of_strings; n; n = n->next)
length += strlen ((gchar*)n->data) + seplen;
p = retval = (gchar*) g_malloc0 (length * sizeof (gchar) + 1);
for (GList *n = list_of_strings; n; n = n->next)
{
p = gnc_strcat (p, (gchar*)n->data);
if (n->next && sep)
p = gnc_strcat (p, sep);
}
return retval;
}

View File

@ -183,6 +183,22 @@ void gnc_scm_log_debug(const gchar *msg);
@{
*/
/**
* @brief Return a string joining a GList whose elements are gchar*
* strings. Returns NULL if an empty GList* is passed through. The
* optional sep string will be used as separator. Must be g_freed when
* not needed anymore.
*
* @param list_of_strings A GList of chars*
*
* @param sep a separator or NULL
*
* @return A newly allocated string that has to be g_free'd by the
* caller.
**/
gchar * gnc_g_list_stringjoin (GList *list_of_strings, const gchar *sep);
/** Kill a process. On UNIX send a SIGKILL, on Windows call TerminateProcess.
*
* @param pid The process ID. */

View File

@ -53,6 +53,62 @@ test_gnc_utf8_strip_invalid_and_controls (gconstpointer data)
g_free (msg1);
}
static void
test_g_list_stringjoin (gconstpointer data)
{
GList *test = NULL;
gchar *ret;
ret = gnc_g_list_stringjoin (NULL, NULL);
g_assert (ret == NULL);
ret = gnc_g_list_stringjoin (NULL, ":");
g_assert (ret == NULL);
test = g_list_prepend (test, "one");
ret = gnc_g_list_stringjoin (test, NULL);
g_assert_cmpstr (ret, ==, "one");
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
g_assert_cmpstr (ret, ==, "one");
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
g_assert_cmpstr (ret, ==, "one");
g_free (ret);
test = g_list_prepend (test, "two");
ret = gnc_g_list_stringjoin (test, NULL);
g_assert_cmpstr (ret, ==, "twoone");
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
g_assert_cmpstr (ret, ==, "twoone");
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
g_assert_cmpstr (ret, ==, "two:one");
g_free (ret);
test = g_list_prepend (test, "three");
ret = gnc_g_list_stringjoin (test, NULL);
g_assert_cmpstr (ret, ==, "threetwoone");
g_free (ret);
ret = gnc_g_list_stringjoin (test, "");
g_assert_cmpstr (ret, ==, "threetwoone");
g_free (ret);
ret = gnc_g_list_stringjoin (test, ":");
g_assert_cmpstr (ret, ==, "three:two:one");
g_free (ret);
g_list_free (test);
}
int
main (int argc, char *argv[])
@ -62,6 +118,7 @@ main (int argc, char *argv[])
g_test_init (&argc, &argv, NULL); // initialize test program
g_test_add_data_func ("/core-utils/gnc_utf8_strip_invalid_and_controls invalid utf8", (gconstpointer)invalid_utf8, test_gnc_utf8_strip_invalid_and_controls);
g_test_add_data_func ("/core-utils/gnc_utf8_strip_invalid_and_controls control chars", (gconstpointer)controls, test_gnc_utf8_strip_invalid_and_controls);
g_test_add_data_func ("/core-utils/gnc_g_list_stringjoin", NULL, test_g_list_stringjoin);
return g_test_run();
}