Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/libgit2.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/buffer.c144
-rw-r--r--src/buffer.h19
-rw-r--r--tests-clay/clay_libgit2.h16
-rw-r--r--tests-clay/core/buffer.c435
4 files changed, 604 insertions, 10 deletions
diff --git a/src/buffer.c b/src/buffer.c
index 1fb848e46..3fd04211c 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -9,7 +9,7 @@
#include <stdarg.h>
#define ENSURE_SIZE(b, d) \
- if ((ssize_t)(d) >= buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
+ if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
return;
int git_buf_grow(git_buf *buf, size_t target_size)
@@ -19,6 +19,9 @@ int git_buf_grow(git_buf *buf, size_t target_size)
if (buf->asize < 0)
return GIT_ENOMEM;
+ if (target_size <= (size_t)buf->asize)
+ return GIT_SUCCESS;
+
if (buf->asize == 0)
buf->asize = target_size;
@@ -27,6 +30,9 @@ int git_buf_grow(git_buf *buf, size_t target_size)
while (buf->asize < (int)target_size)
buf->asize = (buf->asize << 1) - (buf->asize >> 1);
+ /* round allocation up to multiple of 8 */
+ buf->asize = (buf->asize + 7) & ~7;
+
new_ptr = git__realloc(buf->ptr, buf->asize);
if (!new_ptr) {
buf->asize = -1;
@@ -42,6 +48,22 @@ int git_buf_oom(const git_buf *buf)
return (buf->asize < 0);
}
+void git_buf_set(git_buf *buf, const char *data, size_t len)
+{
+ if (len == 0 || data == NULL) {
+ git_buf_clear(buf);
+ } else {
+ ENSURE_SIZE(buf, len);
+ memmove(buf->ptr, data, len);
+ buf->size = len;
+ }
+}
+
+void git_buf_sets(git_buf *buf, const char *string)
+{
+ git_buf_set(buf, string, string ? strlen(string) : 0);
+}
+
void git_buf_putc(git_buf *buf, char c)
{
ENSURE_SIZE(buf, buf->size + 1);
@@ -51,13 +73,14 @@ void git_buf_putc(git_buf *buf, char c)
void git_buf_put(git_buf *buf, const char *data, size_t len)
{
ENSURE_SIZE(buf, buf->size + len);
- memcpy(buf->ptr + buf->size, data, len);
+ memmove(buf->ptr + buf->size, data, len);
buf->size += len;
}
void git_buf_puts(git_buf *buf, const char *string)
{
- git_buf_put(buf, string, strlen(string));
+ if (string != NULL)
+ git_buf_put(buf, string, strlen(string));
}
void git_buf_printf(git_buf *buf, const char *format, ...)
@@ -88,7 +111,8 @@ void git_buf_printf(git_buf *buf, const char *format, ...)
const char *git_buf_cstr(git_buf *buf)
{
- if (buf->size + 1 >= buf->asize && git_buf_grow(buf, buf->size + 1) < GIT_SUCCESS)
+ if (buf->size + 1 > buf->asize &&
+ git_buf_grow(buf, buf->size + 1) < GIT_SUCCESS)
return NULL;
buf->ptr[buf->size] = '\0';
@@ -97,7 +121,14 @@ const char *git_buf_cstr(git_buf *buf)
void git_buf_free(git_buf *buf)
{
- git__free(buf->ptr);
+ if (buf) {
+ if (buf->ptr) {
+ git__free(buf->ptr);
+ buf->ptr = NULL;
+ }
+ buf->asize = 0;
+ buf->size = 0;
+ }
}
void git_buf_clear(git_buf *buf)
@@ -107,7 +138,104 @@ void git_buf_clear(git_buf *buf)
void git_buf_consume(git_buf *buf, const char *end)
{
- size_t consumed = end - buf->ptr;
- memmove(buf->ptr, end, buf->size - consumed);
- buf->size -= consumed;
+ if (end > buf->ptr && end <= buf->ptr + buf->size) {
+ size_t consumed = end - buf->ptr;
+ memmove(buf->ptr, end, buf->size - consumed);
+ buf->size -= consumed;
+ }
+}
+
+void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
+{
+ git_buf t = *buf_a;
+ *buf_a = *buf_b;
+ *buf_b = t;
}
+
+char *git_buf_take_cstr(git_buf *buf)
+{
+ char *data = NULL;
+
+ if (buf->ptr == NULL)
+ return NULL;
+
+ if (buf->size + 1 > buf->asize &&
+ git_buf_grow(buf, buf->size + 1) < GIT_SUCCESS)
+ return NULL;
+
+ data = buf->ptr;
+ data[buf->size] = '\0';
+
+ buf->ptr = NULL;
+ buf->asize = 0;
+ buf->size = 0;
+
+ return data;
+}
+
+void git_buf_join(git_buf *buf, char separator, int nbuf, ...)
+{
+ /* Make two passes to avoid multiple reallocation */
+
+ va_list ap;
+ int i;
+ int total_size = 0;
+ char *out;
+
+ if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
+ ++total_size; /* space for initial separator */
+
+ va_start(ap, nbuf);
+ for (i = 0; i < nbuf; ++i) {
+ const char* segment;
+ int segment_len;
+
+ segment = va_arg(ap, const char *);
+ if (!segment)
+ continue;
+
+ segment_len = strlen(segment);
+ total_size += segment_len;
+ if (segment_len == 0 || segment[segment_len - 1] != separator)
+ ++total_size; /* space for separator */
+ }
+ va_end(ap);
+
+ ENSURE_SIZE(buf, buf->size + total_size);
+
+ out = buf->ptr + buf->size;
+
+ /* append separator to existing buf if needed */
+ if (buf->size > 0 && out[-1] != separator)
+ *out++ = separator;
+
+ va_start(ap, nbuf);
+ for (i = 0; i < nbuf; ++i) {
+ const char* segment;
+ int segment_len;
+
+ segment = va_arg(ap, const char *);
+ if (!segment)
+ continue;
+
+ /* skip leading separators */
+ if (out > buf->ptr && out[-1] == separator)
+ while (*segment == separator) segment++;
+
+ /* copy over next buffer */
+ segment_len = strlen(segment);
+ if (segment_len > 0) {
+ memmove(out, segment, segment_len);
+ out += segment_len;
+ }
+
+ /* append trailing separator (except for last item) */
+ if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
+ *out++ = separator;
+ }
+ va_end(ap);
+
+ /* set size based on num characters actually written */
+ buf->size = out - buf->ptr;
+}
+
diff --git a/src/buffer.h b/src/buffer.h
index ad3b8930f..baa8f4f4d 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -17,15 +17,30 @@ typedef struct {
#define GIT_BUF_INIT {NULL, 0, 0}
int git_buf_grow(git_buf *buf, size_t target_size);
+void git_buf_free(git_buf *buf);
+void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
+
+/**
+ * Any function that writes to a git_buf can fail due to memory allocation
+ * issues. If one fails, the git_buf will be marked with an OOM error and
+ * further calls to modify the buffer will fail. You just check
+ * git_buf_oom() at the end of your sequence and it will be true if you ran
+ * out of memory at any point with that buffer.
+ */
int git_buf_oom(const git_buf *buf);
+
+void git_buf_set(git_buf *buf, const char *data, size_t len);
+void git_buf_sets(git_buf *buf, const char *string);
void git_buf_putc(git_buf *buf, char c);
void git_buf_put(git_buf *buf, const char *data, size_t len);
void git_buf_puts(git_buf *buf, const char *string);
void git_buf_printf(git_buf *buf, const char *format, ...) GIT_FORMAT_PRINTF(2, 3);
-const char *git_buf_cstr(git_buf *buf);
-void git_buf_free(git_buf *buf);
void git_buf_clear(git_buf *buf);
void git_buf_consume(git_buf *buf, const char *end);
+void git_buf_join(git_buf *buf, char separator, int nbuf, ...);
+
+const char *git_buf_cstr(git_buf *buf);
+char *git_buf_take_cstr(git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
diff --git a/tests-clay/clay_libgit2.h b/tests-clay/clay_libgit2.h
index 8784b7e61..2eedc61d2 100644
--- a/tests-clay/clay_libgit2.h
+++ b/tests-clay/clay_libgit2.h
@@ -25,4 +25,20 @@
*/
#define cl_git_fail(expr) cl_must_fail(expr)
+/**
+ * Wrapper for string comparison that knows about nulls.
+ */
+#define cl_assert_strequal(a,b) \
+ cl_assert_strequal_internal(a,b,__FILE__,__LINE__)
+
+GIT_INLINE(void) cl_assert_strequal_internal(const char *a, const char *b, const char *file, int line)
+{
+ int match = (a == NULL || b == NULL) ? (a == b) : (strcmp(a, b) == 0);
+ if (!match) {
+ char buf[4096];
+ snprintf(buf, 4096, "'%s' != '%s'", a, b);
+ clay__assert(0, file, line, buf, "Strings do not match", 1);
+ }
+}
+
#endif
diff --git a/tests-clay/core/buffer.c b/tests-clay/core/buffer.c
new file mode 100644
index 000000000..411c5e84c
--- /dev/null
+++ b/tests-clay/core/buffer.c
@@ -0,0 +1,435 @@
+#include "clay_libgit2.h"
+#include "buffer.h"
+
+#define TESTSTR "Have you seen that? Have you seeeen that??"
+const char *test_string = TESTSTR;
+const char *test_string_x2 = TESTSTR TESTSTR;
+
+#define REP4(STR) STR STR STR STR
+#define REP16(STR) REP4(REP4(STR))
+#define REP1024(STR) REP16(REP16(REP4(STR)))
+#define TESTSTR_4096 REP1024("1234")
+#define TESTSTR_8192 REP1024("12341234")
+const char *test_4096 = TESTSTR_4096;
+const char *test_8192 = TESTSTR_8192;
+
+/* test basic data concatenation */
+void test_core_buffer__0(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ cl_assert(buf.size == 0);
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_string, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_string_x2, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* test git_buf_printf */
+void test_core_buffer__1(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_printf(&buf, "%s %s %d ", "shoop", "da", 23);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("shoop da 23 ", git_buf_cstr(&buf));
+
+ git_buf_printf(&buf, "%s %d", "woop", 42);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("shoop da 23 woop 42", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more thorough test of concatenation options */
+void test_core_buffer__2(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+
+ cl_assert(buf.size == 0);
+
+ /* this must be safe to do */
+ git_buf_free(&buf);
+
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* empty buffer should be empty string */
+ cl_assert_strequal("", git_buf_cstr(&buf));
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize > 0);
+
+ /* free should set us back to the beginning */
+ git_buf_free(&buf);
+ cl_assert(buf.size == 0);
+ cl_assert(buf.asize == 0);
+
+ /* add letter */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("+", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("++", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_putc(&buf, '+');
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_strequal("++++++++++++++++++", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* add data */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("xo", git_buf_cstr(&buf));
+
+ /* add letter again */
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal("xoxo", git_buf_cstr(&buf));
+
+ /* let's try that a few times */
+ for (i = 0; i < 16; ++i) {
+ git_buf_put(&buf, "xo", 2);
+ cl_assert(git_buf_oom(&buf) == 0);
+ }
+ cl_assert_strequal("xoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxoxo",
+ git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+
+ /* set to string */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_string, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_string_x2, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_sets(&buf, test_string);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_string, git_buf_cstr(&buf));
+
+ /* test clear */
+ git_buf_clear(&buf);
+ cl_assert_strequal("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* let's do some tests with larger buffers to push our limits */
+void test_core_buffer__3(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ /* set to string */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_4096, git_buf_cstr(&buf));
+
+ /* append string */
+ git_buf_puts(&buf, test_4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_8192, git_buf_cstr(&buf));
+
+ /* set to string again (should overwrite - not append) */
+ git_buf_set(&buf, test_4096, 4096);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(test_4096, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* let's try some producer/consumer tests */
+void test_core_buffer__4(void)
+{
+ git_buf buf = GIT_BUF_INIT;
+ int i;
+
+ for (i = 0; i < 10; ++i) {
+ git_buf_puts(&buf, "1234"); /* add 4 */
+ cl_assert(git_buf_oom(&buf) == 0);
+ git_buf_consume(&buf, buf.ptr + 2); /* eat the first two */
+ cl_assert(strlen(git_buf_cstr(&buf)) == (size_t)((i + 1) * 2));
+ }
+ /* we have appended 1234 10x and removed the first 20 letters */
+ cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, NULL);
+ cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, "invalid pointer");
+ cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr);
+ cl_assert_strequal("12341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + 1);
+ cl_assert_strequal("2341234123412341234", git_buf_cstr(&buf));
+
+ git_buf_consume(&buf, buf.ptr + buf.size);
+ cl_assert_strequal("", git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+
+static void
+check_buf_append(
+ const char* data_a,
+ const char* data_b,
+ const char* expected_data,
+ ssize_t expected_size,
+ ssize_t expected_asize)
+{
+ git_buf tgt = GIT_BUF_INIT;
+
+ git_buf_sets(&tgt, data_a);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ git_buf_puts(&tgt, data_b);
+ cl_assert(git_buf_oom(&tgt) == 0);
+ if (expected_data == NULL)
+ cl_assert(tgt.ptr == NULL);
+ else
+ cl_assert_strequal(expected_data, git_buf_cstr(&tgt));
+ cl_assert(tgt.size == expected_size);
+ if (expected_asize > 0)
+ cl_assert(tgt.asize == expected_asize);
+
+ git_buf_free(&tgt);
+}
+
+static void
+check_buf_append_abc(
+ const char* buf_a,
+ const char* buf_b,
+ const char* buf_c,
+ const char* expected_ab,
+ const char* expected_abc,
+ const char* expected_abca,
+ const char* expected_abcab,
+ const char* expected_abcabc)
+{
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(buf_a, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected_ab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected_abc, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_a);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected_abca, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected_abcab, git_buf_cstr(&buf));
+
+ git_buf_puts(&buf, buf_c);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected_abcabc, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+/* more variations on append tests */
+void test_core_buffer__5(void)
+{
+ check_buf_append(NULL, NULL, NULL, 0, 0);
+ check_buf_append(NULL, "", "", 0, 8);
+ check_buf_append("", NULL, "", 0, 8);
+ check_buf_append("", "", "", 0, 8);
+ check_buf_append("a", NULL, "a", 1, 8);
+ check_buf_append(NULL, "a", "a", 1, 8);
+ check_buf_append("", "a", "a", 1, 8);
+ check_buf_append("a", "", "a", 1, 8);
+ check_buf_append("a", "b", "ab", 2, 8);
+ check_buf_append("", "abcdefgh", "abcdefgh", 8, 16);
+ check_buf_append("abcdefgh", "", "abcdefgh", 8, 16);
+
+ /* buffer with starting asize will grow to:
+ * 1 -> 2, 2 -> 3, 3 -> 5, 4 -> 6, 5 -> 8, 6 -> 9,
+ * 7 -> 11, 8 -> 12, 9 -> 14, 10 -> 15, 11 -> 17, 12 -> 18,
+ * 13 -> 20, 14 -> 21, 15 -> 23, 16 -> 24, 17 -> 26, 18 -> 27,
+ * 19 -> 29, 20 -> 30, 21 -> 32, 22 -> 33, 23 -> 35, 24 -> 36,
+ * ...
+ * follow sequence until value > target size,
+ * then round up to nearest multiple of 8.
+ */
+
+ check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
+ check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 24);
+ check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
+ check_buf_append("0123456789", "0123456789",
+ "01234567890123456789", 20, 24);
+ check_buf_append(REP16("x"), REP16("o"),
+ REP16("x") REP16("o"), 32, 40);
+
+ check_buf_append(test_4096, NULL, test_4096, 4096, 6144);
+ check_buf_append(test_4096, "", test_4096, 4096, 6144);
+ check_buf_append(test_4096, test_4096, test_8192, 8192, 9216);
+
+ /* check sequences of appends */
+ check_buf_append_abc("a", "b", "c",
+ "ab", "abc", "abca", "abcab", "abcabc");
+ check_buf_append_abc("a1", "b2", "c3",
+ "a1b2", "a1b2c3", "a1b2c3a1",
+ "a1b2c3a1b2", "a1b2c3a1b2c3");
+ check_buf_append_abc("a1/", "b2/", "c3/",
+ "a1/b2/", "a1/b2/c3/", "a1/b2/c3/a1/",
+ "a1/b2/c3/a1/b2/", "a1/b2/c3/a1/b2/c3/");
+}
+
+/* test swap */
+void test_core_buffer__6(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ git_buf b = GIT_BUF_INIT;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ git_buf_sets(&b, "bar");
+ cl_assert(git_buf_oom(&b) == 0);
+
+ cl_assert_strequal("foo", git_buf_cstr(&a));
+ cl_assert_strequal("bar", git_buf_cstr(&b));
+
+ git_buf_swap(&a, &b);
+
+ cl_assert_strequal("bar", git_buf_cstr(&a));
+ cl_assert_strequal("foo", git_buf_cstr(&b));
+
+ git_buf_free(&a);
+ git_buf_free(&b);
+}
+
+
+/* test take cstr data */
+void test_core_buffer__7(void)
+{
+ git_buf a = GIT_BUF_INIT;
+ char *b = NULL;
+
+ git_buf_sets(&a, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_strequal("foo", git_buf_cstr(&a));
+
+ b = git_buf_take_cstr(&a);
+
+ cl_assert_strequal("foo", b);
+ cl_assert_strequal(NULL, a.ptr);
+ git__free(b);
+
+ b = git_buf_take_cstr(&a);
+
+ cl_assert_strequal(NULL, b);
+ cl_assert_strequal(NULL, a.ptr);
+
+ git_buf_free(&a);
+}
+
+
+static void
+check_joinbuf(
+ const char *a,
+ const char *b,
+ const char *expected)
+{
+ char sep = '/';
+ git_buf buf = GIT_BUF_INIT;
+
+ git_buf_sets(&buf, a);
+ cl_assert(git_buf_oom(&buf) == 0);
+
+ git_buf_join(&buf, sep, 1, b);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected, git_buf_cstr(&buf));
+
+ git_buf_free(&buf);
+}
+
+static void
+check_joinbuf_n(
+ const char *a,
+ const char *b,
+ const char *c,
+ const char *d,
+ const char *expected)
+{
+ char sep = ';';
+ git_buf buf = GIT_BUF_INIT;
+ git_buf_join(&buf, sep, 4, a, b, c, d);
+ cl_assert(git_buf_oom(&buf) == 0);
+ cl_assert_strequal(expected, git_buf_cstr(&buf));
+ git_buf_free(&buf);
+}
+
+/* test join */
+void test_core_buffer__8(void)
+{
+ git_buf a = GIT_BUF_INIT;
+
+ git_buf_join(&a, '/', 1, "foo");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_strequal("foo", git_buf_cstr(&a));
+
+ git_buf_join(&a, '/', 1, "bar");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_strequal("foo/bar", git_buf_cstr(&a));
+
+ git_buf_join(&a, '/', 1, "baz");
+ cl_assert(git_buf_oom(&a) == 0);
+ cl_assert_strequal("foo/bar/baz", git_buf_cstr(&a));
+
+ git_buf_free(&a);
+
+ check_joinbuf("", "", "");
+ check_joinbuf("", "a", "a");
+ check_joinbuf("", "/a", "/a");
+ check_joinbuf("a", "", "a/");
+ check_joinbuf("a", "/", "a/");
+ check_joinbuf("a", "b", "a/b");
+ check_joinbuf("/", "a", "/a");
+ check_joinbuf("/", "", "/");
+ check_joinbuf("/a", "/b", "/a/b");
+ check_joinbuf("/a", "/b/", "/a/b/");
+ check_joinbuf("/a/", "b/", "/a/b/");
+ check_joinbuf("/a/", "/b/", "/a/b/");
+ check_joinbuf("/abcd", "/defg", "/abcd/defg");
+ check_joinbuf("/abcd", "/defg/", "/abcd/defg/");
+ check_joinbuf("/abcd/", "defg/", "/abcd/defg/");
+ check_joinbuf("/abcd/", "/defg/", "/abcd/defg/");
+
+ check_joinbuf_n("", "", "", "", "");
+ check_joinbuf_n("", "a", "", "", "a;");
+ check_joinbuf_n("a", "", "", "", "a;");
+ check_joinbuf_n("", "", "", "a", "a");
+ check_joinbuf_n("a", "b", "", ";c;d;", "a;b;c;d;");
+ check_joinbuf_n("a", "b", "", ";c;d", "a;b;c;d");
+ check_joinbuf_n("abcd", "efgh", "ijkl", "mnop", "abcd;efgh;ijkl;mnop");
+ check_joinbuf_n("abcd;", "efgh;", "ijkl;", "mnop;", "abcd;efgh;ijkl;mnop;");
+ check_joinbuf_n(";abcd;", ";efgh;", ";ijkl;", ";mnop;", ";abcd;efgh;ijkl;mnop;");
+}
+