Commit f5f04826 by Vicent Martí

Merge pull request #503 from arrbee/git-buf-always-cstr

Make git_buf functions always maintain a valid cstr
parents fc88a8d3 309113c9
...@@ -8,13 +8,29 @@ ...@@ -8,13 +8,29 @@
#include "posix.h" #include "posix.h"
#include <stdarg.h> #include <stdarg.h>
/* Used as default value for git_buf->ptr so that people can always
* assume ptr is non-NULL and zero terminated even for new git_bufs.
*/
char git_buf_initbuf[1];
#define ENSURE_SIZE(b, d) \ #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; return;
void git_buf_init(git_buf *buf, size_t initial_size)
{
buf->asize = 0;
buf->size = 0;
buf->ptr = git_buf_initbuf;
if (initial_size)
git_buf_grow(buf, initial_size);
}
int git_buf_grow(git_buf *buf, size_t target_size) int git_buf_grow(git_buf *buf, size_t target_size)
{ {
char *new_ptr; char *new_ptr;
size_t new_size;
if (buf->asize < 0) if (buf->asize < 0)
return GIT_ENOMEM; return GIT_ENOMEM;
...@@ -22,27 +38,56 @@ int git_buf_grow(git_buf *buf, size_t target_size) ...@@ -22,27 +38,56 @@ int git_buf_grow(git_buf *buf, size_t target_size)
if (target_size <= (size_t)buf->asize) if (target_size <= (size_t)buf->asize)
return GIT_SUCCESS; return GIT_SUCCESS;
if (buf->asize == 0) if (buf->asize == 0) {
buf->asize = target_size; new_size = target_size;
new_ptr = NULL;
} else {
new_size = (size_t)buf->asize;
new_ptr = buf->ptr;
}
/* grow the buffer size by 1.5, until it's big enough /* grow the buffer size by 1.5, until it's big enough
* to fit our target size */ * to fit our target size */
while (buf->asize < (int)target_size) while (new_size < target_size)
buf->asize = (buf->asize << 1) - (buf->asize >> 1); new_size = (new_size << 1) - (new_size >> 1);
/* round allocation up to multiple of 8 */ /* round allocation up to multiple of 8 */
buf->asize = (buf->asize + 7) & ~7; new_size = (new_size + 7) & ~7;
new_ptr = git__realloc(buf->ptr, buf->asize); new_ptr = git__realloc(new_ptr, new_size);
if (!new_ptr) { if (!new_ptr) {
buf->asize = -1; buf->asize = -1;
return GIT_ENOMEM; return GIT_ENOMEM;
} }
buf->asize = new_size;
buf->ptr = new_ptr; buf->ptr = new_ptr;
/* truncate the existing buffer size if necessary */
if (buf->size >= buf->asize)
buf->size = buf->asize - 1;
buf->ptr[buf->size] = '\0';
return GIT_SUCCESS; return GIT_SUCCESS;
} }
void git_buf_free(git_buf *buf)
{
if (!buf) return;
if (buf->ptr != git_buf_initbuf)
git__free(buf->ptr);
git_buf_init(buf, 0);
}
void git_buf_clear(git_buf *buf)
{
buf->size = 0;
if (buf->asize > 0)
buf->ptr[0] = '\0';
}
int git_buf_oom(const git_buf *buf) int git_buf_oom(const git_buf *buf)
{ {
return (buf->asize < 0); return (buf->asize < 0);
...@@ -53,9 +98,10 @@ void git_buf_set(git_buf *buf, const char *data, size_t len) ...@@ -53,9 +98,10 @@ void git_buf_set(git_buf *buf, const char *data, size_t len)
if (len == 0 || data == NULL) { if (len == 0 || data == NULL) {
git_buf_clear(buf); git_buf_clear(buf);
} else { } else {
ENSURE_SIZE(buf, len); ENSURE_SIZE(buf, len + 1);
memmove(buf->ptr, data, len); memmove(buf->ptr, data, len);
buf->size = len; buf->size = len;
buf->ptr[buf->size] = '\0';
} }
} }
...@@ -66,15 +112,17 @@ void git_buf_sets(git_buf *buf, const char *string) ...@@ -66,15 +112,17 @@ void git_buf_sets(git_buf *buf, const char *string)
void git_buf_putc(git_buf *buf, char c) void git_buf_putc(git_buf *buf, char c)
{ {
ENSURE_SIZE(buf, buf->size + 1); ENSURE_SIZE(buf, buf->size + 2);
buf->ptr[buf->size++] = c; buf->ptr[buf->size++] = c;
buf->ptr[buf->size] = '\0';
} }
void git_buf_put(git_buf *buf, const char *data, size_t len) void git_buf_put(git_buf *buf, const char *data, size_t len)
{ {
ENSURE_SIZE(buf, buf->size + len); ENSURE_SIZE(buf, buf->size + len + 1);
memmove(buf->ptr + buf->size, data, len); memmove(buf->ptr + buf->size, data, len);
buf->size += len; buf->size += len;
buf->ptr[buf->size] = '\0';
} }
void git_buf_puts(git_buf *buf, const char *string) void git_buf_puts(git_buf *buf, const char *string)
...@@ -111,27 +159,25 @@ void git_buf_printf(git_buf *buf, const char *format, ...) ...@@ -111,27 +159,25 @@ void git_buf_printf(git_buf *buf, const char *format, ...)
const char *git_buf_cstr(git_buf *buf) const char *git_buf_cstr(git_buf *buf)
{ {
if (buf->size + 1 > buf->asize &&
git_buf_grow(buf, buf->size + 1) < GIT_SUCCESS)
return NULL;
buf->ptr[buf->size] = '\0';
return buf->ptr; return buf->ptr;
} }
void git_buf_free(git_buf *buf) void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf)
{ {
if (!buf) return; size_t copylen;
git__free(buf->ptr); assert(data && datasize);
buf->ptr = NULL;
buf->asize = 0;
buf->size = 0;
}
void git_buf_clear(git_buf *buf) data[0] = '\0';
{
buf->size = 0; if (buf->size == 0 || buf->asize <= 0)
return;
copylen = buf->size;
if (copylen > datasize - 1)
copylen = datasize - 1;
memmove(data, buf->ptr, copylen);
data[copylen] = '\0';
} }
void git_buf_consume(git_buf *buf, const char *end) void git_buf_consume(git_buf *buf, const char *end)
...@@ -140,6 +186,7 @@ void git_buf_consume(git_buf *buf, const char *end) ...@@ -140,6 +186,7 @@ void git_buf_consume(git_buf *buf, const char *end)
size_t consumed = end - buf->ptr; size_t consumed = end - buf->ptr;
memmove(buf->ptr, end, buf->size - consumed); memmove(buf->ptr, end, buf->size - consumed);
buf->size -= consumed; buf->size -= consumed;
buf->ptr[buf->size] = '\0';
} }
} }
...@@ -152,21 +199,12 @@ void git_buf_swap(git_buf *buf_a, git_buf *buf_b) ...@@ -152,21 +199,12 @@ void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
char *git_buf_take_cstr(git_buf *buf) char *git_buf_take_cstr(git_buf *buf)
{ {
char *data = NULL; char *data = buf->ptr;
if (buf->ptr == NULL) if (buf->asize <= 0)
return NULL; return NULL;
if (buf->size + 1 > buf->asize && git_buf_init(buf, 0);
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; return data;
} }
...@@ -199,7 +237,7 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) ...@@ -199,7 +237,7 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
} }
va_end(ap); va_end(ap);
ENSURE_SIZE(buf, buf->size + total_size); ENSURE_SIZE(buf, buf->size + total_size + 1);
out = buf->ptr + buf->size; out = buf->ptr + buf->size;
...@@ -235,6 +273,7 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...) ...@@ -235,6 +273,7 @@ void git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
/* set size based on num characters actually written */ /* set size based on num characters actually written */
buf->size = out - buf->ptr; buf->size = out - buf->ptr;
buf->ptr[buf->size] = '\0';
} }
void git_buf_join( void git_buf_join(
...@@ -277,7 +316,7 @@ void git_buf_join( ...@@ -277,7 +316,7 @@ void git_buf_join(
if (!add_size) return; if (!add_size) return;
ENSURE_SIZE(buf, buf->size + add_size); ENSURE_SIZE(buf, buf->size + add_size + 1);
/* concatenate strings */ /* concatenate strings */
ptr = buf->ptr + buf->size; ptr = buf->ptr + buf->size;
...@@ -296,4 +335,5 @@ void git_buf_join( ...@@ -296,4 +335,5 @@ void git_buf_join(
/* set size based on num characters actually written */ /* set size based on num characters actually written */
buf->size = ptr - buf->ptr; buf->size = ptr - buf->ptr;
buf->ptr[buf->size] = '\0';
} }
...@@ -14,8 +14,11 @@ typedef struct { ...@@ -14,8 +14,11 @@ typedef struct {
ssize_t asize, size; ssize_t asize, size;
} git_buf; } git_buf;
#define GIT_BUF_INIT {NULL, 0, 0} extern char git_buf_initbuf[];
#define GIT_BUF_INIT { git_buf_initbuf, 0, 0 }
void git_buf_init(git_buf *buf, size_t initial_size);
int git_buf_grow(git_buf *buf, size_t target_size); int git_buf_grow(git_buf *buf, size_t target_size);
void git_buf_free(git_buf *buf); void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b); void git_buf_swap(git_buf *buf_a, git_buf *buf_b);
...@@ -42,6 +45,7 @@ void git_buf_join(git_buf *buf, char separator, const char *str_a, const char *s ...@@ -42,6 +45,7 @@ void git_buf_join(git_buf *buf, char separator, const char *str_a, const char *s
const char *git_buf_cstr(git_buf *buf); const char *git_buf_cstr(git_buf *buf);
char *git_buf_take_cstr(git_buf *buf); char *git_buf_take_cstr(git_buf *buf);
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf);
#define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1) #define git_buf_PUTS(buf, str) git_buf_put(buf, str, sizeof(str) - 1)
......
...@@ -52,19 +52,19 @@ void test_core_buffer__2(void) ...@@ -52,19 +52,19 @@ void test_core_buffer__2(void)
{ {
git_buf buf = GIT_BUF_INIT; git_buf buf = GIT_BUF_INIT;
int i; int i;
char data[100];
cl_assert(buf.size == 0); cl_assert(buf.size == 0);
/* this must be safe to do */ /* this must be safe to do */
git_buf_free(&buf); git_buf_free(&buf);
cl_assert(buf.size == 0); cl_assert(buf.size == 0);
cl_assert(buf.asize == 0); cl_assert(buf.asize == 0);
/* empty buffer should be empty string */ /* empty buffer should be empty string */
cl_assert_strequal("", git_buf_cstr(&buf)); cl_assert_strequal("", git_buf_cstr(&buf));
cl_assert(buf.size == 0); cl_assert(buf.size == 0);
cl_assert(buf.asize > 0); /* cl_assert(buf.asize == 0); -- should not assume what git_buf does */
/* free should set us back to the beginning */ /* free should set us back to the beginning */
git_buf_free(&buf); git_buf_free(&buf);
...@@ -130,6 +130,27 @@ void test_core_buffer__2(void) ...@@ -130,6 +130,27 @@ void test_core_buffer__2(void)
cl_assert_strequal("", git_buf_cstr(&buf)); cl_assert_strequal("", git_buf_cstr(&buf));
git_buf_free(&buf); git_buf_free(&buf);
/* test extracting data into buffer */
git_buf_puts(&buf, REP4("0123456789"));
cl_assert(git_buf_oom(&buf) == 0);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_copy_cstr(data, 11, &buf);
cl_assert_strequal(data, "0123456789");
git_buf_copy_cstr(data, 3, &buf);
cl_assert_strequal(data, "01");
git_buf_copy_cstr(data, 1, &buf);
cl_assert_strequal(data, "");
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, REP4("0123456789"));
git_buf_free(&buf);
git_buf_copy_cstr(data, 100, &buf);
cl_assert_strequal(data, "");
} }
/* let's do some tests with larger buffers to push our limits */ /* let's do some tests with larger buffers to push our limits */
...@@ -203,9 +224,6 @@ check_buf_append( ...@@ -203,9 +224,6 @@ check_buf_append(
cl_assert(git_buf_oom(&tgt) == 0); cl_assert(git_buf_oom(&tgt) == 0);
git_buf_puts(&tgt, data_b); git_buf_puts(&tgt, data_b);
cl_assert(git_buf_oom(&tgt) == 0); 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_strequal(expected_data, git_buf_cstr(&tgt));
cl_assert(tgt.size == expected_size); cl_assert(tgt.size == expected_size);
if (expected_asize > 0) if (expected_asize > 0)
...@@ -277,15 +295,15 @@ void test_core_buffer__5(void) ...@@ -277,15 +295,15 @@ void test_core_buffer__5(void)
*/ */
check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16); check_buf_append("abcdefgh", "/", "abcdefgh/", 9, 16);
check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 24); check_buf_append("abcdefgh", "ijklmno", "abcdefghijklmno", 15, 16);
check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24); check_buf_append("abcdefgh", "ijklmnop", "abcdefghijklmnop", 16, 24);
check_buf_append("0123456789", "0123456789", check_buf_append("0123456789", "0123456789",
"01234567890123456789", 20, 24); "01234567890123456789", 20, 24);
check_buf_append(REP16("x"), REP16("o"), check_buf_append(REP16("x"), REP16("o"),
REP16("x") REP16("o"), 32, 40); REP16("x") REP16("o"), 32, 40);
check_buf_append(test_4096, "", test_4096, 4096, 6144); check_buf_append(test_4096, "", test_4096, 4096, 4104);
check_buf_append(test_4096, test_4096, test_8192, 8192, 9216); check_buf_append(test_4096, test_4096, test_8192, 8192, 9240);
/* check sequences of appends */ /* check sequences of appends */
check_buf_append_abc("a", "b", "c", check_buf_append_abc("a", "b", "c",
...@@ -335,13 +353,13 @@ void test_core_buffer__7(void) ...@@ -335,13 +353,13 @@ void test_core_buffer__7(void)
b = git_buf_take_cstr(&a); b = git_buf_take_cstr(&a);
cl_assert_strequal("foo", b); cl_assert_strequal("foo", b);
cl_assert_strequal(NULL, a.ptr); cl_assert_strequal("", a.ptr);
git__free(b); git__free(b);
b = git_buf_take_cstr(&a); b = git_buf_take_cstr(&a);
cl_assert_strequal(NULL, b); cl_assert_strequal(NULL, b);
cl_assert_strequal(NULL, a.ptr); cl_assert_strequal("", a.ptr);
git_buf_free(&a); git_buf_free(&a);
} }
......
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