]>
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
);
35 int git_buf_try_grow(git_buf
*buf
, size_t target_size
, bool mark_oom
)
40 if (buf
->ptr
== git_buf__oom
)
43 if (target_size
<= buf
->asize
)
46 if (buf
->asize
== 0) {
47 new_size
= target_size
;
50 new_size
= buf
->asize
;
54 /* grow the buffer size by 1.5, until it's big enough
55 * to fit our target size */
56 while (new_size
< target_size
)
57 new_size
= (new_size
<< 1) - (new_size
>> 1);
59 /* round allocation up to multiple of 8 */
60 new_size
= (new_size
+ 7) & ~7;
62 new_ptr
= git__realloc(new_ptr
, new_size
);
66 buf
->ptr
= git_buf__oom
;
70 buf
->asize
= new_size
;
73 /* truncate the existing buffer size if necessary */
74 if (buf
->size
>= buf
->asize
)
75 buf
->size
= buf
->asize
- 1;
76 buf
->ptr
[buf
->size
] = '\0';
81 void git_buf_free(git_buf
*buf
)
85 if (buf
->ptr
!= git_buf__initbuf
&& buf
->ptr
!= git_buf__oom
)
91 void git_buf_clear(git_buf
*buf
)
98 int git_buf_set(git_buf
*buf
, const char *data
, size_t len
)
100 if (len
== 0 || data
== NULL
) {
103 if (data
!= buf
->ptr
) {
104 ENSURE_SIZE(buf
, len
+ 1);
105 memmove(buf
->ptr
, data
, len
);
108 buf
->ptr
[buf
->size
] = '\0';
113 int git_buf_sets(git_buf
*buf
, const char *string
)
115 return git_buf_set(buf
, string
, string
? strlen(string
) : 0);
118 int git_buf_putc(git_buf
*buf
, char c
)
120 ENSURE_SIZE(buf
, buf
->size
+ 2);
121 buf
->ptr
[buf
->size
++] = c
;
122 buf
->ptr
[buf
->size
] = '\0';
126 int git_buf_put(git_buf
*buf
, const char *data
, size_t len
)
128 ENSURE_SIZE(buf
, buf
->size
+ len
+ 1);
129 memmove(buf
->ptr
+ buf
->size
, data
, len
);
131 buf
->ptr
[buf
->size
] = '\0';
135 int git_buf_puts(git_buf
*buf
, const char *string
)
138 return git_buf_put(buf
, string
, strlen(string
));
141 static const char b64str
[64] =
142 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
144 int git_buf_put_base64(git_buf
*buf
, const char *data
, size_t len
)
146 size_t extra
= len
% 3;
147 uint8_t *write
, a
, b
, c
;
148 const uint8_t *read
= (const uint8_t *)data
;
150 ENSURE_SIZE(buf
, buf
->size
+ 4 * ((len
/ 3) + !!extra
) + 1);
151 write
= (uint8_t *)&buf
->ptr
[buf
->size
];
153 /* convert each run of 3 bytes into 4 output bytes */
154 for (len
-= extra
; len
> 0; len
-= 3) {
159 *write
++ = b64str
[a
>> 2];
160 *write
++ = b64str
[(a
& 0x03) << 4 | b
>> 4];
161 *write
++ = b64str
[(b
& 0x0f) << 2 | c
>> 6];
162 *write
++ = b64str
[c
& 0x3f];
167 b
= (extra
> 1) ? *read
++ : 0;
169 *write
++ = b64str
[a
>> 2];
170 *write
++ = b64str
[(a
& 0x03) << 4 | b
>> 4];
171 *write
++ = (extra
> 1) ? b64str
[(b
& 0x0f) << 2] : '=';
175 buf
->size
= ((char *)write
) - buf
->ptr
;
176 buf
->ptr
[buf
->size
] = '\0';
181 int git_buf_vprintf(git_buf
*buf
, const char *format
, va_list ap
)
184 const size_t expected_size
= buf
->size
+ (strlen(format
) * 2);
186 ENSURE_SIZE(buf
, expected_size
);
193 buf
->ptr
+ buf
->size
,
194 buf
->asize
- buf
->size
,
200 buf
->ptr
= git_buf__oom
;
204 if ((size_t)len
+ 1 <= buf
->asize
- buf
->size
) {
209 ENSURE_SIZE(buf
, buf
->size
+ len
+ 1);
215 int git_buf_printf(git_buf
*buf
, const char *format
, ...)
220 va_start(ap
, format
);
221 r
= git_buf_vprintf(buf
, format
, ap
);
227 void git_buf_copy_cstr(char *data
, size_t datasize
, const git_buf
*buf
)
231 assert(data
&& datasize
&& buf
);
235 if (buf
->size
== 0 || buf
->asize
<= 0)
239 if (copylen
> datasize
- 1)
240 copylen
= datasize
- 1;
241 memmove(data
, buf
->ptr
, copylen
);
242 data
[copylen
] = '\0';
245 void git_buf_consume(git_buf
*buf
, const char *end
)
247 if (end
> buf
->ptr
&& end
<= buf
->ptr
+ buf
->size
) {
248 size_t consumed
= end
- buf
->ptr
;
249 memmove(buf
->ptr
, end
, buf
->size
- consumed
);
250 buf
->size
-= consumed
;
251 buf
->ptr
[buf
->size
] = '\0';
255 void git_buf_truncate(git_buf
*buf
, size_t len
)
257 if (len
< buf
->size
) {
259 buf
->ptr
[buf
->size
] = '\0';
263 void git_buf_shorten(git_buf
*buf
, size_t amount
)
265 if (amount
> buf
->size
)
268 buf
->size
= buf
->size
- amount
;
269 buf
->ptr
[buf
->size
] = '\0';
272 void git_buf_rtruncate_at_char(git_buf
*buf
, char separator
)
274 ssize_t idx
= git_buf_rfind_next(buf
, separator
);
275 git_buf_truncate(buf
, idx
< 0 ? 0 : (size_t)idx
);
278 void git_buf_swap(git_buf
*buf_a
, git_buf
*buf_b
)
285 char *git_buf_detach(git_buf
*buf
)
287 char *data
= buf
->ptr
;
289 if (buf
->asize
== 0 || buf
->ptr
== git_buf__oom
)
292 git_buf_init(buf
, 0);
297 void git_buf_attach(git_buf
*buf
, char *ptr
, size_t asize
)
303 buf
->size
= strlen(ptr
);
305 buf
->asize
= (asize
< buf
->size
) ? buf
->size
+ 1 : asize
;
306 else /* pass 0 to fall back on strlen + 1 */
307 buf
->asize
= buf
->size
+ 1;
309 git_buf_grow(buf
, asize
);
313 int git_buf_join_n(git_buf
*buf
, char separator
, int nbuf
, ...)
317 size_t total_size
= 0, original_size
= buf
->size
;
318 char *out
, *original
= buf
->ptr
;
320 if (buf
->size
> 0 && buf
->ptr
[buf
->size
- 1] != separator
)
321 ++total_size
; /* space for initial separator */
323 /* Make two passes to avoid multiple reallocation */
326 for (i
= 0; i
< nbuf
; ++i
) {
330 segment
= va_arg(ap
, const char *);
334 segment_len
= strlen(segment
);
335 total_size
+= segment_len
;
336 if (segment_len
== 0 || segment
[segment_len
- 1] != separator
)
337 ++total_size
; /* space for separator */
341 /* expand buffer if needed */
344 if (git_buf_grow(buf
, buf
->size
+ total_size
+ 1) < 0)
347 out
= buf
->ptr
+ buf
->size
;
349 /* append separator to existing buf if needed */
350 if (buf
->size
> 0 && out
[-1] != separator
)
354 for (i
= 0; i
< nbuf
; ++i
) {
358 segment
= va_arg(ap
, const char *);
362 /* deal with join that references buffer's original content */
363 if (segment
>= original
&& segment
< original
+ original_size
) {
364 size_t offset
= (segment
- original
);
365 segment
= buf
->ptr
+ offset
;
366 segment_len
= original_size
- offset
;
368 segment_len
= strlen(segment
);
371 /* skip leading separators */
372 if (out
> buf
->ptr
&& out
[-1] == separator
)
373 while (segment_len
> 0 && *segment
== separator
) {
378 /* copy over next buffer */
379 if (segment_len
> 0) {
380 memmove(out
, segment
, segment_len
);
384 /* append trailing separator (except for last item) */
385 if (i
< nbuf
- 1 && out
> buf
->ptr
&& out
[-1] != separator
)
390 /* set size based on num characters actually written */
391 buf
->size
= out
- buf
->ptr
;
392 buf
->ptr
[buf
->size
] = '\0';
403 size_t strlen_a
= str_a
? strlen(str_a
) : 0;
404 size_t strlen_b
= strlen(str_b
);
406 ssize_t offset_a
= -1;
408 /* not safe to have str_b point internally to the buffer */
409 assert(str_b
< buf
->ptr
|| str_b
> buf
->ptr
+ buf
->size
);
411 /* figure out if we need to insert a separator */
412 if (separator
&& strlen_a
) {
413 while (*str_b
== separator
) { str_b
++; strlen_b
--; }
414 if (str_a
[strlen_a
- 1] != separator
)
418 /* str_a could be part of the buffer */
419 if (str_a
>= buf
->ptr
&& str_a
< buf
->ptr
+ buf
->size
)
420 offset_a
= str_a
- buf
->ptr
;
422 if (git_buf_grow(buf
, strlen_a
+ strlen_b
+ need_sep
+ 1) < 0)
425 /* fix up internal pointers */
427 str_a
= buf
->ptr
+ offset_a
;
429 /* do the actual copying */
431 memmove(buf
->ptr
, str_a
, strlen_a
);
433 buf
->ptr
[strlen_a
] = separator
;
434 memcpy(buf
->ptr
+ strlen_a
+ need_sep
, str_b
, strlen_b
);
436 buf
->size
= strlen_a
+ strlen_b
+ need_sep
;
437 buf
->ptr
[buf
->size
] = '\0';
442 void git_buf_rtrim(git_buf
*buf
)
444 while (buf
->size
> 0) {
445 if (!git__isspace(buf
->ptr
[buf
->size
- 1]))
451 buf
->ptr
[buf
->size
] = '\0';
454 int git_buf_cmp(const git_buf
*a
, const git_buf
*b
)
456 int result
= memcmp(a
->ptr
, b
->ptr
, min(a
->size
, b
->size
));
457 return (result
!= 0) ? result
:
458 (a
->size
< b
->size
) ? -1 : (a
->size
> b
->size
) ? 1 : 0;
469 where
<= git_buf_len(buf
) &&
470 where
+ nb_to_remove
<= git_buf_len(buf
));
472 /* Ported from git.git
473 * https://github.com/git/git/blob/16eed7c/strbuf.c#L159-176
475 if (git_buf_grow(buf
, git_buf_len(buf
) + nb_to_insert
- nb_to_remove
) < 0)
478 memmove(buf
->ptr
+ where
+ nb_to_insert
,
479 buf
->ptr
+ where
+ nb_to_remove
,
480 buf
->size
- where
- nb_to_remove
);
482 memcpy(buf
->ptr
+ where
, data
, nb_to_insert
);
484 buf
->size
= buf
->size
+ nb_to_insert
- nb_to_remove
;
485 buf
->ptr
[buf
->size
] = '\0';
493 void git_buffer_free(git_buffer
*buffer
)
498 if (buffer
->ptr
!= NULL
&& buffer
->available
> 0)
499 git__free(buffer
->ptr
);
501 git__memzero(buffer
, sizeof(*buffer
));
504 static int git_buffer__resize(
505 git_buffer
*buffer
, size_t want_size
, int preserve_data
)
507 int non_allocated_buffer
= 0;
512 /* check if buffer->ptr points to memory owned elsewhere */
513 non_allocated_buffer
= (buffer
->ptr
!= NULL
&& buffer
->available
== 0);
515 if (non_allocated_buffer
&& !want_size
)
516 want_size
= buffer
->size
;
518 if (buffer
->available
>= want_size
)
521 if (non_allocated_buffer
) {
523 if (want_size
< buffer
->size
)
524 want_size
= buffer
->size
;
526 new_ptr
= buffer
->ptr
;
529 want_size
= (want_size
+ 7) & ~7; /* round up to multiple of 8 */
531 new_ptr
= git__realloc(new_ptr
, want_size
);
532 GITERR_CHECK_ALLOC(new_ptr
);
534 if (non_allocated_buffer
&& preserve_data
)
535 memcpy(new_ptr
, buffer
->ptr
, buffer
->size
);
537 buffer
->ptr
= new_ptr
;
538 buffer
->available
= want_size
;
543 int git_buffer_resize(git_buffer
*buffer
, size_t want_size
)
545 return git_buffer__resize(buffer
, want_size
, true);
549 git_buffer
*buffer
, const void *data
, size_t datalen
)
551 if (git_buffer__resize(buffer
, datalen
+ 1, false) < 0)
553 memcpy(buffer
->ptr
, data
, datalen
);
554 buffer
->ptr
[datalen
] = '\0';
555 buffer
->size
= datalen
;