]>
git.proxmox.com Git - libgit2.git/blob - src/buffer.c
2 * Copyright (C) the libgit2 contributors. All rights reserved.
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.
9 #include "git2/buffer.h"
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.
16 char git_buf__initbuf
[1];
20 #define ENSURE_SIZE(b, d) \
21 if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
25 void git_buf_init(git_buf
*buf
, size_t initial_size
)
29 buf
->ptr
= git_buf__initbuf
;
32 git_buf_grow(buf
, initial_size
);
36 git_buf
*buf
, size_t target_size
, bool mark_oom
, bool preserve_external
)
41 if (buf
->ptr
== git_buf__oom
)
45 target_size
= buf
->size
;
47 if (target_size
<= buf
->asize
)
50 if (buf
->asize
== 0) {
51 new_size
= target_size
;
54 new_size
= buf
->asize
;
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);
63 /* round allocation up to multiple of 8 */
64 new_size
= (new_size
+ 7) & ~7;
66 if (new_size
< buf
->size
) {
68 buf
->ptr
= git_buf__oom
;
74 new_ptr
= git__realloc(new_ptr
, new_size
);
78 if (buf
->ptr
&& (buf
->ptr
!= git_buf__initbuf
))
80 buf
->ptr
= git_buf__oom
;
85 if (preserve_external
&& !buf
->asize
&& buf
->ptr
!= NULL
&& buf
->size
> 0)
86 memcpy(new_ptr
, buf
->ptr
, min(buf
->size
, new_size
));
88 buf
->asize
= new_size
;
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';
99 int git_buf_grow(git_buf
*buffer
, size_t target_size
)
101 return git_buf_try_grow(buffer
, target_size
, true, true);
104 int git_buf_grow_by(git_buf
*buffer
, size_t additional_size
)
108 if (GIT_ADD_SIZET_OVERFLOW(&newsize
, buffer
->size
, additional_size
)) {
109 buffer
->ptr
= git_buf__oom
;
113 return git_buf_try_grow(buffer
, newsize
, true, true);
116 void git_buf_free(git_buf
*buf
)
120 if (buf
->asize
> 0 && buf
->ptr
!= NULL
&& buf
->ptr
!= git_buf__oom
)
123 git_buf_init(buf
, 0);
126 void git_buf_sanitize(git_buf
*buf
)
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';
135 void git_buf_clear(git_buf
*buf
)
140 buf
->ptr
= git_buf__initbuf
;
148 int git_buf_set(git_buf
*buf
, const void *data
, size_t len
)
152 if (len
== 0 || data
== NULL
) {
155 if (data
!= buf
->ptr
) {
156 GITERR_CHECK_ALLOC_ADD(&alloclen
, len
, 1);
157 ENSURE_SIZE(buf
, alloclen
);
158 memmove(buf
->ptr
, data
, len
);
162 if (buf
->asize
> buf
->size
)
163 buf
->ptr
[buf
->size
] = '\0';
169 int git_buf_is_binary(const git_buf
*buf
)
171 return git_buf_text_is_binary(buf
);
174 int git_buf_contains_nul(const git_buf
*buf
)
176 return git_buf_text_contains_nul(buf
);
179 int git_buf_sets(git_buf
*buf
, const char *string
)
181 return git_buf_set(buf
, string
, string
? strlen(string
) : 0);
184 int git_buf_putc(git_buf
*buf
, char c
)
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';
194 int git_buf_putcn(git_buf
*buf
, char c
, size_t len
)
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
);
202 buf
->ptr
[buf
->size
] = '\0';
206 int git_buf_put(git_buf
*buf
, const char *data
, size_t len
)
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
);
218 buf
->ptr
[buf
->size
] = '\0';
223 int git_buf_puts(git_buf
*buf
, const char *string
)
226 return git_buf_put(buf
, string
, strlen(string
));
229 static const char base64_encode
[] =
230 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
232 int git_buf_encode_base64(git_buf
*buf
, const char *data
, size_t len
)
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
;
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
);
243 ENSURE_SIZE(buf
, alloclen
);
244 write
= (uint8_t *)&buf
->ptr
[buf
->size
];
246 /* convert each run of 3 bytes into 4 output bytes */
247 for (len
-= extra
; len
> 0; len
-= 3) {
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];
260 b
= (extra
> 1) ? *read
++ : 0;
262 *write
++ = base64_encode
[a
>> 2];
263 *write
++ = base64_encode
[(a
& 0x03) << 4 | b
>> 4];
264 *write
++ = (extra
> 1) ? base64_encode
[(b
& 0x0f) << 2] : '=';
268 buf
->size
= ((char *)write
) - buf
->ptr
;
269 buf
->ptr
[buf
->size
] = '\0';
274 /* The inverse of base64_encode, offset by '+' == 43. */
275 static const int8_t base64_decode
[] = {
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
288 #define BASE64_DECODE_VALUE(c) (((c) < 43 || (c) > 122) ? -1 : base64_decode[c - 43])
290 int git_buf_decode_base64(git_buf
*buf
, const char *base64
, size_t len
)
294 size_t orig_size
= buf
->size
, new_size
;
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
);
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';
309 giterr_set(GITERR_INVALID
, "Invalid base64 input");
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);
318 buf
->ptr
[buf
->size
] = '\0';
322 static const char b85str
[] =
323 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
325 int git_buf_encode_base85(git_buf
*buf
, const char *data
, size_t len
)
327 size_t blocks
= (len
/ 4) + !!(len
% 4), alloclen
;
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);
333 ENSURE_SIZE(buf
, alloclen
);
340 for (i
= 24; i
>= 0; i
-= 8) {
341 uint8_t ch
= *data
++;
348 for (i
= 4; i
>= 0; i
--) {
352 b85
[i
] = b85str
[val
];
355 for (i
= 0; i
< 5; i
++)
356 buf
->ptr
[buf
->size
++] = b85
[i
];
359 buf
->ptr
[buf
->size
] = '\0';
364 int git_buf_vprintf(git_buf
*buf
, const char *format
, va_list ap
)
366 size_t expected_size
, new_size
;
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
);
378 buf
->ptr
+ buf
->size
,
379 buf
->asize
- buf
->size
,
387 buf
->ptr
= git_buf__oom
;
391 if ((size_t)len
+ 1 <= buf
->asize
- buf
->size
) {
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
);
404 int git_buf_printf(git_buf
*buf
, const char *format
, ...)
409 va_start(ap
, format
);
410 r
= git_buf_vprintf(buf
, format
, ap
);
416 void git_buf_copy_cstr(char *data
, size_t datasize
, const git_buf
*buf
)
420 assert(data
&& datasize
&& buf
);
424 if (buf
->size
== 0 || buf
->asize
<= 0)
428 if (copylen
> datasize
- 1)
429 copylen
= datasize
- 1;
430 memmove(data
, buf
->ptr
, copylen
);
431 data
[copylen
] = '\0';
434 void git_buf_consume(git_buf
*buf
, const char *end
)
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';
444 void git_buf_truncate(git_buf
*buf
, size_t len
)
446 if (len
>= buf
->size
)
450 if (buf
->size
< buf
->asize
)
451 buf
->ptr
[buf
->size
] = '\0';
454 void git_buf_shorten(git_buf
*buf
, size_t amount
)
456 if (buf
->size
> amount
)
457 git_buf_truncate(buf
, buf
->size
- amount
);
462 void git_buf_rtruncate_at_char(git_buf
*buf
, char separator
)
464 ssize_t idx
= git_buf_rfind_next(buf
, separator
);
465 git_buf_truncate(buf
, idx
< 0 ? 0 : (size_t)idx
);
468 void git_buf_swap(git_buf
*buf_a
, git_buf
*buf_b
)
475 char *git_buf_detach(git_buf
*buf
)
477 char *data
= buf
->ptr
;
479 if (buf
->asize
== 0 || buf
->ptr
== git_buf__oom
)
482 git_buf_init(buf
, 0);
487 void git_buf_attach(git_buf
*buf
, char *ptr
, size_t asize
)
493 buf
->size
= strlen(ptr
);
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;
499 git_buf_grow(buf
, asize
);
503 void git_buf_attach_notowned(git_buf
*buf
, const char *ptr
, size_t size
)
505 if (git_buf_is_allocated(buf
))
509 git_buf_init(buf
, 0);
511 buf
->ptr
= (char *)ptr
;
517 int git_buf_join_n(git_buf
*buf
, char separator
, int nbuf
, ...)
521 size_t total_size
= 0, original_size
= buf
->size
;
522 char *out
, *original
= buf
->ptr
;
524 if (buf
->size
> 0 && buf
->ptr
[buf
->size
- 1] != separator
)
525 ++total_size
; /* space for initial separator */
527 /* Make two passes to avoid multiple reallocation */
530 for (i
= 0; i
< nbuf
; ++i
) {
534 segment
= va_arg(ap
, const char *);
538 segment_len
= strlen(segment
);
540 GITERR_CHECK_ALLOC_ADD(&total_size
, total_size
, segment_len
);
542 if (segment_len
== 0 || segment
[segment_len
- 1] != separator
)
543 GITERR_CHECK_ALLOC_ADD(&total_size
, total_size
, 1);
547 /* expand buffer if needed */
551 GITERR_CHECK_ALLOC_ADD(&total_size
, total_size
, 1);
552 if (git_buf_grow_by(buf
, total_size
) < 0)
555 out
= buf
->ptr
+ buf
->size
;
557 /* append separator to existing buf if needed */
558 if (buf
->size
> 0 && out
[-1] != separator
)
562 for (i
= 0; i
< nbuf
; ++i
) {
566 segment
= va_arg(ap
, const char *);
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
;
576 segment_len
= strlen(segment
);
579 /* skip leading separators */
580 if (out
> buf
->ptr
&& out
[-1] == separator
)
581 while (segment_len
> 0 && *segment
== separator
) {
586 /* copy over next buffer */
587 if (segment_len
> 0) {
588 memmove(out
, segment
, segment_len
);
592 /* append trailing separator (except for last item) */
593 if (i
< nbuf
- 1 && out
> buf
->ptr
&& out
[-1] != separator
)
598 /* set size based on num characters actually written */
599 buf
->size
= out
- buf
->ptr
;
600 buf
->ptr
[buf
->size
] = '\0';
611 size_t strlen_a
= str_a
? strlen(str_a
) : 0;
612 size_t strlen_b
= strlen(str_b
);
615 ssize_t offset_a
= -1;
617 /* not safe to have str_b point internally to the buffer */
618 assert(str_b
< buf
->ptr
|| str_b
>= buf
->ptr
+ buf
->size
);
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
)
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
;
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)
638 /* fix up internal pointers */
640 str_a
= buf
->ptr
+ offset_a
;
642 /* do the actual copying */
643 if (offset_a
!= 0 && str_a
)
644 memmove(buf
->ptr
, str_a
, strlen_a
);
646 buf
->ptr
[strlen_a
] = separator
;
647 memcpy(buf
->ptr
+ strlen_a
+ need_sep
, str_b
, strlen_b
);
649 buf
->size
= strlen_a
+ strlen_b
+ need_sep
;
650 buf
->ptr
[buf
->size
] = '\0';
662 size_t len_a
= strlen(str_a
),
663 len_b
= strlen(str_b
),
664 len_c
= strlen(str_c
),
666 int sep_a
= 0, sep_b
= 0;
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
);
676 while (*str_b
== separator
) { str_b
++; len_b
--; }
677 sep_a
= (str_a
[len_a
- 1] != separator
);
679 if (len_a
> 0 || len_b
> 0)
680 while (*str_c
== separator
) { str_c
++; len_c
--; }
682 sep_b
= (str_b
[len_b
- 1] != separator
);
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)
696 memcpy(tgt
, str_a
, len_a
);
702 memcpy(tgt
, str_b
, len_b
);
708 memcpy(tgt
, str_c
, len_c
);
710 buf
->size
= len_a
+ sep_a
+ len_b
+ sep_b
+ len_c
;
711 buf
->ptr
[buf
->size
] = '\0';
716 void git_buf_rtrim(git_buf
*buf
)
718 while (buf
->size
> 0) {
719 if (!git__isspace(buf
->ptr
[buf
->size
- 1]))
725 if (buf
->asize
> buf
->size
)
726 buf
->ptr
[buf
->size
] = '\0';
729 int git_buf_cmp(const git_buf
*a
, const git_buf
*b
)
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;
744 size_t new_size
, alloc_size
;
746 assert(buf
&& where
<= buf
->size
&& nb_to_remove
<= buf
->size
- where
);
748 splice_loc
= buf
->ptr
+ where
;
750 /* Ported from git.git
751 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
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
);
757 memmove(splice_loc
+ nb_to_insert
,
758 splice_loc
+ nb_to_remove
,
759 buf
->size
- where
- nb_to_remove
);
761 memcpy(splice_loc
, data
, nb_to_insert
);
763 buf
->size
= new_size
;
764 buf
->ptr
[buf
->size
] = '\0';