X-Git-Url: http://git.squeep.com/?p=reservoir_sample;a=blobdiff_plain;f=buf.c;fp=buf.c;h=7a94079d0e965ebcd3b174e032c18064d7c51eca;hp=0000000000000000000000000000000000000000;hb=c0224807bccbd7e71e312fb4151378bff4f5a5db;hpb=4fccfc8fc60e56a5c09648e3914250f0312a0b5b diff --git a/buf.c b/buf.c new file mode 100644 index 0000000..7a94079 --- /dev/null +++ b/buf.c @@ -0,0 +1,243 @@ +#include +#include +#include +#include + +#include "buf.h" +#include "notify.h" +#include "test_suite.h" + +inline +buf_t buf_new(size_t sz) { + buf_t 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_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 = 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); + free(dst); + dst = NULL; + + 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); + free(src); + src = NULL; + + 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 */