summaryrefslogtreecommitdiff
path: root/src/buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/buffer.c')
-rw-r--r--src/buffer.c144
1 files changed, 136 insertions, 8 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;
+}
+