/* * upb - a minimalist implementation of protocol buffers. * * Copyright (c) 2010 Joshua Haberman. See LICENSE for details. */ #include "upb_string.h" #include #ifdef __GLIBC__ #include #elif defined(__APPLE__) #include #endif static uint32_t upb_round_up_pow2(uint32_t v) { // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } upb_string *upb_string_new() { upb_string *str = malloc(sizeof(*str)); str->ptr = NULL; str->cached_mem = NULL; #ifndef UPB_HAVE_MSIZE str->size = 0; #endif str->src = NULL; upb_atomic_refcount_init(&str->refcount, 1); return str; } uint32_t upb_string_size(upb_string *str) { #ifdef __GLIBC__ return malloc_usable_size(str->cached_mem); #elif defined(__APPLE__) return malloc_size(str->cached_mem); #else return str->size; #endif } static void upb_string_release(upb_string *str) { if(str->src) { upb_string_unref(str->src); str->src = NULL; } } void _upb_string_free(upb_string *str) { if(str->cached_mem) free(str->cached_mem); upb_string_release(str); free(str); } upb_string *upb_string_tryrecycle(upb_string *str) { if(str == NULL || upb_atomic_read(&str->refcount) > 1) { return upb_string_new(); } else { str->ptr = NULL; upb_string_release(str); return str; } } char *upb_string_getrwbuf(upb_string *str, upb_strlen_t len) { assert(str->ptr == NULL); uint32_t size = upb_string_size(str); if (size < len) { size = upb_round_up_pow2(len); str->cached_mem = realloc(str->cached_mem, size); #ifndef UPB_HAVE_MSIZE str->size = size; #endif } str->len = len; str->ptr = str->cached_mem; return str->cached_mem; } void upb_string_substr(upb_string *str, upb_string *target_str, upb_strlen_t start, upb_strlen_t len) { assert(str->ptr == NULL); str->src = upb_string_getref(target_str); str->ptr = upb_string_getrobuf(target_str) + start; str->len = len; } void upb_string_vprintf(upb_string *str, const char *format, va_list args) { // Try once without reallocating. We have to va_copy because we might have // to call vsnprintf again. uint32_t size = UPB_MAX(upb_string_size(str), 16); char *buf = upb_string_getrwbuf(str, size); va_list args_copy; va_copy(args_copy, args); uint32_t true_size = vsnprintf(buf, size, format, args_copy); va_end(args_copy); if (true_size > size) { // Need to reallocate. str = upb_string_tryrecycle(str); buf = upb_string_getrwbuf(str, true_size); vsnprintf(buf, true_size, format, args); } str->len = true_size; } upb_string *upb_string_asprintf(const char *format, ...) { upb_string *str = upb_string_new(); va_list args; va_start(args, format); upb_string_vprintf(str, format, args); va_end(args); return str; }