diff options
author | Nicolas Noble <nicolasnoble@users.noreply.github.com> | 2015-07-19 10:21:33 -0700 |
---|---|---|
committer | Nicolas Noble <nicolasnoble@users.noreply.github.com> | 2015-07-19 10:21:33 -0700 |
commit | d82d0b295b51e1385481be381eef325423441a65 (patch) | |
tree | 7087253a779b19cc09ad73a7977a4e8f69dc15ef | |
parent | 436822131e523891ce0c4dfa3df49652c283ad32 (diff) | |
parent | 6016e260ca5d36408a3ae23db3c44481ee4c8427 (diff) |
Merge pull request #2319 from dgquintas/str_join_with_sep
Added convenience functions gpr_strjoin_sep and grp_strsplit
-rw-r--r-- | src/core/support/string.c | 62 | ||||
-rw-r--r-- | src/core/support/string.h | 11 | ||||
-rw-r--r-- | test/core/support/string_test.c | 116 |
3 files changed, 188 insertions, 1 deletions
diff --git a/src/core/support/string.c b/src/core/support/string.c index 09598da946..9babbd910a 100644 --- a/src/core/support/string.c +++ b/src/core/support/string.c @@ -38,6 +38,7 @@ #include <string.h> #include <grpc/support/alloc.h> +#include <grpc/support/log.h> #include <grpc/support/port_platform.h> #include <grpc/support/useful.h> @@ -174,6 +175,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; @@ -181,10 +188,17 @@ 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]); + if (i != 0) { + memcpy(out + out_length, sep, sep_len); + out_length += sep_len; + } memcpy(out + out_length, strs[i], slen); out_length += slen; } @@ -195,6 +209,52 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { return out; } +/** 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 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 size_t str_len = GPR_SLICE_LENGTH(str) - read_offset; + 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, sep_len) == 0) { + *begin = read_offset; + *end = read_offset + i; + return 1; + } + } + return 0; +} + +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); + + 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)); + } +} + 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 d950d908d6..3ac4abeef8 100644 --- a/src/core/support/string.h +++ b/src/core/support/string.h @@ -37,6 +37,7 @@ #include <stddef.h> #include <grpc/support/port_platform.h> +#include <grpc/support/slice_buffer.h> #include <grpc/support/slice.h> #ifdef __cplusplus @@ -77,6 +78,16 @@ 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); + +/** 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, const char *sep, gpr_slice_buffer *dst); + /* 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 f04e72ac2b..9023d0746b 100644 --- a/test/core/support/string_test.c +++ b/test/core/support/string_test.c @@ -173,6 +173,119 @@ static void test_asprintf(void) { } } +static void test_strjoin(void) { + const char *parts[4] = {"one", "two", "three", "four"}; + size_t joined_len; + char *joined; + + LOG_TEST_NAME("test_strjoin"); + + joined = gpr_strjoin(parts, 4, &joined_len); + GPR_ASSERT(0 == strcmp("onetwothreefour", joined)); + gpr_free(joined); + + joined = gpr_strjoin(parts, 0, &joined_len); + GPR_ASSERT(0 == strcmp("", joined)); + gpr_free(joined); + + joined = gpr_strjoin(parts, 1, &joined_len); + GPR_ASSERT(0 == strcmp("one", joined)); + gpr_free(joined); +} + +static void test_strjoin_sep(void) { + const char *parts[4] = {"one", "two", "three", "four"}; + size_t joined_len; + char *joined; + + LOG_TEST_NAME("test_strjoin_sep"); + + joined = gpr_strjoin_sep(parts, 4, ", ", &joined_len); + 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); +} + +static void test_strsplit(void) { + gpr_slice_buffer* parts; + gpr_slice str; + + LOG_TEST_NAME("test_strsplit"); + + parts = gpr_malloc(sizeof(gpr_slice_buffer)); + gpr_slice_buffer_init(parts); + + str = gpr_slice_from_copied_string("one, two, three, four"); + 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")); + 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_reset_and_unref(parts); + gpr_slice_unref(str); + + /* separator not present in string */ + str = gpr_slice_from_copied_string("one two three four"); + 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, ",", 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_reset_and_unref(parts); + gpr_slice_unref(str); + + /* separator at the beginning */ + 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")); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); + + /* standalone separator */ + 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], "")); + gpr_slice_buffer_reset_and_unref(parts); + gpr_slice_unref(str); + + /* empty input */ + str = gpr_slice_from_copied_string(""); + 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_buffer_destroy(parts); + gpr_free(parts); +} + int main(int argc, char **argv) { grpc_test_init(argc, argv); test_strdup(); @@ -180,5 +293,8 @@ int main(int argc, char **argv) { test_dump_slice(); test_parse_uint32(); test_asprintf(); + test_strjoin(); + test_strjoin_sep(); + test_strsplit(); return 0; } |