Code

utils strbuf: Added memcpy and memappend functions.
authorSebastian Harl <sh@tokkee.org>
Tue, 29 Oct 2013 08:47:57 +0000 (09:47 +0100)
committerSebastian 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
src/utils/strbuf.c
t/utils/strbuf_test.c

index 302cfdde4bb71839b94d86b47c57fe194b9647f2..6ad9e1e481fa84d7e1c0dff4f26b571159859118 100644 (file)
@@ -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
index 7c24b3a8a75733382274d3f34f4ea46a46283f80..993b086d0ef0adbcb060faf046cd7390f548e4bd 100644 (file)
@@ -32,6 +32,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <stdarg.h>
+#include <string.h>
 
 /*
  * 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)
 {
index f007cbe20425bf1562522a4e2e64fbc1dba7258e..ad04b87e66a0f617812b7be96638f9f0155ebd9f 100644 (file)
@@ -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);