diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 144 | ||||
-rw-r--r-- | src/buffer.h | 19 |
2 files changed, 153 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) |