Make initial value of git_buf ptr always be a valid empty string.

Taking a page from core git's strbuf, this introduces git_buf_initbuf
which is an empty string that is used to initialize the git_buf ptr
value even for new buffers.  Now the git_buf ptr will always point to
a valid NUL-terminated string.

This change required jumping through a few hoops for git_buf_grow
and git_buf_free to distinguish between a actual allocated buffer
and the global initial value.  Also, this moves the allocation
related functions to be next to each other near the top of buffer.c.
This commit is contained in:
Russell Belfer 2011-11-29 23:45:17 -08:00
parent 7df41387f5
commit 309113c984
3 changed files with 63 additions and 41 deletions

View File

@ -8,13 +8,29 @@
#include "posix.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) \
if ((ssize_t)(d) > buf->asize && git_buf_grow(b, (d)) < GIT_SUCCESS)\
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)
{
char *new_ptr;
size_t new_size;
if (buf->asize < 0)
return GIT_ENOMEM;
@ -22,27 +38,56 @@ int git_buf_grow(git_buf *buf, size_t target_size)
if (target_size <= (size_t)buf->asize)
return GIT_SUCCESS;
if (buf->asize == 0)
buf->asize = target_size;
if (buf->asize == 0) {
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
* to fit our target size */
while (buf->asize < (int)target_size)
buf->asize = (buf->asize << 1) - (buf->asize >> 1);
while (new_size < target_size)
new_size = (new_size << 1) - (new_size >> 1);
/* 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) {
buf->asize = -1;
return GIT_ENOMEM;
}
buf->ptr = new_ptr;
buf->asize = new_size;
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;
}
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)
{
return (buf->asize < 0);
@ -114,7 +159,7 @@ void git_buf_printf(git_buf *buf, const char *format, ...)
const char *git_buf_cstr(git_buf *buf)
{
return buf->ptr ? buf->ptr : "";
return buf->ptr;
}
void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf)
@ -135,23 +180,6 @@ void git_buf_copy_cstr(char *data, size_t datasize, git_buf *buf)
data[copylen] = '\0';
}
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)
{
buf->size = 0;
if (buf->ptr)
*buf->ptr = '\0';
}
void git_buf_consume(git_buf *buf, const char *end)
{
if (end > buf->ptr && end <= buf->ptr + buf->size) {
@ -171,18 +199,12 @@ void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
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;
assert(buf->asize > buf->size);
data = buf->ptr;
buf->ptr = NULL;
buf->asize = 0;
buf->size = 0;
git_buf_init(buf, 0);
return data;
}

View File

@ -14,8 +14,11 @@ typedef struct {
ssize_t asize, size;
} 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);
void git_buf_free(git_buf *buf);
void git_buf_swap(git_buf *buf_a, git_buf *buf_b);

View File

@ -224,10 +224,7 @@ check_buf_append(
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_strequal(expected_data, git_buf_cstr(&tgt));
cl_assert(tgt.size == expected_size);
if (expected_asize > 0)
cl_assert(tgt.asize == expected_asize);
@ -356,13 +353,13 @@ void test_core_buffer__7(void)
b = git_buf_take_cstr(&a);
cl_assert_strequal("foo", b);
cl_assert_strequal(NULL, a.ptr);
cl_assert_strequal("", a.ptr);
git__free(b);
b = git_buf_take_cstr(&a);
cl_assert_strequal(NULL, b);
cl_assert_strequal(NULL, a.ptr);
cl_assert_strequal("", a.ptr);
git_buf_free(&a);
}