From db10803daef8bf91971171c86f22abebcd74d4eb Mon Sep 17 00:00:00 2001 From: Sebastian Harl Date: Tue, 29 Oct 2013 09:47:57 +0100 Subject: [PATCH] utils strbuf: Added memcpy and memappend functions. These functions may be used to write arbitrary data to a string buffer. --- src/include/utils/strbuf.h | 18 ++++++++ src/utils/strbuf.c | 45 +++++++++++++++++++ t/utils/strbuf_test.c | 91 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+) diff --git a/src/include/utils/strbuf.h b/src/include/utils/strbuf.h index 302cfdd..6ad9e1e 100644 --- a/src/include/utils/strbuf.h +++ b/src/include/utils/strbuf.h @@ -93,6 +93,24 @@ sdb_strbuf_vsprintf(sdb_strbuf_t *strbuf, const char *fmt, va_list ap); ssize_t sdb_strbuf_sprintf(sdb_strbuf_t *strbuf, const char *fmt, ...); +/* + * sdb_strbuf_memcpy, sdb_strbuf_memappend: + * Copy or a append a memory area to the buffer. These functions do not + * interpret any information in the data pointer (including \0 bytes). + * + * These functions may be used to handle arbitrary byte arrays. Mixing these + * function calls with any of the printf-style function works but will usually + * lead to arbitrary behavior. + * + * Returns: + * - the number of bytes written + * - a negative value on error + */ +ssize_t +sdb_strbuf_memcpy(sdb_strbuf_t *strbuf, const void *data, size_t n); +ssize_t +sdb_strbuf_memappend(sdb_strbuf_t *strbuf, const void *data, size_t n); + /* * sdb_strbuf_chomp: * Remove all consecutive newline characters from the end of the string buffer diff --git a/src/utils/strbuf.c b/src/utils/strbuf.c index 7c24b3a..993b086 100644 --- a/src/utils/strbuf.c +++ b/src/utils/strbuf.c @@ -32,6 +32,7 @@ #include #include #include +#include /* * private data structures @@ -188,6 +189,50 @@ sdb_strbuf_sprintf(sdb_strbuf_t *strbuf, const char *fmt, ...) return status; } /* sdb_strbuf_sprintf */ +ssize_t +sdb_strbuf_memappend(sdb_strbuf_t *strbuf, const void *data, size_t n) +{ + if ((! strbuf) || (! data)) + return -1; + + assert((strbuf->size == 0) || (strbuf->string[strbuf->pos] == '\0')); + + if (strbuf->pos + n + 1 >= strbuf->size) { + size_t newsize = strbuf->size * 2; + + if (! newsize) + newsize = 64; + while (strbuf->pos + n + 1 >= newsize) + newsize *= 2; + + if (strbuf_resize(strbuf, newsize)) + return -1; + } + + assert(strbuf->size && strbuf->string); + assert(strbuf->pos < strbuf->size); + + memcpy((void *)(strbuf->string + strbuf->pos), data, n); + strbuf->pos += n; + strbuf->string[strbuf->pos] = '\0'; + + return (ssize_t)n; +} /* sdb_strbuf_memappend */ + +ssize_t +sdb_strbuf_memcpy(sdb_strbuf_t *strbuf, const void *data, size_t n) +{ + if ((! strbuf) || (! data)) + return -1; + + if (strbuf->size) { + strbuf->string[0] = '\0'; + strbuf->pos = 0; + } + + return sdb_strbuf_memappend(strbuf, data, n); +} /* sdb_strbuf_memcpy */ + ssize_t sdb_strbuf_chomp(sdb_strbuf_t *strbuf) { diff --git a/t/utils/strbuf_test.c b/t/utils/strbuf_test.c index f007cbe..ad04b87 100644 --- a/t/utils/strbuf_test.c +++ b/t/utils/strbuf_test.c @@ -177,6 +177,95 @@ START_TEST(test_sdb_strbuf_sprintf) } END_TEST +static struct { + const char *input; + size_t size; +} mem_golden_data[] = { + { "abc\0\x10\x42", 6 }, + { "\0\1\2\3\4", 5 }, + { "\n\n\0\n\n", 5 }, + { "", 0 }, +}; + +START_TEST(test_sdb_strbuf_memcpy) +{ + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) { + ssize_t n; + const char *check; + + n = sdb_strbuf_memcpy(buf, mem_golden_data[i].input, + mem_golden_data[i].size); + fail_unless(n >= 0, + "sdb_strbuf_memcpy() = %zi; expected: >=0", n); + fail_unless((size_t)n == mem_golden_data[i].size, + "sdb_strbuf_memcpy() = %zi; expected: %zu", + n, mem_golden_data[i].size); + + n = (ssize_t)sdb_strbuf_len(buf); + fail_unless((size_t)n == mem_golden_data[i].size, + "sdb_strbuf_len() = %zu (after memcpy); expected: %zu", + n, mem_golden_data[i].size); + + check = sdb_strbuf_string(buf); + fail_unless(check != NULL, + "sdb_strbuf_string() = NULL (after memcpy); expected: data"); + fail_unless(check[mem_golden_data[i].size] == '\0', + "sdb_strbuf_memcpy() did not nil-terminate the data"); + fail_unless(!memcmp(check, mem_golden_data[i].input, + mem_golden_data[i].size), + "sdb_strbuf_memcpy() did not set the buffer correctly"); + } +} +END_TEST + +START_TEST(test_sdb_strbuf_memappend) +{ + size_t i; + + for (i = 0; i < SDB_STATIC_ARRAY_LEN(mem_golden_data); ++i) { + ssize_t n; + const char *check; + + size_t total, j; + + n = sdb_strbuf_memappend(buf, mem_golden_data[i].input, + mem_golden_data[i].size); + fail_unless(n >= 0, + "sdb_strbuf_memappend() = %zi; expected: >=0", n); + fail_unless((size_t)n == mem_golden_data[i].size, + "sdb_strbuf_memappend() = %zi; expected: %zu", + n, mem_golden_data[i].size); + + check = sdb_strbuf_string(buf); + fail_unless(check != NULL, + "sdb_strbuf_string() = NULL (after memappend); " + "expected: data"); + + n = (ssize_t)sdb_strbuf_len(buf); + total = 0; + for (j = 0; j <= i; ++j) { + fail_unless(total + mem_golden_data[j].size <= (size_t)n, + "sdb_strbuf_len() = %zu (after memappend); " + "expected: >=%zu", n, total + mem_golden_data[j].size); + + fail_unless(!memcmp(check + total, mem_golden_data[j].input, + mem_golden_data[j].size), + "sdb_strbuf_memappend() did not " + "set the buffer correctly"); + total += mem_golden_data[j].size; + } + fail_unless((size_t)n == total, + "sdb_strbuf_len() = %zu (after memappend); expected: %zu", + n, total); + + fail_unless(check[total] == '\0', + "sdb_strbuf_memappend() did not nil-terminate the data"); + } +} +END_TEST + static struct { const char *input; ssize_t expected; @@ -278,6 +367,8 @@ util_strbuf_suite(void) tcase_add_test(tc, test_sdb_strbuf_create); tcase_add_test(tc, test_sdb_strbuf_append); tcase_add_test(tc, test_sdb_strbuf_sprintf); + tcase_add_test(tc, test_sdb_strbuf_memcpy); + tcase_add_test(tc, test_sdb_strbuf_memappend); tcase_add_test(tc, test_sdb_strbuf_chomp); tcase_add_test(tc, test_sdb_strbuf_string); tcase_add_test(tc, test_sdb_strbuf_len); -- 2.30.2