Commit 8adbd909 authored by Shuhei Matsumoto's avatar Shuhei Matsumoto Committed by Jim Harris
Browse files

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: default avatarShuhei Matsumoto <shuhei.matsumoto.xt@hitachi.com>
Signed-off-by: default avatarChunyang Hui <chunyang.hui@intel.com>
Signed-off-by: default avatarPawel Wodkowski <pawelx.wodkowski@intel.com>
Reviewed-on: https://review.gerrithub.io/c/436913


Tested-by: default avatarSPDK CI Jenkins <sys_sgci@intel.com>
Reviewed-by: default avatarDarek Stojaczyk <dariusz.stojaczyk@intel.com>
Reviewed-by: default avatarBen Walker <benjamin.walker@intel.com>
Chandler-Test-Pool: SPDK Automated Test System <sys_sgsw@intel.com>
parent bf6210b3
Loading
Loading
Loading
Loading
+36 −0
Original line number Diff line number Diff line
@@ -69,6 +69,42 @@ char *spdk_sprintf_alloc(const char *format, ...) __attribute__((format(printf,
 */
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.
 *
+40 −33
Original line number Diff line number Diff line
@@ -36,47 +36,54 @@
#include "spdk/string.h"

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;
	char *buf;
	size_t bufsize;
	int rc;

	/* Try with a small buffer first. */
	bufsize = 32;
	char *new_buffer;
	int orig_size = 0, new_size;

	/* 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;
	/* Original buffer size */
	if (buffer) {
		orig_size = strlen(buffer);
	}

	/* Necessary buffer size */
	va_copy(args_copy, args);
		rc = vsnprintf(buf, bufsize, format, args_copy);
	new_size = vsnprintf(NULL, 0, format, args_copy);
	va_end(args_copy);

		/*
		 * 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;
	if (new_size < 0) {
		return NULL;
	}
	new_size += orig_size + 1;

		/*
		 * 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;
	new_buffer = realloc(buffer, new_size);
	if (new_buffer == NULL) {
		return 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 *
+49 −1
Original line number Diff line number Diff line
@@ -202,6 +202,53 @@ test_parse_capacity(void)
	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
main(int argc, char **argv)
{
@@ -221,7 +268,8 @@ main(int argc, char **argv)
	if (
		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_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();
		return CU_get_error();
	}