]> git.proxmox.com Git - libgit2.git/blob - src/buffer.c
Merge pull request #2907 from jasonhaslam/git_packfile_unpack_race
[libgit2.git] / src / buffer.c
1 /*
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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 */
7 #include "buffer.h"
8 #include "posix.h"
9 #include "git2/buffer.h"
10 #include "buf_text.h"
11 #include <ctype.h>
12
13 /* Used as default value for git_buf->ptr so that people can always
14 * assume ptr is non-NULL and zero terminated even for new git_bufs.
15 */
16 char git_buf__initbuf[1];
17
18 char git_buf__oom[1];
19
20 #define ENSURE_SIZE(b, d) \
21 if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
22 return -1;
23
24
25 void git_buf_init(git_buf *buf, size_t initial_size)
26 {
27 buf->asize = 0;
28 buf->size = 0;
29 buf->ptr = git_buf__initbuf;
30
31 if (initial_size)
32 git_buf_grow(buf, initial_size);
33 }
34
35 int git_buf_try_grow(
36 git_buf *buf, size_t target_size, bool mark_oom, bool preserve_external)
37 {
38 char *new_ptr;
39 size_t new_size;
40
41 if (buf->ptr == git_buf__oom)
42 return -1;
43
44 if (!target_size)
45 target_size = buf->size;
46
47 if (target_size <= buf->asize)
48 return 0;
49
50 if (buf->asize == 0) {
51 new_size = target_size;
52 new_ptr = NULL;
53 } else {
54 new_size = buf->asize;
55 new_ptr = buf->ptr;
56 }
57
58 /* grow the buffer size by 1.5, until it's big enough
59 * to fit our target size */
60 while (new_size < target_size)
61 new_size = (new_size << 1) - (new_size >> 1);
62
63 /* round allocation up to multiple of 8 */
64 new_size = (new_size + 7) & ~7;
65
66 if (new_size < buf->size) {
67 if (mark_oom)
68 buf->ptr = git_buf__oom;
69
70 giterr_set_oom();
71 return -1;
72 }
73
74 new_ptr = git__realloc(new_ptr, new_size);
75
76 if (!new_ptr) {
77 if (mark_oom) {
78 if (buf->ptr && (buf->ptr != git_buf__initbuf))
79 git__free(buf->ptr);
80 buf->ptr = git_buf__oom;
81 }
82 return -1;
83 }
84
85 if (preserve_external && !buf->asize && buf->ptr != NULL && buf->size > 0)
86 memcpy(new_ptr, buf->ptr, min(buf->size, new_size));
87
88 buf->asize = new_size;
89 buf->ptr = new_ptr;
90
91 /* truncate the existing buffer size if necessary */
92 if (buf->size >= buf->asize)
93 buf->size = buf->asize - 1;
94 buf->ptr[buf->size] = '\0';
95
96 return 0;
97 }
98
99 int git_buf_grow(git_buf *buffer, size_t target_size)
100 {
101 return git_buf_try_grow(buffer, target_size, true, true);
102 }
103
104 int git_buf_grow_by(git_buf *buffer, size_t additional_size)
105 {
106 size_t newsize;
107
108 if (GIT_ADD_SIZET_OVERFLOW(&newsize, buffer->size, additional_size)) {
109 buffer->ptr = git_buf__oom;
110 return -1;
111 }
112
113 return git_buf_try_grow(buffer, newsize, true, true);
114 }
115
116 void git_buf_free(git_buf *buf)
117 {
118 if (!buf) return;
119
120 if (buf->asize > 0 && buf->ptr != NULL && buf->ptr != git_buf__oom)
121 git__free(buf->ptr);
122
123 git_buf_init(buf, 0);
124 }
125
126 void git_buf_sanitize(git_buf *buf)
127 {
128 if (buf->ptr == NULL) {
129 assert(buf->size == 0 && buf->asize == 0);
130 buf->ptr = git_buf__initbuf;
131 } else if (buf->asize > buf->size)
132 buf->ptr[buf->size] = '\0';
133 }
134
135 void git_buf_clear(git_buf *buf)
136 {
137 buf->size = 0;
138
139 if (!buf->ptr) {
140 buf->ptr = git_buf__initbuf;
141 buf->asize = 0;
142 }
143
144 if (buf->asize > 0)
145 buf->ptr[0] = '\0';
146 }
147
148 int git_buf_set(git_buf *buf, const void *data, size_t len)
149 {
150 size_t alloclen;
151
152 if (len == 0 || data == NULL) {
153 git_buf_clear(buf);
154 } else {
155 if (data != buf->ptr) {
156 GITERR_CHECK_ALLOC_ADD(&alloclen, len, 1);
157 ENSURE_SIZE(buf, alloclen);
158 memmove(buf->ptr, data, len);
159 }
160
161 buf->size = len;
162 if (buf->asize > buf->size)
163 buf->ptr[buf->size] = '\0';
164
165 }
166 return 0;
167 }
168
169 int git_buf_is_binary(const git_buf *buf)
170 {
171 return git_buf_text_is_binary(buf);
172 }
173
174 int git_buf_contains_nul(const git_buf *buf)
175 {
176 return git_buf_text_contains_nul(buf);
177 }
178
179 int git_buf_sets(git_buf *buf, const char *string)
180 {
181 return git_buf_set(buf, string, string ? strlen(string) : 0);
182 }
183
184 int git_buf_putc(git_buf *buf, char c)
185 {
186 size_t new_size;
187 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, 2);
188 ENSURE_SIZE(buf, new_size);
189 buf->ptr[buf->size++] = c;
190 buf->ptr[buf->size] = '\0';
191 return 0;
192 }
193
194 int git_buf_putcn(git_buf *buf, char c, size_t len)
195 {
196 size_t new_size;
197 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
198 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
199 ENSURE_SIZE(buf, new_size);
200 memset(buf->ptr + buf->size, c, len);
201 buf->size += len;
202 buf->ptr[buf->size] = '\0';
203 return 0;
204 }
205
206 int git_buf_put(git_buf *buf, const char *data, size_t len)
207 {
208 if (len) {
209 size_t new_size;
210
211 assert(data);
212
213 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
214 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
215 ENSURE_SIZE(buf, new_size);
216 memmove(buf->ptr + buf->size, data, len);
217 buf->size += len;
218 buf->ptr[buf->size] = '\0';
219 }
220 return 0;
221 }
222
223 int git_buf_puts(git_buf *buf, const char *string)
224 {
225 assert(string);
226 return git_buf_put(buf, string, strlen(string));
227 }
228
229 static const char base64_encode[] =
230 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
231
232 int git_buf_encode_base64(git_buf *buf, const char *data, size_t len)
233 {
234 size_t extra = len % 3;
235 uint8_t *write, a, b, c;
236 const uint8_t *read = (const uint8_t *)data;
237 size_t blocks = (len / 3) + !!extra, alloclen;
238
239 GITERR_CHECK_ALLOC_ADD(&blocks, blocks, 1);
240 GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 4);
241 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
242
243 ENSURE_SIZE(buf, alloclen);
244 write = (uint8_t *)&buf->ptr[buf->size];
245
246 /* convert each run of 3 bytes into 4 output bytes */
247 for (len -= extra; len > 0; len -= 3) {
248 a = *read++;
249 b = *read++;
250 c = *read++;
251
252 *write++ = base64_encode[a >> 2];
253 *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
254 *write++ = base64_encode[(b & 0x0f) << 2 | c >> 6];
255 *write++ = base64_encode[c & 0x3f];
256 }
257
258 if (extra > 0) {
259 a = *read++;
260 b = (extra > 1) ? *read++ : 0;
261
262 *write++ = base64_encode[a >> 2];
263 *write++ = base64_encode[(a & 0x03) << 4 | b >> 4];
264 *write++ = (extra > 1) ? base64_encode[(b & 0x0f) << 2] : '=';
265 *write++ = '=';
266 }
267
268 buf->size = ((char *)write) - buf->ptr;
269 buf->ptr[buf->size] = '\0';
270
271 return 0;
272 }
273
274 /* The inverse of base64_encode, offset by '+' == 43. */
275 static const int8_t base64_decode[] = {
276 62,
277 -1, -1, -1,
278 63,
279 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
280 -1, -1, -1, 0, -1, -1, -1,
281 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
282 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
283 -1, -1, -1, -1, -1, -1,
284 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38,
285 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
286 };
287
288 #define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
289
290 int git_buf_decode_base64(git_buf *buf, const char *base64, size_t len)
291 {
292 size_t i;
293 int8_t a, b, c, d;
294 size_t orig_size = buf->size, new_size;
295
296 assert(len % 4 == 0);
297 GITERR_CHECK_ALLOC_ADD(&new_size, (len / 4 * 3), buf->size);
298 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
299 ENSURE_SIZE(buf, new_size);
300
301 for (i = 0; i < len; i += 4) {
302 if ((a = BASE64_DECODE_VALUE(base64[i])) < 0 ||
303 (b = BASE64_DECODE_VALUE(base64[i+1])) < 0 ||
304 (c = BASE64_DECODE_VALUE(base64[i+2])) < 0 ||
305 (d = BASE64_DECODE_VALUE(base64[i+3])) < 0) {
306 buf->size = orig_size;
307 buf->ptr[buf->size] = '\0';
308
309 giterr_set(GITERR_INVALID, "Invalid base64 input");
310 return -1;
311 }
312
313 buf->ptr[buf->size++] = ((a << 2) | (b & 0x30) >> 4);
314 buf->ptr[buf->size++] = ((b & 0x0f) << 4) | ((c & 0x3c) >> 2);
315 buf->ptr[buf->size++] = (c & 0x03) << 6 | (d & 0x3f);
316 }
317
318 buf->ptr[buf->size] = '\0';
319 return 0;
320 }
321
322 static const char b85str[] =
323 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
324
325 int git_buf_encode_base85(git_buf *buf, const char *data, size_t len)
326 {
327 size_t blocks = (len / 4) + !!(len % 4), alloclen;
328
329 GITERR_CHECK_ALLOC_MULTIPLY(&alloclen, blocks, 5);
330 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, buf->size);
331 GITERR_CHECK_ALLOC_ADD(&alloclen, alloclen, 1);
332
333 ENSURE_SIZE(buf, alloclen);
334
335 while (len) {
336 uint32_t acc = 0;
337 char b85[5];
338 int i;
339
340 for (i = 24; i >= 0; i -= 8) {
341 uint8_t ch = *data++;
342 acc |= ch << i;
343
344 if (--len == 0)
345 break;
346 }
347
348 for (i = 4; i >= 0; i--) {
349 int val = acc % 85;
350 acc /= 85;
351
352 b85[i] = b85str[val];
353 }
354
355 for (i = 0; i < 5; i++)
356 buf->ptr[buf->size++] = b85[i];
357 }
358
359 buf->ptr[buf->size] = '\0';
360
361 return 0;
362 }
363
364 int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
365 {
366 size_t expected_size, new_size;
367 int len;
368
369 GITERR_CHECK_ALLOC_MULTIPLY(&expected_size, strlen(format), 2);
370 GITERR_CHECK_ALLOC_ADD(&expected_size, expected_size, buf->size);
371 ENSURE_SIZE(buf, expected_size);
372
373 while (1) {
374 va_list args;
375 va_copy(args, ap);
376
377 len = p_vsnprintf(
378 buf->ptr + buf->size,
379 buf->asize - buf->size,
380 format, args
381 );
382
383 va_end(args);
384
385 if (len < 0) {
386 git__free(buf->ptr);
387 buf->ptr = git_buf__oom;
388 return -1;
389 }
390
391 if ((size_t)len + 1 <= buf->asize - buf->size) {
392 buf->size += len;
393 break;
394 }
395
396 GITERR_CHECK_ALLOC_ADD(&new_size, buf->size, len);
397 GITERR_CHECK_ALLOC_ADD(&new_size, new_size, 1);
398 ENSURE_SIZE(buf, new_size);
399 }
400
401 return 0;
402 }
403
404 int git_buf_printf(git_buf *buf, const char *format, ...)
405 {
406 int r;
407 va_list ap;
408
409 va_start(ap, format);
410 r = git_buf_vprintf(buf, format, ap);
411 va_end(ap);
412
413 return r;
414 }
415
416 void git_buf_copy_cstr(char *data, size_t datasize, const git_buf *buf)
417 {
418 size_t copylen;
419
420 assert(data && datasize && buf);
421
422 data[0] = '\0';
423
424 if (buf->size == 0 || buf->asize <= 0)
425 return;
426
427 copylen = buf->size;
428 if (copylen > datasize - 1)
429 copylen = datasize - 1;
430 memmove(data, buf->ptr, copylen);
431 data[copylen] = '\0';
432 }
433
434 void git_buf_consume(git_buf *buf, const char *end)
435 {
436 if (end > buf->ptr && end <= buf->ptr + buf->size) {
437 size_t consumed = end - buf->ptr;
438 memmove(buf->ptr, end, buf->size - consumed);
439 buf->size -= consumed;
440 buf->ptr[buf->size] = '\0';
441 }
442 }
443
444 void git_buf_truncate(git_buf *buf, size_t len)
445 {
446 if (len >= buf->size)
447 return;
448
449 buf->size = len;
450 if (buf->size < buf->asize)
451 buf->ptr[buf->size] = '\0';
452 }
453
454 void git_buf_shorten(git_buf *buf, size_t amount)
455 {
456 if (buf->size > amount)
457 git_buf_truncate(buf, buf->size - amount);
458 else
459 git_buf_clear(buf);
460 }
461
462 void git_buf_rtruncate_at_char(git_buf *buf, char separator)
463 {
464 ssize_t idx = git_buf_rfind_next(buf, separator);
465 git_buf_truncate(buf, idx < 0 ? 0 : (size_t)idx);
466 }
467
468 void git_buf_swap(git_buf *buf_a, git_buf *buf_b)
469 {
470 git_buf t = *buf_a;
471 *buf_a = *buf_b;
472 *buf_b = t;
473 }
474
475 char *git_buf_detach(git_buf *buf)
476 {
477 char *data = buf->ptr;
478
479 if (buf->asize == 0 || buf->ptr == git_buf__oom)
480 return NULL;
481
482 git_buf_init(buf, 0);
483
484 return data;
485 }
486
487 void git_buf_attach(git_buf *buf, char *ptr, size_t asize)
488 {
489 git_buf_free(buf);
490
491 if (ptr) {
492 buf->ptr = ptr;
493 buf->size = strlen(ptr);
494 if (asize)
495 buf->asize = (asize < buf->size) ? buf->size + 1 : asize;
496 else /* pass 0 to fall back on strlen + 1 */
497 buf->asize = buf->size + 1;
498 } else {
499 git_buf_grow(buf, asize);
500 }
501 }
502
503 void git_buf_attach_notowned(git_buf *buf, const char *ptr, size_t size)
504 {
505 if (git_buf_is_allocated(buf))
506 git_buf_free(buf);
507
508 if (!size) {
509 git_buf_init(buf, 0);
510 } else {
511 buf->ptr = (char *)ptr;
512 buf->asize = 0;
513 buf->size = size;
514 }
515 }
516
517 int git_buf_join_n(git_buf *buf, char separator, int nbuf, ...)
518 {
519 va_list ap;
520 int i;
521 size_t total_size = 0, original_size = buf->size;
522 char *out, *original = buf->ptr;
523
524 if (buf->size > 0 && buf->ptr[buf->size - 1] != separator)
525 ++total_size; /* space for initial separator */
526
527 /* Make two passes to avoid multiple reallocation */
528
529 va_start(ap, nbuf);
530 for (i = 0; i < nbuf; ++i) {
531 const char* segment;
532 size_t segment_len;
533
534 segment = va_arg(ap, const char *);
535 if (!segment)
536 continue;
537
538 segment_len = strlen(segment);
539
540 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, segment_len);
541
542 if (segment_len == 0 || segment[segment_len - 1] != separator)
543 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
544 }
545 va_end(ap);
546
547 /* expand buffer if needed */
548 if (total_size == 0)
549 return 0;
550
551 GITERR_CHECK_ALLOC_ADD(&total_size, total_size, 1);
552 if (git_buf_grow_by(buf, total_size) < 0)
553 return -1;
554
555 out = buf->ptr + buf->size;
556
557 /* append separator to existing buf if needed */
558 if (buf->size > 0 && out[-1] != separator)
559 *out++ = separator;
560
561 va_start(ap, nbuf);
562 for (i = 0; i < nbuf; ++i) {
563 const char* segment;
564 size_t segment_len;
565
566 segment = va_arg(ap, const char *);
567 if (!segment)
568 continue;
569
570 /* deal with join that references buffer's original content */
571 if (segment >= original && segment < original + original_size) {
572 size_t offset = (segment - original);
573 segment = buf->ptr + offset;
574 segment_len = original_size - offset;
575 } else {
576 segment_len = strlen(segment);
577 }
578
579 /* skip leading separators */
580 if (out > buf->ptr && out[-1] == separator)
581 while (segment_len > 0 && *segment == separator) {
582 segment++;
583 segment_len--;
584 }
585
586 /* copy over next buffer */
587 if (segment_len > 0) {
588 memmove(out, segment, segment_len);
589 out += segment_len;
590 }
591
592 /* append trailing separator (except for last item) */
593 if (i < nbuf - 1 && out > buf->ptr && out[-1] != separator)
594 *out++ = separator;
595 }
596 va_end(ap);
597
598 /* set size based on num characters actually written */
599 buf->size = out - buf->ptr;
600 buf->ptr[buf->size] = '\0';
601
602 return 0;
603 }
604
605 int git_buf_join(
606 git_buf *buf,
607 char separator,
608 const char *str_a,
609 const char *str_b)
610 {
611 size_t strlen_a = str_a ? strlen(str_a) : 0;
612 size_t strlen_b = strlen(str_b);
613 size_t alloc_len;
614 int need_sep = 0;
615 ssize_t offset_a = -1;
616
617 /* not safe to have str_b point internally to the buffer */
618 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
619
620 /* figure out if we need to insert a separator */
621 if (separator && strlen_a) {
622 while (*str_b == separator) { str_b++; strlen_b--; }
623 if (str_a[strlen_a - 1] != separator)
624 need_sep = 1;
625 }
626
627 /* str_a could be part of the buffer */
628 if (str_a >= buf->ptr && str_a < buf->ptr + buf->size)
629 offset_a = str_a - buf->ptr;
630
631 GITERR_CHECK_ALLOC_ADD(&alloc_len, strlen_a, strlen_b);
632 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, need_sep);
633 GITERR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1);
634 if (git_buf_grow(buf, alloc_len) < 0)
635 return -1;
636 assert(buf->ptr);
637
638 /* fix up internal pointers */
639 if (offset_a >= 0)
640 str_a = buf->ptr + offset_a;
641
642 /* do the actual copying */
643 if (offset_a != 0 && str_a)
644 memmove(buf->ptr, str_a, strlen_a);
645 if (need_sep)
646 buf->ptr[strlen_a] = separator;
647 memcpy(buf->ptr + strlen_a + need_sep, str_b, strlen_b);
648
649 buf->size = strlen_a + strlen_b + need_sep;
650 buf->ptr[buf->size] = '\0';
651
652 return 0;
653 }
654
655 int git_buf_join3(
656 git_buf *buf,
657 char separator,
658 const char *str_a,
659 const char *str_b,
660 const char *str_c)
661 {
662 size_t len_a = strlen(str_a),
663 len_b = strlen(str_b),
664 len_c = strlen(str_c),
665 len_total;
666 int sep_a = 0, sep_b = 0;
667 char *tgt;
668
669 /* for this function, disallow pointers into the existing buffer */
670 assert(str_a < buf->ptr || str_a >= buf->ptr + buf->size);
671 assert(str_b < buf->ptr || str_b >= buf->ptr + buf->size);
672 assert(str_c < buf->ptr || str_c >= buf->ptr + buf->size);
673
674 if (separator) {
675 if (len_a > 0) {
676 while (*str_b == separator) { str_b++; len_b--; }
677 sep_a = (str_a[len_a - 1] != separator);
678 }
679 if (len_a > 0 || len_b > 0)
680 while (*str_c == separator) { str_c++; len_c--; }
681 if (len_b > 0)
682 sep_b = (str_b[len_b - 1] != separator);
683 }
684
685 GITERR_CHECK_ALLOC_ADD(&len_total, len_a, sep_a);
686 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_b);
687 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, sep_b);
688 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, len_c);
689 GITERR_CHECK_ALLOC_ADD(&len_total, len_total, 1);
690 if (git_buf_grow(buf, len_total) < 0)
691 return -1;
692
693 tgt = buf->ptr;
694
695 if (len_a) {
696 memcpy(tgt, str_a, len_a);
697 tgt += len_a;
698 }
699 if (sep_a)
700 *tgt++ = separator;
701 if (len_b) {
702 memcpy(tgt, str_b, len_b);
703 tgt += len_b;
704 }
705 if (sep_b)
706 *tgt++ = separator;
707 if (len_c)
708 memcpy(tgt, str_c, len_c);
709
710 buf->size = len_a + sep_a + len_b + sep_b + len_c;
711 buf->ptr[buf->size] = '\0';
712
713 return 0;
714 }
715
716 void git_buf_rtrim(git_buf *buf)
717 {
718 while (buf->size > 0) {
719 if (!git__isspace(buf->ptr[buf->size - 1]))
720 break;
721
722 buf->size--;
723 }
724
725 if (buf->asize > buf->size)
726 buf->ptr[buf->size] = '\0';
727 }
728
729 int git_buf_cmp(const git_buf *a, const git_buf *b)
730 {
731 int result = memcmp(a->ptr, b->ptr, min(a->size, b->size));
732 return (result != 0) ? result :
733 (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0;
734 }
735
736 int git_buf_splice(
737 git_buf *buf,
738 size_t where,
739 size_t nb_to_remove,
740 const char *data,
741 size_t nb_to_insert)
742 {
743 char *splice_loc;
744 size_t new_size, alloc_size;
745
746 assert(buf && where <= buf->size && nb_to_remove <= buf->size - where);
747
748 splice_loc = buf->ptr + where;
749
750 /* Ported from git.git
751 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
752 */
753 GITERR_CHECK_ALLOC_ADD(&new_size, (buf->size - nb_to_remove), nb_to_insert);
754 GITERR_CHECK_ALLOC_ADD(&alloc_size, new_size, 1);
755 ENSURE_SIZE(buf, alloc_size);
756
757 memmove(splice_loc + nb_to_insert,
758 splice_loc + nb_to_remove,
759 buf->size - where - nb_to_remove);
760
761 memcpy(splice_loc, data, nb_to_insert);
762
763 buf->size = new_size;
764 buf->ptr[buf->size] = '\0';
765 return 0;
766 }