Commit 309113c984c1f3157659dc1174e5d4218f610ae4

Russell Belfer 2011-11-29T23:45:17

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.

diff --git a/src/buffer.c b/src/buffer.c
index aa66261..3dff813 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -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;
 }
diff --git a/src/buffer.h b/src/buffer.h
index 9e8ebd0..fa0c7f0 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -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);
diff --git a/tests-clay/core/buffer.c b/tests-clay/core/buffer.c
index 1abee3e..acde8c0 100644
--- a/tests-clay/core/buffer.c
+++ b/tests-clay/core/buffer.c
@@ -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);
 }