/* * Copyright (C) the libgit2 contributors. All rights reserved. * * This file is part of libgit2, distributed under the GNU GPL v2 with * a Linking Exception. For full terms see the included COPYING file. */ #include #include #include #include #include #include #include #include #include "buffer.h" /* Used as default value for gh_buf->ptr so that people can always * assume ptr is non-NULL and zero terminated even for new gh_bufs. */ char gh_buf__initbuf[1]; char gh_buf__oom[1]; #define ENSURE_SIZE(b, d) \ if ((d) > buf->asize && gh_buf_grow(b, (d)) < 0)\ return -1; void gh_buf_init(gh_buf *buf, size_t initial_size) { buf->asize = 0; buf->size = 0; buf->ptr = gh_buf__initbuf; if (initial_size) gh_buf_grow(buf, initial_size); } int gh_buf_try_grow(gh_buf *buf, size_t target_size, bool mark_oom) { char *new_ptr; size_t new_size; if (buf->ptr == gh_buf__oom) return -1; if (target_size <= buf->asize) return 0; if (buf->asize == 0) { new_size = target_size; new_ptr = NULL; } else { new_size = buf->asize; new_ptr = buf->ptr; } /* grow the buffer size by 1.5, until it's big enough * to fit our target size */ while (new_size < target_size) new_size = (new_size << 1) - (new_size >> 1); /* round allocation up to multiple of 8 */ new_size = (new_size + 7) & ~7; new_ptr = realloc(new_ptr, new_size); if (!new_ptr) { if (mark_oom) buf->ptr = gh_buf__oom; return -1; } buf->asize = new_size; buf->ptr = new_ptr; /* truncate the existing buffer size if necessary */ if (buf->size >= buf->asize) buf->size = buf->asize - 1; buf->ptr[buf->size] = '\0'; return 0; } void gh_buf_free(gh_buf *buf) { if (!buf) return; if (buf->ptr != gh_buf__initbuf && buf->ptr != gh_buf__oom) free(buf->ptr); gh_buf_init(buf, 0); } void gh_buf_clear(gh_buf *buf) { buf->size = 0; if (buf->asize > 0) buf->ptr[0] = '\0'; } int gh_buf_set(gh_buf *buf, const char *data, size_t len) { if (len == 0 || data == NULL) { gh_buf_clear(buf); } else { if (data != buf->ptr) { ENSURE_SIZE(buf, len + 1); memmove(buf->ptr, data, len); } buf->size = len; buf->ptr[buf->size] = '\0'; } return 0; } int gh_buf_sets(gh_buf *buf, const char *string) { return gh_buf_set(buf, string, string ? strlen(string) : 0); } int gh_buf_putc(gh_buf *buf, char c) { ENSURE_SIZE(buf, buf->size + 2); buf->ptr[buf->size++] = c; buf->ptr[buf->size] = '\0'; return 0; } int gh_buf_put(gh_buf *buf, const void *data, size_t len) { ENSURE_SIZE(buf, buf->size + len + 1); memmove(buf->ptr + buf->size, data, len); buf->size += len; buf->ptr[buf->size] = '\0'; return 0; } int gh_buf_puts(gh_buf *buf, const char *string) { assert(string); return gh_buf_put(buf, string, strlen(string)); } int gh_buf_vprintf(gh_buf *buf, const char *format, va_list ap) { int len; const size_t expected_size = buf->size + (strlen(format) * 2); ENSURE_SIZE(buf, expected_size); while (1) { va_list args; va_copy(args, ap); len = vsnprintf( buf->ptr + buf->size, buf->asize - buf->size, format, args ); if (len < 0) { free(buf->ptr); buf->ptr = gh_buf__oom; return -1; } if ((size_t)len + 1 <= buf->asize - buf->size) { buf->size += len; break; } ENSURE_SIZE(buf, buf->size + len + 1); } return 0; } int gh_buf_printf(gh_buf *buf, const char *format, ...) { int r; va_list ap; va_start(ap, format); r = gh_buf_vprintf(buf, format, ap); va_end(ap); return r; } void gh_buf_copy_cstr(char *data, size_t datasize, const gh_buf *buf) { size_t copylen; assert(data && datasize && buf); data[0] = '\0'; if (buf->size == 0 || buf->asize <= 0) return; copylen = buf->size; if (copylen > datasize - 1) copylen = datasize - 1; memmove(data, buf->ptr, copylen); data[copylen] = '\0'; } void gh_buf_swap(gh_buf *buf_a, gh_buf *buf_b) { gh_buf t = *buf_a; *buf_a = *buf_b; *buf_b = t; } char *gh_buf_detach(gh_buf *buf) { char *data = buf->ptr; if (buf->asize == 0 || buf->ptr == gh_buf__oom) return NULL; gh_buf_init(buf, 0); return data; } void gh_buf_attach(gh_buf *buf, char *ptr, size_t asize) { gh_buf_free(buf); if (ptr) { buf->ptr = ptr; buf->size = strlen(ptr); if (asize) buf->asize = (asize < buf->size) ? buf->size + 1 : asize; else /* pass 0 to fall back on strlen + 1 */ buf->asize = buf->size + 1; } else { gh_buf_grow(buf, asize); } } int gh_buf_cmp(const gh_buf *a, const gh_buf *b) { int result = memcmp(a->ptr, b->ptr, a->size < b->size ? a->size : b->size); return (result != 0) ? result : (a->size < b->size) ? -1 : (a->size > b->size) ? 1 : 0; }