util/string: sprintf_append_realloc to concatenate strings with realloc
Appending string by using sprintf with realloc will be generally usable and add sprintf_append_realloc() and vsprintf_append_realloc() to the utility. These APIs follow realloc about buffer management, i.e., the original buffer is left untouched if they fail. Besides, the original buffer is NULL, they are equivalent to sprintf_alloc() and vsprintf_alloc(), respectively. Change-Id: I8b69d9640e86e1862ddd3917995bad6f59426b7e Signed-off-by: Shuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com> Signed-off-by: Chunyang Hui <chunyang.hui@intel.com> Signed-off-by: Pawel Wodkowski <pawelx.wodkowski@intel.com> Reviewed-on: https://review.gerrithub.io/c/436913 Tested-by: SPDK CI Jenkins <sys_sgci@intel.com> Reviewed-by: Darek Stojaczyk <dariusz.stojaczyk@intel.com> Reviewed-by: Ben Walker <benjamin.walker@intel.com> Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
This commit is contained in:
parent
bf6210b3c7
commit
8adbd90991
@ -69,6 +69,42 @@ char *spdk_sprintf_alloc(const char *format, ...) __attribute__((format(printf,
|
|||||||
*/
|
*/
|
||||||
char *spdk_vsprintf_alloc(const char *format, va_list args);
|
char *spdk_vsprintf_alloc(const char *format, va_list args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append string using vsprintf with automatic buffer re-allocation.
|
||||||
|
*
|
||||||
|
* The return value is the formatted string, in which the original string in
|
||||||
|
* buffer is unchanged and the specified formatted string is appended.
|
||||||
|
*
|
||||||
|
* The returned string should be passed to free() when no longer needed.
|
||||||
|
*
|
||||||
|
* If buffer is NULL, the call is equivalent to spdk_sprintf_alloc().
|
||||||
|
* If the call fails, the original buffer is left untouched.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer which has a formatted string.
|
||||||
|
* \param format Format for the string to print.
|
||||||
|
*
|
||||||
|
* \return the formatted string on success, or NULL on failure.
|
||||||
|
*/
|
||||||
|
char *spdk_sprintf_append_realloc(char *buffer, const char *format, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Append string using vsprintf with automatic buffer re-allocation.
|
||||||
|
* The return value is the formatted string, in which the original string in
|
||||||
|
* buffer is unchanged and the specified formatted string is appended.
|
||||||
|
*
|
||||||
|
* The returned string should be passed to free() when no longer needed.
|
||||||
|
*
|
||||||
|
* If buffer is NULL, the call is equivalent to spdk_sprintf_alloc().
|
||||||
|
* If the call fails, the original buffer is left untouched.
|
||||||
|
*
|
||||||
|
* \param buffer Buffer which has a formatted string.
|
||||||
|
* \param format Format for the string to print.
|
||||||
|
* \param args A value that identifies a variable arguments list.
|
||||||
|
*
|
||||||
|
* \return the formatted string on success, or NULL on failure.
|
||||||
|
*/
|
||||||
|
char *spdk_vsprintf_append_realloc(char *buffer, const char *format, va_list args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert string to lowercase in place.
|
* Convert string to lowercase in place.
|
||||||
*
|
*
|
||||||
|
@ -36,47 +36,54 @@
|
|||||||
#include "spdk/string.h"
|
#include "spdk/string.h"
|
||||||
|
|
||||||
char *
|
char *
|
||||||
spdk_vsprintf_alloc(const char *format, va_list args)
|
spdk_vsprintf_append_realloc(char *buffer, const char *format, va_list args)
|
||||||
{
|
{
|
||||||
va_list args_copy;
|
va_list args_copy;
|
||||||
char *buf;
|
char *new_buffer;
|
||||||
size_t bufsize;
|
int orig_size = 0, new_size;
|
||||||
int rc;
|
|
||||||
|
|
||||||
/* Try with a small buffer first. */
|
/* Original buffer size */
|
||||||
bufsize = 32;
|
if (buffer) {
|
||||||
|
orig_size = strlen(buffer);
|
||||||
/* Limit maximum buffer size to something reasonable so we don't loop forever. */
|
|
||||||
while (bufsize <= 1024 * 1024) {
|
|
||||||
buf = malloc(bufsize);
|
|
||||||
if (buf == NULL) {
|
|
||||||
return NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Necessary buffer size */
|
||||||
va_copy(args_copy, args);
|
va_copy(args_copy, args);
|
||||||
rc = vsnprintf(buf, bufsize, format, args_copy);
|
new_size = vsnprintf(NULL, 0, format, args_copy);
|
||||||
va_end(args_copy);
|
va_end(args_copy);
|
||||||
|
|
||||||
/*
|
if (new_size < 0) {
|
||||||
* If vsnprintf() returned a count within our current buffer size, we are done.
|
|
||||||
* The count does not include the \0 terminator, so rc == bufsize is not OK.
|
|
||||||
*/
|
|
||||||
if (rc >= 0 && (size_t)rc < bufsize) {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* vsnprintf() should return the required space, but some libc versions do not
|
|
||||||
* implement this correctly, so just double the buffer size and try again.
|
|
||||||
*
|
|
||||||
* We don't need the data in buf, so rather than realloc(), use free() and malloc()
|
|
||||||
* again to avoid a copy.
|
|
||||||
*/
|
|
||||||
free(buf);
|
|
||||||
bufsize *= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
new_size += orig_size + 1;
|
||||||
|
|
||||||
|
new_buffer = realloc(buffer, new_size);
|
||||||
|
if (new_buffer == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
vsnprintf(new_buffer + orig_size, new_size - orig_size, format, args);
|
||||||
|
|
||||||
|
return new_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
spdk_sprintf_append_realloc(char *buffer, const char *format, ...)
|
||||||
|
{
|
||||||
|
va_list args;
|
||||||
|
char *ret;
|
||||||
|
|
||||||
|
va_start(args, format);
|
||||||
|
ret = spdk_vsprintf_append_realloc(buffer, format, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
spdk_vsprintf_alloc(const char *format, va_list args)
|
||||||
|
{
|
||||||
|
return spdk_vsprintf_append_realloc(NULL, format, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
|
@ -202,6 +202,53 @@ test_parse_capacity(void)
|
|||||||
CU_ASSERT(rc != 0);
|
CU_ASSERT(rc != 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_sprintf_append_realloc(void)
|
||||||
|
{
|
||||||
|
char *str1, *str2, *str3, *str4;
|
||||||
|
|
||||||
|
/* Test basic functionality. */
|
||||||
|
str1 = spdk_sprintf_alloc("hello world\ngood morning\n" \
|
||||||
|
"good afternoon\ngood evening\n");
|
||||||
|
SPDK_CU_ASSERT_FATAL(str1 != NULL);
|
||||||
|
|
||||||
|
str2 = spdk_sprintf_append_realloc(NULL, "hello world\n");
|
||||||
|
SPDK_CU_ASSERT_FATAL(str2);
|
||||||
|
|
||||||
|
str2 = spdk_sprintf_append_realloc(str2, "good morning\n");
|
||||||
|
SPDK_CU_ASSERT_FATAL(str2);
|
||||||
|
|
||||||
|
str2 = spdk_sprintf_append_realloc(str2, "good afternoon\n");
|
||||||
|
SPDK_CU_ASSERT_FATAL(str2);
|
||||||
|
|
||||||
|
str2 = spdk_sprintf_append_realloc(str2, "good evening\n");
|
||||||
|
SPDK_CU_ASSERT_FATAL(str2);
|
||||||
|
|
||||||
|
CU_ASSERT(strcmp(str1, str2) == 0);
|
||||||
|
|
||||||
|
free(str1);
|
||||||
|
free(str2);
|
||||||
|
|
||||||
|
/* Test doubling buffer size. */
|
||||||
|
str3 = spdk_sprintf_append_realloc(NULL, "aaaaaaaaaa\n");
|
||||||
|
str3 = spdk_sprintf_append_realloc(str3, "bbbbbbbbbb\n");
|
||||||
|
str3 = spdk_sprintf_append_realloc(str3, "cccccccccc\n");
|
||||||
|
|
||||||
|
str4 = malloc(33 + 1);
|
||||||
|
memset(&str4[0], 'a', 10);
|
||||||
|
str4[10] = '\n';
|
||||||
|
memset(&str4[11], 'b', 10);
|
||||||
|
str4[21] = '\n';
|
||||||
|
memset(&str4[22], 'c', 10);
|
||||||
|
str4[32] = '\n';
|
||||||
|
str4[33] = 0;
|
||||||
|
|
||||||
|
CU_ASSERT(strcmp(str3, str4) == 0);
|
||||||
|
|
||||||
|
free(str3);
|
||||||
|
free(str4);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
@ -221,7 +268,8 @@ main(int argc, char **argv)
|
|||||||
if (
|
if (
|
||||||
CU_add_test(suite, "test_parse_ip_addr", test_parse_ip_addr) == NULL ||
|
CU_add_test(suite, "test_parse_ip_addr", test_parse_ip_addr) == NULL ||
|
||||||
CU_add_test(suite, "test_str_chomp", test_str_chomp) == NULL ||
|
CU_add_test(suite, "test_str_chomp", test_str_chomp) == NULL ||
|
||||||
CU_add_test(suite, "test_parse_capacity", test_parse_capacity) == NULL) {
|
CU_add_test(suite, "test_parse_capacity", test_parse_capacity) == NULL ||
|
||||||
|
CU_add_test(suite, "test_sprintf_append_realloc", test_sprintf_append_realloc) == NULL) {
|
||||||
CU_cleanup_registry();
|
CU_cleanup_registry();
|
||||||
return CU_get_error();
|
return CU_get_error();
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user