]>
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"
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.
15 char git_buf__initbuf
[1];
19 #define ENSURE_SIZE(b, d) \
20 if ((d) > buf->asize && git_buf_grow(b, (d)) < 0)\
24 void git_buf_init(git_buf
*buf
, size_t initial_size
)
28 buf
->ptr
= git_buf__initbuf
;
31 git_buf_grow(buf
, initial_size
);
35 git_buf
*buf
, size_t target_size
, bool mark_oom
, bool preserve_external
)
40 if (buf
->ptr
== git_buf__oom
)
44 target_size
= buf
->size
;
46 if (target_size
<= buf
->asize
)
49 if (buf
->asize
== 0) {
50 new_size
= target_size
;
53 new_size
= buf
->asize
;
57 /* grow the buffer size by 1.5, until it's big enough
58 * to fit our target size */
59 while (new_size
< target_size
)
60 new_size
= (new_size
<< 1) - (new_size
>> 1);
62 /* round allocation up to multiple of 8 */
63 new_size
= (new_size
+ 7) & ~7;
65 new_ptr
= git__realloc(new_ptr
, new_size
);
69 if (buf
->ptr
) git__free(buf
->ptr
);
70 buf
->ptr
= git_buf__oom
;
75 if (preserve_external
&& !buf
->asize
&& buf
->ptr
!= NULL
&& buf
->size
> 0)
76 memcpy(new_ptr
, buf
->ptr
, min(buf
->size
, new_size
));
78 buf
->asize
= new_size
;
81 /* truncate the existing buffer size if necessary */
82 if (buf
->size
>= buf
->asize
)
83 buf
->size
= buf
->asize
- 1;
84 buf
->ptr
[buf
->size
] = '\0';
89 int git_buf_grow(git_buf
*buffer
, size_t target_size
)
91 return git_buf_try_grow(buffer
, target_size
, true, true);
94 void git_buf_free(git_buf
*buf
)
98 if (buf
->asize
> 0 && buf
->ptr
!= NULL
&& buf
->ptr
!= git_buf__oom
)
101 git_buf_init(buf
, 0);
104 void git_buf_sanitize(git_buf
*buf
)
106 if (buf
->ptr
== NULL
) {
107 assert(buf
->size
== 0 && buf
->asize
== 0);
108 buf
->ptr
= git_buf__initbuf
;
109 } else if (buf
->asize
> buf
->size
)
110 buf
->ptr
[buf
->size
] = '\0';
113 void git_buf_clear(git_buf
*buf
)
118 buf
->ptr
= git_buf__initbuf
;
126 int git_buf_set(git_buf
*buf
, const void *data
, size_t len
)
128 if (len
== 0 || data
== NULL
) {
131 if (data
!= buf
->ptr
) {
132 ENSURE_SIZE(buf
, len
+ 1);
133 memmove(buf
->ptr
, data
, len
);
137 if (buf
->asize
> buf
->size
)
138 buf
->ptr
[buf
->size
] = '\0';
144 int git_buf_sets(git_buf
*buf
, const char *string
)
146 return git_buf_set(buf
, string
, string
? strlen(string
) : 0);
149 int git_buf_putc(git_buf
*buf
, char c
)
151 ENSURE_SIZE(buf
, buf
->size
+ 2);
152 buf
->ptr
[buf
->size
++] = c
;
153 buf
->ptr
[buf
->size
] = '\0';
157 int git_buf_putcn(git_buf
*buf
, char c
, size_t len
)
159 ENSURE_SIZE(buf
, buf
->size
+ len
+ 1);
160 memset(buf
->ptr
+ buf
->size
, c
, len
);
162 buf
->ptr
[buf
->size
] = '\0';
166 int git_buf_put(git_buf
*buf
, const char *data
, size_t len
)
168 ENSURE_SIZE(buf
, buf
->size
+ len
+ 1);
169 memmove(buf
->ptr
+ buf
->size
, data
, len
);
171 buf
->ptr
[buf
->size
] = '\0';
175 int git_buf_puts(git_buf
*buf
, const char *string
)
178 return git_buf_put(buf
, string
, strlen(string
));
181 static const char b64str
[] =
182 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
184 int git_buf_put_base64(git_buf
*buf
, const char *data
, size_t len
)
186 size_t extra
= len
% 3;
187 uint8_t *write
, a
, b
, c
;
188 const uint8_t *read
= (const uint8_t *)data
;
190 ENSURE_SIZE(buf
, buf
->size
+ 4 * ((len
/ 3) + !!extra
) + 1);
191 write
= (uint8_t *)&buf
->ptr
[buf
->size
];
193 /* convert each run of 3 bytes into 4 output bytes */
194 for (len
-= extra
; len
> 0; len
-= 3) {
199 *write
++ = b64str
[a
>> 2];
200 *write
++ = b64str
[(a
& 0x03) << 4 | b
>> 4];
201 *write
++ = b64str
[(b
& 0x0f) << 2 | c
>> 6];
202 *write
++ = b64str
[c
& 0x3f];
207 b
= (extra
> 1) ? *read
++ : 0;
209 *write
++ = b64str
[a
>> 2];
210 *write
++ = b64str
[(a
& 0x03) << 4 | b
>> 4];
211 *write
++ = (extra
> 1) ? b64str
[(b
& 0x0f) << 2] : '=';
215 buf
->size
= ((char *)write
) - buf
->ptr
;
216 buf
->ptr
[buf
->size
] = '\0';
221 static const char b85str
[] =
222 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
224 int git_buf_put_base85(git_buf
*buf
, const char *data
, size_t len
)
226 ENSURE_SIZE(buf
, buf
->size
+ (5 * ((len
/ 4) + !!(len
% 4))) + 1);
233 for (i
= 24; i
>= 0; i
-= 8) {
234 uint8_t ch
= *data
++;
241 for (i
= 4; i
>= 0; i
--) {
245 b85
[i
] = b85str
[val
];
248 for (i
= 0; i
< 5; i
++)
249 buf
->ptr
[buf
->size
++] = b85
[i
];
252 buf
->ptr
[buf
->size
] = '\0';
257 int git_buf_vprintf(git_buf
*buf
, const char *format
, va_list ap
)
260 const size_t expected_size
= buf
->size
+ (strlen(format
) * 2);
262 ENSURE_SIZE(buf
, expected_size
);
269 buf
->ptr
+ buf
->size
,
270 buf
->asize
- buf
->size
,
278 buf
->ptr
= git_buf__oom
;
282 if ((size_t)len
+ 1 <= buf
->asize
- buf
->size
) {
287 ENSURE_SIZE(buf
, buf
->size
+ len
+ 1);
293 int git_buf_printf(git_buf
*buf
, const char *format
, ...)
298 va_start(ap
, format
);
299 r
= git_buf_vprintf(buf
, format
, ap
);
305 void git_buf_copy_cstr(char *data
, size_t datasize
, const git_buf
*buf
)
309 assert(data
&& datasize
&& buf
);
313 if (buf
->size
== 0 || buf
->asize
<= 0)
317 if (copylen
> datasize
- 1)
318 copylen
= datasize
- 1;
319 memmove(data
, buf
->ptr
, copylen
);
320 data
[copylen
] = '\0';
323 void git_buf_consume(git_buf
*buf
, const char *end
)
325 if (end
> buf
->ptr
&& end
<= buf
->ptr
+ buf
->size
) {
326 size_t consumed
= end
- buf
->ptr
;
327 memmove(buf
->ptr
, end
, buf
->size
- consumed
);
328 buf
->size
-= consumed
;
329 buf
->ptr
[buf
->size
] = '\0';
333 void git_buf_truncate(git_buf
*buf
, size_t len
)
335 if (len
>= buf
->size
)
339 if (buf
->size
< buf
->asize
)
340 buf
->ptr
[buf
->size
] = '\0';
343 void git_buf_shorten(git_buf
*buf
, size_t amount
)
345 if (buf
->size
> amount
)
346 git_buf_truncate(buf
, buf
->size
- amount
);
351 void git_buf_rtruncate_at_char(git_buf
*buf
, char separator
)
353 ssize_t idx
= git_buf_rfind_next(buf
, separator
);
354 git_buf_truncate(buf
, idx
< 0 ? 0 : (size_t)idx
);
357 void git_buf_swap(git_buf
*buf_a
, git_buf
*buf_b
)
364 char *git_buf_detach(git_buf
*buf
)
366 char *data
= buf
->ptr
;
368 if (buf
->asize
== 0 || buf
->ptr
== git_buf__oom
)
371 git_buf_init(buf
, 0);
376 void git_buf_attach(git_buf
*buf
, char *ptr
, size_t asize
)
382 buf
->size
= strlen(ptr
);
384 buf
->asize
= (asize
< buf
->size
) ? buf
->size
+ 1 : asize
;
385 else /* pass 0 to fall back on strlen + 1 */
386 buf
->asize
= buf
->size
+ 1;
388 git_buf_grow(buf
, asize
);
392 int git_buf_join_n(git_buf
*buf
, char separator
, int nbuf
, ...)
396 size_t total_size
= 0, original_size
= buf
->size
;
397 char *out
, *original
= buf
->ptr
;
399 if (buf
->size
> 0 && buf
->ptr
[buf
->size
- 1] != separator
)
400 ++total_size
; /* space for initial separator */
402 /* Make two passes to avoid multiple reallocation */
405 for (i
= 0; i
< nbuf
; ++i
) {
409 segment
= va_arg(ap
, const char *);
413 segment_len
= strlen(segment
);
414 total_size
+= segment_len
;
415 if (segment_len
== 0 || segment
[segment_len
- 1] != separator
)
416 ++total_size
; /* space for separator */
420 /* expand buffer if needed */
423 if (git_buf_grow(buf
, buf
->size
+ total_size
+ 1) < 0)
426 out
= buf
->ptr
+ buf
->size
;
428 /* append separator to existing buf if needed */
429 if (buf
->size
> 0 && out
[-1] != separator
)
433 for (i
= 0; i
< nbuf
; ++i
) {
437 segment
= va_arg(ap
, const char *);
441 /* deal with join that references buffer's original content */
442 if (segment
>= original
&& segment
< original
+ original_size
) {
443 size_t offset
= (segment
- original
);
444 segment
= buf
->ptr
+ offset
;
445 segment_len
= original_size
- offset
;
447 segment_len
= strlen(segment
);
450 /* skip leading separators */
451 if (out
> buf
->ptr
&& out
[-1] == separator
)
452 while (segment_len
> 0 && *segment
== separator
) {
457 /* copy over next buffer */
458 if (segment_len
> 0) {
459 memmove(out
, segment
, segment_len
);
463 /* append trailing separator (except for last item) */
464 if (i
< nbuf
- 1 && out
> buf
->ptr
&& out
[-1] != separator
)
469 /* set size based on num characters actually written */
470 buf
->size
= out
- buf
->ptr
;
471 buf
->ptr
[buf
->size
] = '\0';
482 size_t strlen_a
= str_a
? strlen(str_a
) : 0;
483 size_t strlen_b
= strlen(str_b
);
485 ssize_t offset_a
= -1;
487 /* not safe to have str_b point internally to the buffer */
488 assert(str_b
< buf
->ptr
|| str_b
>= buf
->ptr
+ buf
->size
);
490 /* figure out if we need to insert a separator */
491 if (separator
&& strlen_a
) {
492 while (*str_b
== separator
) { str_b
++; strlen_b
--; }
493 if (str_a
[strlen_a
- 1] != separator
)
497 /* str_a could be part of the buffer */
498 if (str_a
>= buf
->ptr
&& str_a
< buf
->ptr
+ buf
->size
)
499 offset_a
= str_a
- buf
->ptr
;
501 if (git_buf_grow(buf
, strlen_a
+ strlen_b
+ need_sep
+ 1) < 0)
505 /* fix up internal pointers */
507 str_a
= buf
->ptr
+ offset_a
;
509 /* do the actual copying */
510 if (offset_a
!= 0 && str_a
)
511 memmove(buf
->ptr
, str_a
, strlen_a
);
513 buf
->ptr
[strlen_a
] = separator
;
514 memcpy(buf
->ptr
+ strlen_a
+ need_sep
, str_b
, strlen_b
);
516 buf
->size
= strlen_a
+ strlen_b
+ need_sep
;
517 buf
->ptr
[buf
->size
] = '\0';
529 size_t len_a
= strlen(str_a
), len_b
= strlen(str_b
), len_c
= strlen(str_c
);
530 int sep_a
= 0, sep_b
= 0;
533 /* for this function, disallow pointers into the existing buffer */
534 assert(str_a
< buf
->ptr
|| str_a
>= buf
->ptr
+ buf
->size
);
535 assert(str_b
< buf
->ptr
|| str_b
>= buf
->ptr
+ buf
->size
);
536 assert(str_c
< buf
->ptr
|| str_c
>= buf
->ptr
+ buf
->size
);
540 while (*str_b
== separator
) { str_b
++; len_b
--; }
541 sep_a
= (str_a
[len_a
- 1] != separator
);
543 if (len_a
> 0 || len_b
> 0)
544 while (*str_c
== separator
) { str_c
++; len_c
--; }
546 sep_b
= (str_b
[len_b
- 1] != separator
);
549 if (git_buf_grow(buf
, len_a
+ sep_a
+ len_b
+ sep_b
+ len_c
+ 1) < 0)
555 memcpy(tgt
, str_a
, len_a
);
561 memcpy(tgt
, str_b
, len_b
);
567 memcpy(tgt
, str_c
, len_c
);
569 buf
->size
= len_a
+ sep_a
+ len_b
+ sep_b
+ len_c
;
570 buf
->ptr
[buf
->size
] = '\0';
575 void git_buf_rtrim(git_buf
*buf
)
577 while (buf
->size
> 0) {
578 if (!git__isspace(buf
->ptr
[buf
->size
- 1]))
584 if (buf
->asize
> buf
->size
)
585 buf
->ptr
[buf
->size
] = '\0';
588 int git_buf_cmp(const git_buf
*a
, const git_buf
*b
)
590 int result
= memcmp(a
->ptr
, b
->ptr
, min(a
->size
, b
->size
));
591 return (result
!= 0) ? result
:
592 (a
->size
< b
->size
) ? -1 : (a
->size
> b
->size
) ? 1 : 0;
603 where
<= git_buf_len(buf
) &&
604 where
+ nb_to_remove
<= git_buf_len(buf
));
606 /* Ported from git.git
607 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
609 ENSURE_SIZE(buf
, buf
->size
+ nb_to_insert
- nb_to_insert
+ 1);
611 memmove(buf
->ptr
+ where
+ nb_to_insert
,
612 buf
->ptr
+ where
+ nb_to_remove
,
613 buf
->size
- where
- nb_to_remove
);
615 memcpy(buf
->ptr
+ where
, data
, nb_to_insert
);
617 buf
->size
= buf
->size
+ nb_to_insert
- nb_to_remove
;
618 buf
->ptr
[buf
->size
] = '\0';