aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2015-07-19 10:21:33 -0700
committerGravatar Nicolas Noble <nicolasnoble@users.noreply.github.com>2015-07-19 10:21:33 -0700
commitd82d0b295b51e1385481be381eef325423441a65 (patch)
tree7087253a779b19cc09ad73a7977a4e8f69dc15ef
parent436822131e523891ce0c4dfa3df49652c283ad32 (diff)
parent6016e260ca5d36408a3ae23db3c44481ee4c8427 (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.c62
-rw-r--r--src/core/support/string.h11
-rw-r--r--test/core/support/string_test.c116
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;
}