@@ -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;
}
@@ -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),
};
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(-)