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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202
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);
}