diff --git a/include/ucore/dbuf.h b/include/ucore/dbuf.h new file mode 100644 index 0000000..4aa71ab --- /dev/null +++ b/include/ucore/dbuf.h @@ -0,0 +1,121 @@ +#ifndef dbuf_H_ +#define dbuf_H_ +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * dbuf is a generic buffer that will grow automatically. + * call buf_ensure() to reserve space, and don't write more data + * than you told buf_ensure() to reserve. + * + * You write data to buf->end + * When finished writing data + * you call dbuf_added/( with the no. of bytes you've added. + * + * You read data from buf->start, the buffer has dbuf_len() bytes of data + * When you've read and processed the data, call dbuf_take() + * to indicate you're done with the data. + * + * typically(but add error checking) + * DBuf buf; + * dbuf_init(&buf,4096); + * for(;;) { + * dbuf_ensure(buf,4096); + * size_t len = fread(buf,4096,1,f); + * dbuf_added(&buf,len); + * char *end; + * while((end = memchr(buf.start,'\n',dbuf_len(&dbuf))) != NULL) { //look for a newline + * ++end; + * int linelen = end - buf.start; + * process_line(buf.start,linelen); + * dbuf_take(&dbuf,linelen); + * } + * } + */ +typedef struct DBuf DBuf; +struct DBuf { + /** Start of user data */ + unsigned char *start; + /** end of user data */ + unsigned char *end; + /** Start of underlying buffer. Internal use.*/ + unsigned char *bufstart; + /** end of underlying buffer. Internal use.*/ + unsigned char *bufend; +}; +/** free the internals of a DBuf, does not free buf itself. + * Does nothing if buf == NULL + * @param buf buffer to free + */ +void uc_dbuf_free(DBuf *buf); + +/** Initialixe a dbuf. +* +* @param buf buffer to initialize +* @return 0 on success +*/ +int uc_dbuf_init(DBuf *buf,size_t initlen); + +/** Get length of user data in the buffer + * + * @param buf buffer to get length of + * @return length of the user data + */ +size_t uc_dbuf_len(DBuf *buf); + +/** Allocated size of the buffer + * + * @buf buffer to get allocated length of + * @return allocated length of the underlying buffer + */ +size_t uc_dbuf_buflen(DBuf *buf); + +/** Get remaining space in the buffer + * @param buf buffer + * @return remaining space (after buf->end) in the buffer + */ +size_t uc_dbuf_remaining(DBuf *buf); + +/** ensure the buffer can hold 'capacity' no of bytes, + * + * @param buffer + * @param capacity ensure there is room for capacity bytes after buf->end + * @retun 0 on success + */ +int uc_dbuf_ensure(DBuf *buf,size_t capacity); + +/** trim the buffer to hold no more space than needed. + * @param buf buffer to trum + */ +void uc_dbuf_trim(DBuf *buf); + +/** Indicate you've taken 'len' bytes out of the buffer. + * Advances buf->start by len bytes + * This may re-set buf->start and buf->end if e.g. all data + * is taken out of the buffer. + * + * @param buf buffer + * @param len length that was taken out of the buffer + * @return 0 on success. Non-zero if @len is larger than the bufer capacity + */ +int uc_dbuf_take(DBuf *buf,size_t len); + +/** indicate you've added 'len' bytes to the buffer + * Advances buf->end by @len bytes + * User must always make sure (e.g. by using uc_dbuf_ensure) + * that the buffer has the capacity to add @len bytes + * + * @param buf buffer + * @param len length that was added to the buffer. + */ +void uc_dbuf_added(DBuf *buf,size_t len); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/src/dbuf.c b/src/dbuf.c new file mode 100644 index 0000000..ff03328 --- /dev/null +++ b/src/dbuf.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include "ucore/dbuf.h" + +void +uc_dbuf_free(DBuf *buf) +{ + if(buf != NULL) + free(buf->bufstart); + +} + +int +uc_dbuf_init(DBuf *buf,size_t initlen) +{ + unsigned char *tmp = malloc(initlen); + if(tmp == NULL) + return -1; + buf->start = buf->bufstart = buf->end = tmp; + buf->bufend = tmp + initlen; + + return 0; +} + +size_t +uc_dbuf_len(DBuf *buf) +{ + return buf->end - buf->start; +} + +size_t +uc_dbuf_buflen(DBuf *buf) +{ + return buf->bufend - buf->bufstart; +} + +size_t +uc_dbuf_remaining(DBuf *buf) +{ + return buf->bufend - buf->end; +} + +int +uc_dbuf_ensure(DBuf *buf,size_t capacity) +{ + size_t sz,remaining,len; + + remaining = uc_dbuf_remaining(buf); + if(remaining >= capacity) + return 0; + len = uc_dbuf_len(buf); + sz = buf->start - buf->bufstart; + memmove(buf->bufstart,buf->start,len); + if(sz + remaining < capacity) { + size_t newlen = uc_dbuf_buflen(buf) + capacity; + void *tmp = realloc(buf->bufstart,newlen); + if(tmp == NULL) + return 1; + buf->bufstart = tmp; + buf->bufend = buf->bufstart + newlen; + + } + buf->start = buf->bufstart; + buf->end = buf->start + len; + + return 0; +} + +void +uc_dbuf_trim(DBuf *buf) +{ + size_t len = uc_dbuf_len(buf); + void *tmp; + + if(len == 0) + return; + + memmove(buf->bufstart,buf->start,len); + + tmp = realloc(buf->bufstart,len); + if(tmp != NULL) + buf->bufstart = tmp; + + buf->start = buf->bufstart; + buf->bufend = buf->end = buf->bufstart + len; +} + +int +uc_dbuf_take(DBuf *buf,size_t len) +{ + size_t buflen = uc_dbuf_len(buf); + + if(len == buflen) { + buf->start = buf->end = buf->bufstart; + } else if (len < buflen) { + buf->start = buf->start + len; + } else { + //should maybe assert. This is a serious offence + return 1; + } + + return 0; +} + +void +uc_dbuf_added(DBuf *buf,size_t len) +{ + assert(len <= uc_dbuf_remaining(buf)); + buf->end +=len; +} diff --git a/test/test_dbuf.c b/test/test_dbuf.c new file mode 100644 index 0000000..6214efa --- /dev/null +++ b/test/test_dbuf.c @@ -0,0 +1,84 @@ +#include +#include +#include + + +START_TEST (test_dbuf) + DBuf buf; + int rc; + + rc = uc_dbuf_init(&buf, 10); + + fail_if(rc != 0); + fail_if(uc_dbuf_buflen(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 10); + + strcpy((char *)buf.end, "12345679"); + uc_dbuf_added(&buf, 10); + fail_if(uc_dbuf_len(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 0); + + rc = uc_dbuf_ensure(&buf, 110); + fail_if(rc != 0); + fail_if(uc_dbuf_remaining(&buf) != 110); + fail_if(uc_dbuf_buflen(&buf) != 120); + + rc = uc_dbuf_ensure(&buf, 5); + fail_if(rc != 0); + strcpy((char *)buf.end, "ABCD"); + uc_dbuf_added(&buf, 5); + uc_dbuf_take(&buf, 10); + fail_if(uc_dbuf_remaining(&buf) != 105); + fail_if(uc_dbuf_len(&buf) != 5); + ck_assert_str_eq((char *)buf.start, "ABCD"); + + uc_dbuf_take(&buf, 5); + + fail_if(uc_dbuf_remaining(&buf) != 120); + fail_if(uc_dbuf_buflen(&buf) != 120); + fail_if(uc_dbuf_len(&buf) != 0); + + strcpy((char *)buf.end, "EFGH"); + uc_dbuf_added(&buf, 5); + uc_dbuf_trim(&buf); + fail_if(uc_dbuf_buflen(&buf) != 5); + fail_if(uc_dbuf_len(&buf) != 5); + + uc_dbuf_free(&buf); +END_TEST + +START_TEST (test_dbuf_take_fail) + DBuf buf; + int rc; + + rc = uc_dbuf_init(&buf, 10); + + fail_if(rc != 0); + fail_if(uc_dbuf_buflen(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 10); + + strcpy((char *)buf.end, "12345679"); + uc_dbuf_added(&buf, 10); + fail_if(uc_dbuf_len(&buf) != 10); + fail_if(uc_dbuf_remaining(&buf) != 0); + + rc = uc_dbuf_take(&buf, 11); + fail_if(rc == 0); + + uc_dbuf_free(&buf); +END_TEST + + + +Suite *dbuf_suite(void) +{ + Suite *s = suite_create("dbuf"); + TCase *tc = tcase_create("dbuf tests"); + tcase_add_test(tc, test_dbuf); + tcase_add_test(tc, test_dbuf_take_fail); + + suite_add_tcase(s, tc); + + return s; +} + diff --git a/test/test_runner.c b/test/test_runner.c index bf20b6a..2ab0db3 100644 --- a/test/test_runner.c +++ b/test/test_runner.c @@ -21,6 +21,7 @@ extern Suite *sat_math_suite(void); extern Suite *timers_suite(void); extern Suite *mbuf_suite(void); extern Suite *human_bytesz_suite(void); +extern Suite *dbuf_suite(void); static suite_func suites[] = { bitvec_suite, @@ -37,7 +38,8 @@ static suite_func suites[] = { sat_math_suite, timers_suite, mbuf_suite, - human_bytesz_suite + human_bytesz_suite, + dbuf_suite }; int