diff mbox series

[02/41] libmpathutil: avoid extra memory allocation in print_strbuf()

Message ID 20240808152620.93965-3-mwilck@suse.com (mailing list archive)
State Not Applicable, archived
Delegated to: christophe varoqui
Headers show
Series multipath-tools: comply with C library reserved names | expand

Commit Message

Martin Wilck Aug. 8, 2024, 3:25 p.m. UTC
Instead of allocating a temporary buffer with vasprintf() and copying its
contents to the strbuf, try to print directly into the strbuf and use the
return value to expand the buffer if necessary. This saves a memory
allocation on every invocation of print_strbuf(), and thus reduces the
likelihood of failure and at the same time increases efficiency. We need
an extra call to vsnprintf() if the buffer must be expanded, but vasprintf()
will need to re-process the input internally as well after determining the
required buffer size, therefore this probably won't hurt much.

Add some more tests for print_strbuf() to make sure this works as intended.

Signed-off-by: Martin Wilck <mwilck@suse.com>
---
 libmpathutil/strbuf.c | 23 +++++++++++----
 tests/strbuf.c        | 68 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 86 insertions(+), 5 deletions(-)
diff mbox series

Patch

diff --git a/libmpathutil/strbuf.c b/libmpathutil/strbuf.c
index e23b65e..6e53c3f 100644
--- a/libmpathutil/strbuf.c
+++ b/libmpathutil/strbuf.c
@@ -196,17 +196,30 @@  int print_strbuf(struct strbuf *buf, const char *fmt, ...)
 {
 	va_list ap;
 	int ret;
-	char *tail;
+	size_t space = buf->size - buf->offs;
 
 	va_start(ap, fmt);
-	ret = vasprintf(&tail, fmt, ap);
+	ret = vsnprintf(buf->buf + buf->offs, space, fmt, ap);
 	va_end(ap);
 
 	if (ret < 0)
-		return -ENOMEM;
+		return ret;
+	else if ((size_t)ret < space) {
+		buf->offs += ret;
+		return ret;
+	}
 
-	ret = __append_strbuf_str(buf, tail, ret);
+	ret = expand_strbuf(buf, ret);
+	if (ret < 0)
+		return ret;
+
+	space = buf->size - buf->offs;
+	va_start(ap, fmt);
+	ret = vsnprintf(buf->buf + buf->offs, space, fmt, ap);
+	va_end(ap);
+
+	if (ret >= 0)
+		buf->offs += ret;
 
-	free(tail);
 	return ret;
 }
diff --git a/tests/strbuf.c b/tests/strbuf.c
index f8554da..d7d4cd9 100644
--- a/tests/strbuf.c
+++ b/tests/strbuf.c
@@ -279,6 +279,71 @@  static void test_print_strbuf(void **state)
 	assert_int_equal(print_strbuf(&buf, "%d%% of %d is %0.2f",
 				      5, 100, 0.05), 17);
 	assert_string_equal(get_strbuf_str(&buf), "5% of 100 is 0.05");
+
+}
+
+/* length of string is not a divisor of chunk size */
+static void test_print_strbuf_2(void **state)
+{
+	STRBUF_ON_STACK(buf);
+	const char sentence[] = "This sentence has forty-seven (47) characters. ";
+	const char *s;
+	const int repeat = 100;
+	int i;
+
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(print_strbuf(&buf, "%s", sentence),
+				 sizeof(sentence) - 1);
+
+	s = get_strbuf_str(&buf);
+	condlog(3, "%s", s);
+	assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1));
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1),
+					 sentence, sizeof(sentence) - 1), 0);
+}
+
+/* length of string is divisor of chunk size */
+static void test_print_strbuf_3(void **state)
+{
+	STRBUF_ON_STACK(buf);
+	const char sentence[] = "This sentence has 32 characters.";
+	const char *s;
+	const int repeat = 100;
+	int i;
+
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(print_strbuf(&buf, "%s", sentence),
+				 sizeof(sentence) - 1);
+
+	s = get_strbuf_str(&buf);
+	condlog(3, "%s", s);
+	assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1));
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1),
+					 sentence, sizeof(sentence) - 1), 0);
+}
+
+static void test_print_strbuf_4(void **state)
+{
+	STRBUF_ON_STACK(buf);
+	const char sentence[] = "This sentence has a lot of characters, "
+		"which makes it hopefully longer than the chunk size given by "
+		"the constant \"BUF_CHUNK\" in libmpathutil/strbuf.c. ";
+	const char *s;
+	const int repeat = 100;
+	int i;
+
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(print_strbuf(&buf, "%s", sentence),
+				 sizeof(sentence) - 1);
+
+	s = get_strbuf_str(&buf);
+	condlog(3, "%s", s);
+	assert_int_equal(strlen(s), repeat * (sizeof(sentence) - 1));
+	for (i = 0; i < repeat; i++)
+		assert_int_equal(strncmp(s + i * (sizeof(sentence) - 1),
+					 sentence, sizeof(sentence) - 1), 0);
 }
 
 static void test_truncate_strbuf(void **state)
@@ -394,6 +459,9 @@  static int test_strbuf(void)
 		cmocka_unit_test(test_strbuf_quoted),
 		cmocka_unit_test(test_strbuf_escaped),
 		cmocka_unit_test(test_print_strbuf),
+		cmocka_unit_test(test_print_strbuf_2),
+		cmocka_unit_test(test_print_strbuf_3),
+		cmocka_unit_test(test_print_strbuf_4),
 		cmocka_unit_test(test_truncate_strbuf),
 		cmocka_unit_test(test_fill_strbuf),
 	};