#include #include #include #include #include "buf.h" #include "notify.h" #include "test_suite.h" /* If these ever need to be overridden... */ void *(*buf_malloc_)(size_t) = malloc; void *(*buf_realloc_)(void *, size_t) = realloc; void (*buf_free_)(void *) = free; inline buf_t buf_new(size_t sz) { buf_t buf = buf_malloc_(sz + sizeof *buf); if (buf) { buf->buf_sz = sz; buf->buf_start = buf->buf_used = 0; memset(buf->buf, 0, sz); } return buf; } inline void buf_del(buf_t *pbuf) { if (pbuf) { if (*pbuf) { buf_free_(*pbuf); *pbuf = NULL; } } } inline void buf_rebase(buf_t buf) { if (buf->buf_start == 0) return; memmove(buf->buf, buf->buf + buf->buf_start, buf->buf_used); buf->buf_start = 0; } inline int buf_makeroom(buf_t *pbuf, size_t roomfor) { size_t new_sz; void *tmp_ptr; assert(pbuf != NULL); if (*pbuf == NULL) { *pbuf = buf_new(roomfor); if (*pbuf == NULL) { return -1; } } buf_rebase(*pbuf); if (BUF_ROOM(*pbuf) >= roomfor) return 0; new_sz = (*pbuf)->buf_used + roomfor; tmp_ptr = buf_realloc_(*pbuf, new_sz + sizeof **pbuf); if (tmp_ptr == NULL) { NOTIFY_ERROR("%s:%s", "realloc", strerror(errno)); return -1; } *pbuf = tmp_ptr; (*pbuf)->buf_sz = new_sz; return 0; } inline int buf_range_dup_or_append(buf_t src, size_t src_offset, size_t n, buf_t *pdst) { assert(src != NULL); assert(pdst != NULL); assert(src_offset + n <= src->buf_used); if (buf_makeroom(pdst, n)) { return -1; } memcpy((*pdst)->buf + (*pdst)->buf_used, src->buf + src->buf_start + src_offset, n); (*pdst)->buf_used += n; return 0; } /* Room for improvement: Depending on ratio of flensed portion to buffer use, try to do less copying-around of buffer ranges, by swapping buf pointers instead. */ ssize_t buf_flense(buf_t *psrc, size_t src_offset, int delimiter, buf_t *pdst) { const size_t delimiter_len = 1; size_t i; assert(psrc != NULL); assert(src_offset <= (*psrc)->buf_used); NOTIFY_DEBUG("src_offset:%zu", src_offset); D_BUF("src ", *psrc); D_BUF("dst ", pdst ? *pdst : NULL); for (i = src_offset; i < (*psrc)->buf_used; i++) { if ((*psrc)->buf[(*psrc)->buf_start + i] == delimiter) { if (pdst != NULL) { if (buf_range_dup_or_append((*psrc), 0, i, pdst)) { return -1; } } (*psrc)->buf_start += i + delimiter_len; (*psrc)->buf_used -= i + delimiter_len; D_BUF("src ", *psrc); D_BUF("dst ", pdst ? *pdst : NULL); return i + delimiter_len; } } return 0; } #ifdef TEST static const char buf_flense_testdata1_[] = "a\nbc\ndef\nghij\nklmno\npqr\ns\ntuv\nwxyz0\n1234\n567\n89\n0leftovers"; static const char buf_flense_testdata2_[] = "\n\nfoo\nbar\n\n"; struct buf_flense_expected_result_ { ssize_t r; const char *buf; } buf_flense_expected1[] = { { 1 + 1, "a" }, { 2 + 1, "bc" }, { 3 + 1, "def" }, { 4 + 1, "ghij" }, { 5 + 1, "klmno" }, { 3 + 1, "pqr" }, { 1 + 1, "s" }, { 3 + 1, "tuv" }, { 5 + 1, "wxyz0" }, { 4 + 1, "1234" }, { 3 + 1, "567" }, { 2 + 1, "89" }, { 0, "0leftovers" }, }, buf_flense_expected2[] = { { 0 + 1, "" }, { 0 + 1, "" }, { 3 + 1, "foo" }, { 3 + 1, "bar" }, { 0 + 1, "" }, { 0, "" }, }; struct test_buf_flense_data_ { const char *src; const struct buf_flense_expected_result_ *expected; } test_buf_flense_data1 = { .src = buf_flense_testdata1_, .expected = buf_flense_expected1, }, test_buf_flense_data2 = { .src = buf_flense_testdata2_, .expected = buf_flense_expected2, }; static int test_buf_flense_(void *test_arg, void *suite_arg) { (void)suite_arg; struct test_buf_flense_data_ *test_data = (struct test_buf_flense_data_ *)test_arg; const char testdata_len = strlen(test_data->src); const struct buf_flense_expected_result_ *next_result = test_data->expected; int retval = 0; buf_t src; TEST_INFO("initializing src buffer with %zu characters", testdata_len); src = buf_new(testdata_len); if (src == NULL) { TEST_ERROR("%s:%s", "new_buf_", "failed"); return -1; } memcpy(src->buf, test_data->src, testdata_len); src->buf_used += testdata_len; D_BUF("src ", src); for (;;) { ssize_t r; buf_t dst = NULL; r = buf_flense(&src, 0, '\n', &dst); if (r != next_result->r) { TEST_ERROR("result '%zd' does not match expected result '%zd'", r, next_result->r); retval = -1; } if (r == 0) { TEST_INFO("finished flensing"); break; } if (strlen(next_result->buf) > dst->buf_used) { TEST_ERROR("flensed buffer smaller than expected result '%s'", next_result->buf); D_BUF("dst ", dst); retval = -1; } else if (memcmp(next_result->buf, dst->buf + dst->buf_start, strlen(next_result->buf))) { TEST_ERROR("flensed buffer does not match expected result '%s'", next_result->buf); D_BUF("dst ", dst); retval = -1; } TEST_INFO("flensed: '%.*s'", (int)dst->buf_used, dst->buf + dst->buf_start); memset(dst, 0, dst->buf_sz + sizeof *dst); buf_del(&dst); next_result++; } if (strlen(next_result->buf) > src->buf_used) { TEST_ERROR("remaining buffer smaller than expected result '%s'", next_result->buf); D_BUF("src ", src); retval = -1; } else if (memcmp(next_result->buf, src->buf + src->buf_start, strlen(next_result->buf))) { TEST_ERROR("remaining buffer does not match expected result '%s'", next_result->buf); D_BUF("src ", src); retval = -1; } TEST_INFO("remains: '%.*s'", (int)src->buf_used, src->buf + src->buf_start); memset(src, 0, src->buf_sz + sizeof *src); buf_del(&src); return retval; } void *test_suite_data; int test_suite_pre(void *suite_data) { (void)suite_data; return 0; } int test_suite_post(void *suite_data) { (void)suite_data; return 0; } test_t test_suite[] = { { "test_buf_flense_ 1", test_buf_flense_, NULL, NULL, &test_buf_flense_data1 }, { "test_buf_flense_ 2", test_buf_flense_, NULL, NULL, &test_buf_flense_data2 }, { NULL, NULL, NULL, NULL, NULL }, }; #endif /* TEST */