Commit 798dd36c by Vicent Martí

Merge pull request #499 from arrbee/extend-git-buf

Extend git_buf with new utility functions and unit tests.
parents 89886d0b 679b69c4
......@@ -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,12 +73,13 @@ 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)
{
assert(string);
git_buf_put(buf, string, strlen(string));
}
......@@ -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,12 @@ const char *git_buf_cstr(git_buf *buf)
void git_buf_free(git_buf *buf)
{
if (!buf) return;
git__free(buf->ptr);
buf->ptr = NULL;
buf->asize = 0;
buf->size = 0;
}
void git_buf_clear(git_buf *buf)
......@@ -107,7 +136,164 @@ 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_n(git_buf *buf, char separator, int nbuf, ...)
{
/* Make two passes to avoid multiple reallocation */
va_list ap;
int i;
size_t 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;
size_t 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;
size_t 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;
}
void git_buf_join(
git_buf *buf,
char separator,
const char *str_a,
const char *str_b)
{
size_t add_size = 0;
size_t sep_a = 0;
size_t strlen_a = 0;
size_t sep_b = 0;
size_t strlen_b = 0;
char *ptr;
/* calculate string lengths and need for added separators */
if (str_a) {
while (*str_a == separator) { sep_a = 1; str_a++; }
strlen_a = strlen(str_a);
}
if (str_b) {
while (*str_b == separator) { sep_b = 1; str_b++; }
strlen_b = strlen(str_b);
}
if (buf->size > 0) {
if (buf->ptr[buf->size - 1] == separator) {
sep_a = 0;
if (!strlen_a) sep_b = 0;
} else if (!strlen_a)
sep_b = (str_b != NULL);
}
if (strlen_a > 0) {
if (str_a[strlen_a - 1] == separator)
sep_b = 0;
else if (str_b)
sep_b = 1;
}
add_size = sep_a + strlen_a + sep_b + strlen_b;
if (!add_size) return;
ENSURE_SIZE(buf, buf->size + add_size);
/* concatenate strings */
ptr = buf->ptr + buf->size;
if (sep_a)
*ptr++ = separator;
if (strlen_a) {
memmove(ptr, str_a, strlen_a);
ptr += strlen_a;
}
if (sep_b)
*ptr++ = separator;
if (strlen_b) {
memmove(ptr, str_b, strlen_b);
ptr += strlen_b;
}
/* set size based on num characters actually written */
buf->size = ptr - buf->ptr;
}
......@@ -17,15 +17,31 @@ 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_n(git_buf *buf, char separator, int nbuf, ...);
void git_buf_join(git_buf *buf, char separator, const char *str_a, const char *str_b);
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)
......
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment