summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 40bc8e8)
raw | patch | inline | side by side (parent: 40bc8e8)
author | Sebastian Harl <sh@tokkee.org> | |
Tue, 29 Oct 2013 08:47:57 +0000 (09:47 +0100) | ||
committer | Sebastian Harl <sh@tokkee.org> | |
Tue, 29 Oct 2013 08:47:57 +0000 (09:47 +0100) |
These functions may be used to write arbitrary data to a string buffer.
src/include/utils/strbuf.h | patch | blob | history | |
src/utils/strbuf.c | patch | blob | history | |
t/utils/strbuf_test.c | patch | blob | history |
index 302cfdde4bb71839b94d86b47c57fe194b9647f2..6ad9e1e481fa84d7e1c0dff4f26b571159859118 100644 (file)
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 7c24b3a8a75733382274d3f34f4ea46a46283f80..993b086d0ef0adbcb060faf046cd7390f548e4bd 100644 (file)
--- a/src/utils/strbuf.c
+++ b/src/utils/strbuf.c
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <string.h>
/*
* private data structures
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 f007cbe20425bf1562522a4e2e64fbc1dba7258e..ad04b87e66a0f617812b7be96638f9f0155ebd9f 100644 (file)
--- a/t/utils/strbuf_test.c
+++ b/t/utils/strbuf_test.c
}
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;
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);