Skip to content
Snippets Groups Projects
Commit d82d0b29 authored by Nicolas Noble's avatar Nicolas Noble
Browse files

Merge pull request #2319 from dgquintas/str_join_with_sep

Added convenience functions gpr_strjoin_sep and grp_strsplit
parents 43682213 6016e260
No related branches found
No related tags found
No related merge requests found
...@@ -38,6 +38,7 @@ ...@@ -38,6 +38,7 @@
#include <string.h> #include <string.h>
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h>
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
...@@ -174,6 +175,12 @@ int gpr_ltoa(long value, char *string) { ...@@ -174,6 +175,12 @@ int gpr_ltoa(long value, char *string) {
} }
char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { 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 out_length = 0;
size_t i; size_t i;
char *out; char *out;
...@@ -181,10 +188,17 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { ...@@ -181,10 +188,17 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) {
out_length += strlen(strs[i]); out_length += strlen(strs[i]);
} }
out_length += 1; /* null terminator */ out_length += 1; /* null terminator */
if (nstrs > 0) {
out_length += sep_len * (nstrs - 1); /* separators */
}
out = gpr_malloc(out_length); out = gpr_malloc(out_length);
out_length = 0; out_length = 0;
for (i = 0; i < nstrs; i++) { 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); memcpy(out + out_length, strs[i], slen);
out_length += slen; out_length += slen;
} }
...@@ -195,6 +209,52 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) { ...@@ -195,6 +209,52 @@ char *gpr_strjoin(const char **strs, size_t nstrs, size_t *final_length) {
return out; 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) { void gpr_strvec_init(gpr_strvec *sv) {
memset(sv, 0, sizeof(*sv)); memset(sv, 0, sizeof(*sv));
} }
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#include <stddef.h> #include <stddef.h>
#include <grpc/support/port_platform.h> #include <grpc/support/port_platform.h>
#include <grpc/support/slice_buffer.h>
#include <grpc/support/slice.h> #include <grpc/support/slice.h>
#ifdef __cplusplus #ifdef __cplusplus
...@@ -77,6 +78,16 @@ void gpr_reverse_bytes(char *str, int len); ...@@ -77,6 +78,16 @@ void gpr_reverse_bytes(char *str, int len);
if it is non-null. */ if it is non-null. */
char *gpr_strjoin(const char **strs, size_t nstrs, size_t *total_length); 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 */ /* A vector of strings... for building up a final string one piece at a time */
typedef struct { typedef struct {
char **strs; char **strs;
......
...@@ -173,6 +173,119 @@ static void test_asprintf(void) { ...@@ -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) { int main(int argc, char **argv) {
grpc_test_init(argc, argv); grpc_test_init(argc, argv);
test_strdup(); test_strdup();
...@@ -180,5 +293,8 @@ int main(int argc, char **argv) { ...@@ -180,5 +293,8 @@ int main(int argc, char **argv) {
test_dump_slice(); test_dump_slice();
test_parse_uint32(); test_parse_uint32();
test_asprintf(); test_asprintf();
test_strjoin();
test_strjoin_sep();
test_strsplit();
return 0; return 0;
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment