]> git.proxmox.com Git - libgit2.git/blame - src/buffer.c
Merge pull request #1204 from arrbee/diff-blob-to-buffer
[libgit2.git] / src / buffer.c
CommitLineData
bb742ede 1/*
5e0de328 2 * Copyright (C) 2009-2012 the libgit2 contributors
bb742ede
VM
3 *
4 * This file is part of libgit2, distributed under the GNU GPL v2 with
5 * a Linking Exception. For full terms see the included COPYING file.
6 */
afeecf4f
VM
7#include "buffer.h"
8#include "posix.h"
9#include <stdarg.h>
13224ea4 10#include <ctype.h>
afeecf4f 11
309113c9
RB
12/* Used as default value for git_buf->ptr so that people can always
13 * assume ptr is non-NULL and zero terminated even for new git_bufs.
14 */
a0d95962 15char git_buf__initbuf[1];
309113c9 16
a0d95962 17char git_buf__oom[1];
13224ea4 18
afeecf4f 19#define ENSURE_SIZE(b, d) \
0d0fa7c3
RB
20 if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
21 return -1;
97769280 22
afeecf4f 23
309113c9
RB
24void git_buf_init(git_buf *buf, size_t initial_size)
25{
26 buf->asize = 0;
27 buf->size = 0;
a0d95962 28 buf->ptr = git_buf__initbuf;
309113c9
RB
29
30 if (initial_size)
31 git_buf_grow(buf, initial_size);
32}
33
7bf87ab6 34int git_buf_try_grow(git_buf *buf, size_t target_size, bool mark_oom)
afeecf4f
VM
35{
36 char *new_ptr;
309113c9 37 size_t new_size;
afeecf4f 38
a0d95962 39 if (buf->ptr == git_buf__oom)
0d0fa7c3 40 return -1;
afeecf4f 41
13224ea4 42 if (target_size <= buf->asize)
0d0fa7c3 43 return 0;
8c74d22e 44
309113c9
RB
45 if (buf->asize == 0) {
46 new_size = target_size;
47 new_ptr = NULL;
48 } else {
13224ea4 49 new_size = buf->asize;
309113c9
RB
50 new_ptr = buf->ptr;
51 }
afeecf4f
VM
52
53 /* grow the buffer size by 1.5, until it's big enough
54 * to fit our target size */
309113c9
RB
55 while (new_size < target_size)
56 new_size = (new_size << 1) - (new_size >> 1);
afeecf4f 57
8c74d22e 58 /* round allocation up to multiple of 8 */
309113c9 59 new_size = (new_size + 7) & ~7;
8c74d22e 60
309113c9 61 new_ptr = git__realloc(new_ptr, new_size);
7bf87ab6
RB
62
63 if (!new_ptr) {
64 if (mark_oom)
65 buf->ptr = git_buf__oom;
cb8a7961 66 return -1;
7bf87ab6 67 }
afeecf4f 68
309113c9
RB
69 buf->asize = new_size;
70 buf->ptr = new_ptr;
71
72 /* truncate the existing buffer size if necessary */
73 if (buf->size >= buf->asize)
74 buf->size = buf->asize - 1;
75 buf->ptr[buf->size] = '\0';
76
0d0fa7c3 77 return 0;
afeecf4f
VM
78}
79
309113c9
RB
80void git_buf_free(git_buf *buf)
81{
82 if (!buf) return;
83
a0d95962 84 if (buf->ptr != git_buf__initbuf && buf->ptr != git_buf__oom)
309113c9
RB
85 git__free(buf->ptr);
86
87 git_buf_init(buf, 0);
88}
89
90void git_buf_clear(git_buf *buf)
91{
92 buf->size = 0;
93 if (buf->asize > 0)
94 buf->ptr[0] = '\0';
95}
96
97769280 97int git_buf_set(git_buf *buf, const char *data, size_t len)
8c74d22e
RB
98{
99 if (len == 0 || data == NULL) {
100 git_buf_clear(buf);
101 } else {
df743c7d
RB
102 if (data != buf->ptr) {
103 ENSURE_SIZE(buf, len + 1);
104 memmove(buf->ptr, data, len);
105 }
8c74d22e 106 buf->size = len;
c63728cd 107 buf->ptr[buf->size] = '\0';
8c74d22e 108 }
0d0fa7c3 109 return 0;
8c74d22e
RB
110}
111
97769280 112int git_buf_sets(git_buf *buf, const char *string)
8c74d22e 113{
97769280 114 return git_buf_set(buf, string, string ? strlen(string) : 0);
8c74d22e
RB
115}
116
97769280 117int git_buf_putc(git_buf *buf, char c)
afeecf4f 118{
c63728cd 119 ENSURE_SIZE(buf, buf->size + 2);
afeecf4f 120 buf->ptr[buf->size++] = c;
c63728cd 121 buf->ptr[buf->size] = '\0';
0d0fa7c3 122 return 0;
afeecf4f
VM
123}
124
97769280 125int git_buf_put(git_buf *buf, const char *data, size_t len)
afeecf4f 126{
c63728cd 127 ENSURE_SIZE(buf, buf->size + len + 1);
8c74d22e 128 memmove(buf->ptr + buf->size, data, len);
afeecf4f 129 buf->size += len;
c63728cd 130 buf->ptr[buf->size] = '\0';
0d0fa7c3 131 return 0;
afeecf4f
VM
132}
133
97769280 134int git_buf_puts(git_buf *buf, const char *string)
afeecf4f 135{
679b69c4 136 assert(string);
97769280 137 return git_buf_put(buf, string, strlen(string));
afeecf4f
VM
138}
139
2d3579be
RB
140static const char b64str[64] =
141 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
142
143int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
144{
145 size_t extra = len % 3;
146 uint8_t *write, a, b, c;
147 const uint8_t *read = (const uint8_t *)data;
148
9d9288f4 149 ENSURE_SIZE(buf, buf->size + 4 * ((len / 3) + !!extra) + 1);
2d3579be
RB
150 write = (uint8_t *)&buf->ptr[buf->size];
151
152 /* convert each run of 3 bytes into 4 output bytes */
153 for (len -= extra; len > 0; len -= 3) {
154 a = *read++;
155 b = *read++;
156 c = *read++;
157
158 *write++ = b64str[a >> 2];
159 *write++ = b64str[(a & 0x03) << 4 | b >> 4];
160 *write++ = b64str[(b & 0x0f) << 2 | c >> 6];
161 *write++ = b64str[c & 0x3f];
162 }
163
164 if (extra > 0) {
165 a = *read++;
166 b = (extra > 1) ? *read++ : 0;
167
168 *write++ = b64str[a >> 2];
169 *write++ = b64str[(a & 0x03) << 4 | b >> 4];
170 *write++ = (extra > 1) ? b64str[(b & 0x0f) << 2] : '=';
171 *write++ = '=';
172 }
173
174 buf->size = ((char *)write) - buf->ptr;
175 buf->ptr[buf->size] = '\0';
176
177 return 0;
178}
179
baaf1c47 180int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
afeecf4f
VM
181{
182 int len;
3f035860 183 const size_t expected_size = buf->size + (strlen(format) * 2);
afeecf4f 184
3f035860 185 ENSURE_SIZE(buf, expected_size);
afeecf4f
VM
186
187 while (1) {
baaf1c47
VM
188 va_list args;
189 va_copy(args, ap);
190
191 len = p_vsnprintf(
192 buf->ptr + buf->size,
193 buf->asize - buf->size,
194 format, args
195 );
afeecf4f
VM
196
197 if (len < 0) {
2bc8fa02 198 git__free(buf->ptr);
a0d95962 199 buf->ptr = git_buf__oom;
0d0fa7c3 200 return -1;
afeecf4f
VM
201 }
202
13224ea4 203 if ((size_t)len + 1 <= buf->asize - buf->size) {
afeecf4f 204 buf->size += len;
97769280 205 break;
afeecf4f
VM
206 }
207
208 ENSURE_SIZE(buf, buf->size + len + 1);
209 }
97769280 210
0d0fa7c3 211 return 0;
afeecf4f
VM
212}
213
baaf1c47
VM
214int git_buf_printf(git_buf *buf, const char *format, ...)
215{
216 int r;
217 va_list ap;
218
219 va_start(ap, format);
220 r = git_buf_vprintf(buf, format, ap);
221 va_end(ap);
222
223 return r;
224}
225
97769280 226void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
c63728cd
RB
227{
228 size_t copylen;
229
1dbcc9fc 230 assert(data && datasize && buf);
c63728cd
RB
231
232 data[0] = '\0';
233
234 if (buf->size == 0 || buf->asize <= 0)
235 return;
236
237 copylen = buf->size;
238 if (copylen > datasize - 1)
239 copylen = datasize - 1;
240 memmove(data, buf->ptr, copylen);
241 data[copylen] = '\0';
afeecf4f
VM
242}
243
c7c30513
CMN
244void git_buf_consume(git_buf *buf, const char *end)
245{
8c74d22e
RB
246 if (end > buf->ptr && end <= buf->ptr + buf->size) {
247 size_t consumed = end - buf->ptr;
248 memmove(buf->ptr, end, buf->size - consumed);
249 buf->size -= consumed;
c63728cd 250 buf->ptr[buf->size] = '\0';
8c74d22e
RB
251 }
252}
253
13224ea4 254void git_buf_truncate(git_buf *buf, size_t len)
97769280 255{
13224ea4 256 if (len < buf->size) {
97769280
RB
257 buf->size = len;
258 buf->ptr[buf->size] = '\0';
259 }
260}
261
b6c93aef
RB
262void git_buf_rtruncate_at_char(git_buf *buf, char separator)
263{
deafee7b
RB
264 ssize_t idx = git_buf_rfind_next(buf, separator);
265 git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
b6c93aef
RB
266}
267
8c74d22e
RB
268void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
269{
270 git_buf t = *buf_a;
271 *buf_a = *buf_b;
272 *buf_b = t;
c7c30513 273}
8c74d22e 274
97769280 275char *git_buf_detach(git_buf *buf)
8c74d22e 276{
309113c9 277 char *data = buf->ptr;
8c74d22e 278
a0d95962 279 if (buf->asize == 0 || buf->ptr == git_buf__oom)
8c74d22e
RB
280 return NULL;
281
309113c9 282 git_buf_init(buf, 0);
8c74d22e
RB
283
284 return data;
285}
286
13224ea4 287void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
8c74d22e 288{
97769280
RB
289 git_buf_free(buf);
290
291 if (ptr) {
292 buf->ptr = ptr;
293 buf->size = strlen(ptr);
294 if (asize)
295 buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
296 else /* pass 0 to fall back on strlen + 1 */
297 buf->asize = buf->size + 1;
298 } else {
299 git_buf_grow(buf, asize);
300 }
301}
8c74d22e 302
97769280
RB
303int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
304{
8c74d22e 305 va_list ap;
0d0fa7c3 306 int i;
952f94c8
RB
307 size_t total_size = 0, original_size = buf->size;
308 char *out, *original = buf->ptr;
8c74d22e
RB
309
310 if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
311 ++total_size; /* space for initial separator */
312
97769280
RB
313 /* Make two passes to avoid multiple reallocation */
314
8c74d22e
RB
315 va_start(ap, nbuf);
316 for (i = 0; i < nbuf; ++i) {
317 const char* segment;
679b69c4 318 size_t segment_len;
8c74d22e
RB
319
320 segment = va_arg(ap, const char *);
321 if (!segment)
322 continue;
323
324 segment_len = strlen(segment);
325 total_size += segment_len;
326 if (segment_len == 0 || segment[segment_len - 1] != separator)
327 ++total_size; /* space for separator */
328 }
329 va_end(ap);
330
97769280 331 /* expand buffer if needed */
952f94c8
RB
332 if (total_size == 0)
333 return 0;
334 if (git_buf_grow(buf, buf->size + total_size + 1) < 0)
0d0fa7c3 335 return -1;
8c74d22e
RB
336
337 out = buf->ptr + buf->size;
338
339 /* append separator to existing buf if needed */
340 if (buf->size > 0 && out[-1] != separator)
341 *out++ = separator;
342
343 va_start(ap, nbuf);
344 for (i = 0; i < nbuf; ++i) {
345 const char* segment;
679b69c4 346 size_t segment_len;
8c74d22e
RB
347
348 segment = va_arg(ap, const char *);
349 if (!segment)
350 continue;
351
952f94c8
RB
352 /* deal with join that references buffer's original content */
353 if (segment >= original && segment < original + original_size) {
354 size_t offset = (segment - original);
355 segment = buf->ptr + offset;
356 segment_len = original_size - offset;
357 } else {
358 segment_len = strlen(segment);
359 }
360
8c74d22e
RB
361 /* skip leading separators */
362 if (out > buf->ptr && out[-1] == separator)
952f94c8
RB
363 while (segment_len > 0 && *segment == separator) {
364 segment++;
365 segment_len--;
366 }
8c74d22e
RB
367
368 /* copy over next buffer */
8c74d22e
RB
369 if (segment_len > 0) {
370 memmove(out, segment, segment_len);
371 out += segment_len;
372 }
373
374 /* append trailing separator (except for last item) */
375 if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
376 *out++ = separator;
377 }
378 va_end(ap);
379
380 /* set size based on num characters actually written */
381 buf->size = out - buf->ptr;
c63728cd 382 buf->ptr[buf->size] = '\0';
97769280 383
0d0fa7c3 384 return 0;
8c74d22e
RB
385}
386
97769280 387int git_buf_join(
3aa294fd
RB
388 git_buf *buf,
389 char separator,
390 const char *str_a,
391 const char *str_b)
392{
b6c93aef 393 size_t strlen_a = str_a ? strlen(str_a) : 0;
969d588d
RB
394 size_t strlen_b = strlen(str_b);
395 int need_sep = 0;
b5daae68
RB
396 ssize_t offset_a = -1;
397
398 /* not safe to have str_b point internally to the buffer */
399 assert(str_b < buf->ptr || str_b > buf->ptr + buf->size);
969d588d
RB
400
401 /* figure out if we need to insert a separator */
402 if (separator && strlen_a) {
403 while (*str_b == separator) { str_b++; strlen_b--; }
404 if (str_a[strlen_a - 1] != separator)
405 need_sep = 1;
3aa294fd 406 }
3aa294fd 407
b5daae68
RB
408 /* str_a could be part of the buffer */
409 if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
410 offset_a = str_a - buf->ptr;
411
0d0fa7c3
RB
412 if (git_buf_grow(buf, strlen_a + strlen_b + need_sep + 1) < 0)
413 return -1;
3aa294fd 414
b5daae68
RB
415 /* fix up internal pointers */
416 if (offset_a >= 0)
417 str_a = buf->ptr + offset_a;
418
419 /* do the actual copying */
420 if (offset_a != 0)
421 memmove(buf->ptr, str_a, strlen_a);
969d588d
RB
422 if (need_sep)
423 buf->ptr[strlen_a] = separator;
b5daae68 424 memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
3aa294fd 425
969d588d 426 buf->size = strlen_a + strlen_b + need_sep;
c63728cd 427 buf->ptr[buf->size] = '\0';
97769280 428
0d0fa7c3 429 return 0;
3aa294fd 430}
13224ea4
VM
431
432void git_buf_rtrim(git_buf *buf)
433{
434 while (buf->size > 0) {
0f49200c 435 if (!git__isspace(buf->ptr[buf->size - 1]))
13224ea4
VM
436 break;
437
438 buf->size--;
439 }
eb8f90e5
VM
440
441 buf->ptr[buf->size] = '\0';
13224ea4 442}
ce49c7a8
RB
443
444int git_buf_cmp(const git_buf *a, const git_buf *b)
445{
446 int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
447 return (result != 0) ? result :
448 (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
449}
41a82592 450
3a14d3e2 451int git_buf_splice(
452 git_buf *buf,
453 size_t where,
454 size_t nb_to_remove,
455 const char *data,
456 size_t nb_to_insert)
457{
458 assert(buf &&
459 where <= git_buf_len(buf) &&
460 where + nb_to_remove <= git_buf_len(buf));
461
462 /* Ported from git.git
463 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
464 */
465 if (git_buf_grow(buf, git_buf_len(buf) + nb_to_insert - nb_to_remove) < 0)
466 return -1;
467
468 memmove(buf->ptr + where + nb_to_insert,
469 buf->ptr + where + nb_to_remove,
470 buf->size - where - nb_to_remove);
471
472 memcpy(buf->ptr + where, data, nb_to_insert);
473
474 buf->size = buf->size + nb_to_insert - nb_to_remove;
475 buf->ptr[buf->size] = '\0';
476 return 0;
477}