From b7541e8eea1c5a6f4676e79fa3cd2f40a78a49d0 Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Tue, 7 Jul 2015 15:41:52 -0700 Subject: Added convenience method gpr_strjoin_sep --- src/core/support/string.c | 15 ++++++++++++++- src/core/support/string.h | 6 ++++++ 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'src/core') diff --git a/src/core/support/string.c b/src/core/support/string.c index 6a80ccc841..f85f656da4 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -153,6 +153,12 @@ int gpr_ltoa(long value, char *string) { } char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { + return gpr_strjoin_sep(strs, nstrs, "", final_length); +} + +char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, + size_t *final_length) { + const size_t sep_len = strlen(sep); size_t out_length = 0; size_t i; char *out; @@ -160,12 +166,19 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { out_length += strlen(strs[i]); } out_length += 1; /* null terminator */ + if (nstrs > 0) { + out_length += sep_len * (nstrs - 1); /* separators */ + } out = gpr_malloc(out_length); out_length = 0; for (i = 0; i < nstrs; i++) { - size_t slen = strlen(strs[i]); + const size_t slen = strlen(strs[i]); memcpy(out + out_length, strs[i], slen); out_length += slen; + if (sep_len > 0 && nstrs > 0 && i < nstrs - 1) { + memcpy(out + out_length, sep, sep_len); + out_length += sep_len; + } } out[out_length] = 0; if (final_length != NULL) { diff --git a/src/core/support/string.h b/src/core/support/string.h index 31e9fcb5e9..a4da485dce 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -72,6 +72,12 @@ void gpr_reverse_bytes(char *str, int len); if it is non-null. */ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); +/* Join a set of strings using a separator, returning the resulting string. + Total combined length (excluding null terminator) is returned in total_length + if it is non-null. */ +char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, + size_t *total_length); + /* A vector of strings... for building up a final string one piece at a time */ typedef struct { char **strs; -- cgit v1.2.3 From a16cf1ee123126a8274bb216cf824e277e8b9b97 Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Tue, 7 Jul 2015 17:02:37 -0700 Subject: PR comments + some more tests. --- src/core/support/string.c | 6 +++--- test/core/support/string_test.c | 7 +++++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'src/core') diff --git a/src/core/support/string.c b/src/core/support/string.c index f85f656da4..8f59945c59 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -173,12 +173,12 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, out_length = 0; for (i = 0; i < nstrs; i++) { const size_t slen = strlen(strs[i]); - memcpy(out + out_length, strs[i], slen); - out_length += slen; - if (sep_len > 0 && nstrs > 0 && i < nstrs - 1) { + if (i != 0) { memcpy(out + out_length, sep, sep_len); out_length += sep_len; } + memcpy(out + out_length, strs[i], slen); + out_length += slen; } out[out_length] = 0; if (final_length != NULL) { diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index 24e28d68dd..30d97de1a5 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -176,10 +176,17 @@ static void test_strjoin_sep(void) { GPR_ASSERT(0 == strcmp("one, two, three, four", joined)); gpr_free(joined); + /* empty separator */ + joined = gpr_strjoin_sep(parts, 4, "", &joined_len); + GPR_ASSERT(0 == strcmp("onetwothreefour", joined)); + gpr_free(joined); + + /* degenerated case specifying zero input parts */ joined = gpr_strjoin_sep(parts, 0, ", ", &joined_len); GPR_ASSERT(0 == strcmp("", joined)); gpr_free(joined); + /* single part should have no separator */ joined = gpr_strjoin_sep(parts, 1, ", ", &joined_len); GPR_ASSERT(0 == strcmp("one", joined)); gpr_free(joined); -- cgit v1.2.3 From 14f1c271927976db4918cbb88529de65a874465a Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Tue, 7 Jul 2015 21:56:02 -0700 Subject: Added gpr_strsplit (with tests) --- src/core/support/string.c | 22 +++++++++++++++++ src/core/support/string.h | 4 ++++ test/core/support/string_test.c | 53 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+) (limited to 'src/core') diff --git a/src/core/support/string.c b/src/core/support/string.c index 8f59945c59..8bf091d8d2 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -38,6 +38,7 @@ #include #include +#include #include #include @@ -187,6 +188,27 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, return out; } +static void do_nothing(void *ignored) {} +gpr_slice_buffer *gpr_strsplit(const char *str, const char *sep) { + const size_t sep_len = strlen(sep); + const char *splitpoint = str; + gpr_slice_buffer *parts; + + GPR_ASSERT(sep_len > 0); + + parts = gpr_malloc(sizeof(gpr_slice_buffer)); + gpr_slice_buffer_init(parts); + + for (; (splitpoint = strstr(str, sep)) != NULL; splitpoint += sep_len) { + gpr_slice_buffer_add( + parts, gpr_slice_new((void *)str, splitpoint - str, do_nothing)); + str += (splitpoint - str + sep_len); + } + gpr_slice_buffer_add(parts, + gpr_slice_new((void *)str, strlen(str), do_nothing)); + return parts; +} + void gpr_strvec_init(gpr_strvec *sv) { memset(sv, 0, sizeof(*sv)); } diff --git a/src/core/support/string.h b/src/core/support/string.h index a4da485dce..efb11778b9 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -37,6 +37,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -78,6 +79,9 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, size_t *total_length); +/** Split \a str at separator \a sep. */ +gpr_slice_buffer *gpr_strsplit(const char *str, const char *sep); + /* A vector of strings... for building up a final string one piece at a time */ typedef struct { char **strs; diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index 30d97de1a5..b7ee7cad3b 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -192,6 +192,58 @@ static void test_strjoin_sep(void) { gpr_free(joined); } +static void test_strsplit(void) { + gpr_slice_buffer* parts; + LOG_TEST_NAME("test_strsplit"); + + parts = gpr_strsplit("one, two, three, four", ", "); + GPR_ASSERT(4 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "two")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[2], "three")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[3], "four")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); + + /* separator not present in string */ + parts = gpr_strsplit("one two three four", ", "); + GPR_ASSERT(1 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one two three four")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); + + /* separator at the end */ + parts = gpr_strsplit("foo,", ","); + GPR_ASSERT(2 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "foo")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); + + /* separator at the beginning */ + parts = gpr_strsplit(",foo", ","); + GPR_ASSERT(2 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "foo")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); + + /* standalone separator */ + parts = gpr_strsplit(",", ","); + GPR_ASSERT(2 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); + + /* empty input */ + parts = gpr_strsplit("", ","); + GPR_ASSERT(1 == parts->count); + GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); + gpr_slice_buffer_destroy(parts); + gpr_free(parts); +} + int main(int argc, char **argv) { grpc_test_init(argc, argv); test_strdup(); @@ -200,5 +252,6 @@ int main(int argc, char **argv) { test_asprintf(); test_strjoin(); test_strjoin_sep(); + test_strsplit(); return 0; } -- cgit v1.2.3 From 074e2247ebd91b1fdb70e0b1164940253f2196fa Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Tue, 7 Jul 2015 23:43:22 -0700 Subject: Split function now takes slices as input, performs no allocs --- src/core/support/string.c | 56 ++++++++++++++++++++++++++++++----------- src/core/support/string.h | 5 ++-- test/core/support/string_test.c | 49 ++++++++++++++++++++++++------------ 3 files changed, 77 insertions(+), 33 deletions(-) (limited to 'src/core') diff --git a/src/core/support/string.c b/src/core/support/string.c index 8bf091d8d2..60590d5653 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -188,25 +188,51 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, return out; } -static void do_nothing(void *ignored) {} -gpr_slice_buffer *gpr_strsplit(const char *str, const char *sep) { - const size_t sep_len = strlen(sep); - const char *splitpoint = str; - gpr_slice_buffer *parts; +/** Finds the initial (\a begin) and final (\a end) offsets of the next + * substring from \a str + \a read_offset until the next \a sep or the end of \a + * str. + * + * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */ +static int slice_find_separator_offset(const gpr_slice str, + const gpr_slice sep, + const size_t read_offset, + size_t *begin, + size_t *end) { + size_t i; + const gpr_uint8 *str_ptr = GPR_SLICE_START_PTR(str) + read_offset; + const gpr_uint8 *sep_ptr = GPR_SLICE_START_PTR(sep); + const size_t str_len = GPR_SLICE_LENGTH(str) - read_offset; + const size_t sep_len = GPR_SLICE_LENGTH(sep); + if (str_len < sep_len) { + return 0; + } - GPR_ASSERT(sep_len > 0); + for (i = 0; i <= str_len - sep_len; i++) { + if (memcmp(str_ptr + i, sep_ptr, sep_len) == 0) { + *begin = read_offset; + *end = read_offset + i; + return 1; + } + } + return 0; +} - parts = gpr_malloc(sizeof(gpr_slice_buffer)); - gpr_slice_buffer_init(parts); +void gpr_slice_split(gpr_slice str, gpr_slice sep, gpr_slice_buffer *dst) { + const size_t sep_len = GPR_SLICE_LENGTH(sep); + size_t begin, end; + + GPR_ASSERT(sep_len > 0); - for (; (splitpoint = strstr(str, sep)) != NULL; splitpoint += sep_len) { - gpr_slice_buffer_add( - parts, gpr_slice_new((void *)str, splitpoint - str, do_nothing)); - str += (splitpoint - str + sep_len); + if (slice_find_separator_offset(str, sep, 0, &begin, &end) != 0) { + do { + gpr_slice_buffer_add_indexed(dst, gpr_slice_sub(str, begin, end)); + } while (slice_find_separator_offset(str, sep, end + sep_len, &begin, + &end) != 0); + gpr_slice_buffer_add_indexed( + dst, gpr_slice_sub(str, end + sep_len, GPR_SLICE_LENGTH(str))); + } else { /* no sep found, add whole input */ + gpr_slice_buffer_add_indexed(dst, gpr_slice_ref(str)); } - gpr_slice_buffer_add(parts, - gpr_slice_new((void *)str, strlen(str), do_nothing)); - return parts; } void gpr_strvec_init(gpr_strvec *sv) { diff --git a/src/core/support/string.h b/src/core/support/string.h index efb11778b9..4964e7d2da 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -79,8 +79,9 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, size_t *total_length); -/** Split \a str at separator \a sep. */ -gpr_slice_buffer *gpr_strsplit(const char *str, const char *sep); +/** Split \a str by the separator \a sep. Results are stored in \a dst, which + * should be a properly initialized instance. */ +void gpr_slice_split(gpr_slice str, gpr_slice sep, gpr_slice_buffer *dst); /* A vector of strings... for building up a final string one piece at a time */ typedef struct { diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index b7ee7cad3b..4fd11302d0 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -194,52 +194,69 @@ static void test_strjoin_sep(void) { static void test_strsplit(void) { gpr_slice_buffer* parts; + gpr_slice str; + gpr_slice sep; + LOG_TEST_NAME("test_strsplit"); - parts = gpr_strsplit("one, two, three, four", ", "); + parts = gpr_malloc(sizeof(gpr_slice_buffer)); + gpr_slice_buffer_init(parts); + + str = gpr_slice_from_copied_string("one, two, three, four"); + sep = gpr_slice_from_copied_string(", "); + gpr_slice_split(str, sep, parts); GPR_ASSERT(4 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "two")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[2], "three")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[3], "four")); - gpr_slice_buffer_destroy(parts); - gpr_free(parts); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); /* separator not present in string */ - parts = gpr_strsplit("one two three four", ", "); + str = gpr_slice_from_copied_string("one two three four"); + gpr_slice_split(str, sep, parts); GPR_ASSERT(1 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one two three four")); - gpr_slice_buffer_destroy(parts); - gpr_free(parts); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); /* separator at the end */ - parts = gpr_strsplit("foo,", ","); + str = gpr_slice_from_copied_string("foo, "); + gpr_slice_split(str, sep, parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "foo")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); - gpr_slice_buffer_destroy(parts); - gpr_free(parts); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); /* separator at the beginning */ - parts = gpr_strsplit(",foo", ","); + str = gpr_slice_from_copied_string(", foo"); + gpr_slice_split(str, sep, parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "foo")); - gpr_slice_buffer_destroy(parts); - gpr_free(parts); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); /* standalone separator */ - parts = gpr_strsplit(",", ","); + str = gpr_slice_from_copied_string(", "); + gpr_slice_split(str, sep, parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); - gpr_slice_buffer_destroy(parts); - gpr_free(parts); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); /* empty input */ - parts = gpr_strsplit("", ","); + str = gpr_slice_from_copied_string(""); + gpr_slice_split(str, sep, parts); GPR_ASSERT(1 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); + + gpr_slice_unref(sep); gpr_slice_buffer_destroy(parts); gpr_free(parts); } -- cgit v1.2.3 From ffe3d2b1908933a149932502fd0607239ccef88a Mon Sep 17 00:00:00 2001 From: David Garcia Quintas Date: Wed, 8 Jul 2015 20:13:41 -0700 Subject: Modified gpr_slice_split to take a char* separator. --- src/core/support/string.c | 11 +++++------ src/core/support/string.h | 2 +- test/core/support/string_test.c | 21 +++++++++------------ 3 files changed, 15 insertions(+), 19 deletions(-) (limited to 'src/core') diff --git a/src/core/support/string.c b/src/core/support/string.c index 74d98de5c1..9babbd910a 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -215,21 +215,20 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, * * Returns 1 and updates \a begin and \a end. Returns 0 otherwise. */ static int slice_find_separator_offset(const gpr_slice str, - const gpr_slice sep, + const char *sep, const size_t read_offset, size_t *begin, size_t *end) { size_t i; const gpr_uint8 *str_ptr = GPR_SLICE_START_PTR(str) + read_offset; - const gpr_uint8 *sep_ptr = GPR_SLICE_START_PTR(sep); const size_t str_len = GPR_SLICE_LENGTH(str) - read_offset; - const size_t sep_len = GPR_SLICE_LENGTH(sep); + const size_t sep_len = strlen(sep); if (str_len < sep_len) { return 0; } for (i = 0; i <= str_len - sep_len; i++) { - if (memcmp(str_ptr + i, sep_ptr, sep_len) == 0) { + if (memcmp(str_ptr + i, sep, sep_len) == 0) { *begin = read_offset; *end = read_offset + i; return 1; @@ -238,8 +237,8 @@ static int slice_find_separator_offset(const gpr_slice str, return 0; } -void gpr_slice_split(gpr_slice str, gpr_slice sep, gpr_slice_buffer *dst) { - const size_t sep_len = GPR_SLICE_LENGTH(sep); +void gpr_slice_split(gpr_slice str, const char *sep, gpr_slice_buffer *dst) { + const size_t sep_len = strlen(sep); size_t begin, end; GPR_ASSERT(sep_len > 0); diff --git a/src/core/support/string.h b/src/core/support/string.h index 819ce4ac83..3ac4abeef8 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -86,7 +86,7 @@ char *gpr_strjoin_sep(const char **strs, size_t nstrs, const char *sep, /** Split \a str by the separator \a sep. Results are stored in \a dst, which * should be a properly initialized instance. */ -void gpr_slice_split(gpr_slice str, gpr_slice sep, gpr_slice_buffer *dst); +void gpr_slice_split(gpr_slice str, const char *sep, gpr_slice_buffer *dst); /* A vector of strings... for building up a final string one piece at a time */ typedef struct { diff --git a/test/core/support/string_test.c b/test/core/support/string_test.c index 7a58307d05..9023d0746b 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -223,7 +223,6 @@ static void test_strjoin_sep(void) { static void test_strsplit(void) { gpr_slice_buffer* parts; gpr_slice str; - gpr_slice sep; LOG_TEST_NAME("test_strsplit"); @@ -231,8 +230,7 @@ static void test_strsplit(void) { gpr_slice_buffer_init(parts); str = gpr_slice_from_copied_string("one, two, three, four"); - sep = gpr_slice_from_copied_string(", "); - gpr_slice_split(str, sep, parts); + gpr_slice_split(str, ", ", parts); GPR_ASSERT(4 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "two")); @@ -243,15 +241,15 @@ static void test_strsplit(void) { /* separator not present in string */ str = gpr_slice_from_copied_string("one two three four"); - gpr_slice_split(str, sep, parts); + gpr_slice_split(str, ", ", parts); GPR_ASSERT(1 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "one two three four")); gpr_slice_buffer_reset_and_unref(parts); gpr_slice_unref(str); /* separator at the end */ - str = gpr_slice_from_copied_string("foo, "); - gpr_slice_split(str, sep, parts); + str = gpr_slice_from_copied_string("foo,"); + gpr_slice_split(str, ",", parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "foo")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); @@ -259,8 +257,8 @@ static void test_strsplit(void) { gpr_slice_unref(str); /* separator at the beginning */ - str = gpr_slice_from_copied_string(", foo"); - gpr_slice_split(str, sep, parts); + str = gpr_slice_from_copied_string(",foo"); + gpr_slice_split(str, ",", parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "foo")); @@ -268,8 +266,8 @@ static void test_strsplit(void) { gpr_slice_unref(str); /* standalone separator */ - str = gpr_slice_from_copied_string(", "); - gpr_slice_split(str, sep, parts); + str = gpr_slice_from_copied_string(","); + gpr_slice_split(str, ",", parts); GPR_ASSERT(2 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[1], "")); @@ -278,13 +276,12 @@ static void test_strsplit(void) { /* empty input */ str = gpr_slice_from_copied_string(""); - gpr_slice_split(str, sep, parts); + gpr_slice_split(str, ", ", parts); GPR_ASSERT(1 == parts->count); GPR_ASSERT(0 == gpr_slice_str_cmp(parts->slices[0], "")); gpr_slice_buffer_reset_and_unref(parts); gpr_slice_unref(str); - gpr_slice_unref(sep); gpr_slice_buffer_destroy(parts); gpr_free(parts); } -- cgit v1.2.3