/** * Copyright (C) Mellanox Technologies Ltd. 2019. ALL RIGHTS RESERVED. * * See file LICENSE for terms. */ #include "string_buffer.h" #include #include #include #include #include #include #define UCS_STRING_BUFFER_INITIAL_CAPACITY 32 #define UCS_STRING_BUFFER_ALLOC_NAME "string_buffer" static void ucs_string_buffer_reset(ucs_string_buffer_t *strb) { strb->buffer = NULL; strb->length = 0; strb->capacity = 0; } void ucs_string_buffer_init(ucs_string_buffer_t *strb) { ucs_string_buffer_reset(strb); } void ucs_string_buffer_cleanup(ucs_string_buffer_t *strb) { ucs_free(strb->buffer); ucs_string_buffer_reset(strb); } static ucs_status_t ucs_string_buffer_grow(ucs_string_buffer_t *strb, size_t new_capacity) { char *new_buffer; ucs_assert(new_capacity > strb->capacity); new_buffer = ucs_realloc(strb->buffer, new_capacity, UCS_STRING_BUFFER_ALLOC_NAME); if (new_buffer == NULL) { ucs_error("failed to grow string from %zu to %zu characters", strb->capacity, new_capacity); return UCS_ERR_NO_MEMORY; } strb->buffer = new_buffer; strb->capacity = new_capacity; /* length stays the same */ return UCS_OK; } ucs_status_t ucs_string_buffer_appendf(ucs_string_buffer_t *strb, const char *fmt, ...) { ucs_status_t status; size_t max_print; va_list ap; int ret; /* set minimal initial size */ if (strb->capacity - strb->length <= 1) { status = ucs_string_buffer_grow(strb, UCS_STRING_BUFFER_INITIAL_CAPACITY); if (status != UCS_OK) { return status; } } /* try to write to existing buffer */ va_start(ap, fmt); max_print = strb->capacity - strb->length - 1; ret = vsnprintf(strb->buffer + strb->length, max_print, fmt, ap); va_end(ap); /* if failed, grow the buffer to at least the required size and at least * double the previous size (to reduce the amortized cost of realloc) */ if (ret >= max_print) { status = ucs_string_buffer_grow(strb, ucs_max(strb->capacity * 2, strb->length + ret + 1)); if (status != UCS_OK) { return status; } va_start(ap, fmt); max_print = strb->capacity - strb->length - 1; ret = vsnprintf(strb->buffer + strb->length, strb->capacity - 1, fmt, ap); va_end(ap); /* since we've grown the buffer, it should be sufficient now */ ucs_assert(ret < max_print); } /* string length grows by the amount of characters written by vsnprintf */ strb->length += ret; ucs_assert(strb->length < strb->capacity); ucs_assert(strb->buffer[strb->length] == '\0'); /* \0 is written by vsnprintf */ return UCS_OK; } void ucs_string_buffer_rtrim(ucs_string_buffer_t *strb, const char *charset) { char *ptr; ptr = &strb->buffer[strb->length]; while (strb->length > 0) { --ptr; if (((charset == NULL) && !isspace(*ptr)) || ((charset != NULL) && (strchr(charset, *ptr) == NULL))) { /* if the last character should NOT be removed - stop */ break; } --strb->length; } /* mark the new end of string */ *(ptr + 1) = '\0'; } const char *ucs_string_buffer_cstr(const ucs_string_buffer_t *strb) { if (strb->length == 0) { return ""; } ucs_assert(strb->buffer != NULL); ucs_assert(strb->capacity > 0); return strb->buffer; }