rsys

Basic data structures and low-level features
git clone git://git.meso-star.fr/rsys.git
Log | Files | Refs | README | LICENSE

commit 8bc1b3e34a5276a6cecc9920ef2d2ddd4aa999ea
parent 301536c3a9c8d6a7d270b800112cdc9def2353d5
Author: vaplv <vaplv@free.fr>
Date:   Mon,  8 May 2017 15:23:01 +0200

The big buffer can be initialized from a stream containing data

Diffstat:
Msrc/big_buffer.h | 156+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msrc/test_big_buffer.c | 40++++++++++++++++++++++++++++++++++++++--
2 files changed, 155 insertions(+), 41 deletions(-)

diff --git a/src/big_buffer.h b/src/big_buffer.h @@ -26,6 +26,10 @@ /* Internal enumerate used to define the mode of the buffered data */ enum bigbuf_mode__ { BIGBUF_READ__, BIGBUF_WRITE__ }; +enum bigbuf_flag { + BIGBUF_LOAD_STREAM = BIT(1) +}; + #endif /* BIGBUF_H */ #else @@ -78,27 +82,109 @@ struct BIGBUF__ { FILE* file; /* Stream where buffer data are written */ size_t buf_index; /* Id of the first big buffer item store into `buf' */ size_t size; /* Number of items stored into the big buffer */ - long head; /* Head position indicator into `file' of the big buffer data */ - int temp_file; /* Define if `file' is temporary file or not */ + long head; /* Position indicator into `file' toward the first data */ + long end; /* Position indicator into `file' behind the last data */ + int temp_file; /* Define if `file' is a temporary file or not */ struct mem_allocator* allocator; }; /******************************************************************************* + * Internal helper functions + ******************************************************************************/ +static INLINE res_T +BIGBUF_FUNC__(flush__)(struct BIGBUF__* bigbuf) +{ + BIGBUF_DATA* buf_data; + size_t buf_size, write_size; + int err; + res_T res = RES_OK; + ASSERT(bigbuf); + + if(bigbuf->mode == BIGBUF_READ__) goto exit; + if(!bigbuf->buf.size) goto exit; + + buf_size = bigbuf->buf.size; + buf_data = bigbuf->buf.data; + ASSERT(bigbuf->buf_index + buf_size == bigbuf->size); + + err = fseek(bigbuf->file, bigbuf->end, SEEK_SET); + if(err < 0) { + res = RES_IO_ERR; + goto error; + } + + write_size = fwrite(buf_data, sizeof(BIGBUF_DATA), buf_size, bigbuf->file); + bigbuf->end = bigbuf->end + (long)write_size * (long)sizeof(BIGBUF_DATA); + if(write_size == buf_size) { + bigbuf->buf.size = 0; /* Clear buffered data */ + bigbuf->buf_index = bigbuf->size; + } else if(write_size) { + size_t cp_size = buf_size - write_size; + memmove(buf_data, buf_data + write_size, cp_size); + bigbuf->buf.size = cp_size; + bigbuf->buf_index = bigbuf->buf_index + write_size; + res = RES_IO_ERR; + } else { + res = RES_IO_ERR; + } + if(res != RES_OK) goto error; + +exit: + return res; +error: + goto exit; +} + +/******************************************************************************* * Big buffer API ******************************************************************************/ +static INLINE res_T +BIGBUF_FUNC__(flush)(struct BIGBUF__* bigbuf) +{ + size_t write_size; + int err; + res_T res = RES_OK; + ASSERT(bigbuf); + + /* Flush buffered data */ + res = BIGBUF_FUNC__(flush__)(bigbuf); + if(res != RES_OK) goto error; + + /* Write the big buffer header */ + err = fseek(bigbuf->file, bigbuf->head - (long)sizeof(size_t), SEEK_SET); + if(err < 0) { + res = RES_IO_ERR; + goto error; + } + write_size = fwrite(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(write_size != 1) { + res = RES_IO_ERR; + goto error; + } + +exit: + return res; +error: + goto exit; +} + static INLINE void BIGBUF_FUNC__(release)(struct BIGBUF__* bigbuf) { ASSERT(bigbuf); if(bigbuf->buf.data) MEM_RM(bigbuf->allocator, bigbuf->buf.data); - if(bigbuf->file && bigbuf->temp_file) fclose(bigbuf->file); + if(bigbuf->file) { + CHECK(BIGBUF_FUNC__(flush)(bigbuf), RES_OK); + if(bigbuf->temp_file) fclose(bigbuf->file); + } } static INLINE res_T BIGBUF_FUNC__(init) (struct mem_allocator* allocator, /* May be NULL <=> use default allocator */ FILE* stream, /* May be NULL <=> internally manage the stream */ + int mask, /* Combination of bigbuf_flag */ struct BIGBUF__* bigbuf) { size_t bufsz_adjusted; @@ -106,6 +192,11 @@ BIGBUF_FUNC__(init) ASSERT(bigbuf); memset(bigbuf, 0, sizeof(struct BIGBUF__)); + if(!stream && (mask & BIGBUF_LOAD_STREAM)) { + res = RES_BAD_ARG; + goto error; + } + bigbuf->allocator = allocator ? allocator : &mem_default_allocator; bigbuf->buf.capacity = BIGBUF_BUFSIZE / sizeof(BIGBUF_DATA); bigbuf->buf.capacity = MMAX(bigbuf->buf.capacity, 1); @@ -132,9 +223,27 @@ BIGBUF_FUNC__(init) bigbuf->temp_file = 1; } - bigbuf->buf_index = 0; + if(mask & BIGBUF_LOAD_STREAM) { + /* Read the big buffer header */ + size_t rsize = fread(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(rsize != 1) { + res = RES_IO_ERR; + goto error; + } + } else { + /* Reserve space for the big buffer header storing the big buffer size */ + size_t wsize; + bigbuf->size = 0; + wsize = fwrite(&bigbuf->size, sizeof(size_t), 1, bigbuf->file); + if(wsize != 1) { + res = RES_IO_ERR; + goto error; + } + } + + bigbuf->buf_index = bigbuf->size; bigbuf->head = ftell(bigbuf->file); - bigbuf->size = 0; + bigbuf->end = bigbuf->head + (long)bigbuf->size * (long)sizeof(BIGBUF_DATA); bigbuf->mode = BIGBUF_WRITE__; exit: @@ -145,37 +254,6 @@ error: } static INLINE res_T -BIGBUF_FUNC__(flush)(struct BIGBUF__* bigbuf) -{ - BIGBUF_DATA* buf_data; - size_t buf_size, write_size; - ASSERT(bigbuf); - - if(bigbuf->mode == BIGBUF_READ__) return RES_OK; - if(!bigbuf->buf.size) return RES_OK; - - buf_size = bigbuf->buf.size; - buf_data = bigbuf->buf.data; - ASSERT(bigbuf->buf_index + buf_size == bigbuf->size); - - fseek(bigbuf->file, 0, SEEK_END); - write_size = fwrite(buf_data, sizeof(BIGBUF_DATA), buf_size, bigbuf->file); - if(write_size == buf_size) { - bigbuf->buf.size = 0; /* Clear buffered data */ - bigbuf->buf_index = bigbuf->size; - return RES_OK; - } else if(write_size) { - size_t cp_size = buf_size - write_size; - memmove(buf_data, buf_data + write_size, cp_size); - bigbuf->buf.size = cp_size; - bigbuf->buf_index = bigbuf->buf_index + write_size; - return RES_IO_ERR; - } else { - return RES_IO_ERR; - } -} - -static INLINE res_T BIGBUF_FUNC__(push_back)(struct BIGBUF__* bigbuf, const BIGBUF_DATA* data) { ASSERT(bigbuf); @@ -192,7 +270,7 @@ BIGBUF_FUNC__(push_back)(struct BIGBUF__* bigbuf, const BIGBUF_DATA* data) ++bigbuf->size; if(bigbuf->buf.size == bigbuf->buf.capacity) { - return BIGBUF_FUNC__(flush)(bigbuf); + return BIGBUF_FUNC__(flush__)(bigbuf); } else { return RES_OK; } @@ -228,14 +306,14 @@ BIGBUF_FUNC__(at)(struct BIGBUF__* bigbuf, const size_t at, BIGBUF_DATA* data) int err; if(bigbuf->mode == BIGBUF_WRITE__) { - res = BIGBUF_FUNC__(flush)(bigbuf); + res = BIGBUF_FUNC__(flush__)(bigbuf); if(res != RES_OK) goto error; } buf_index = at / bigbuf->buf.capacity * bigbuf->buf.capacity; cur = ftell(bigbuf->file); - offset = bigbuf->head + (long)(sizeof(BIGBUF_DATA)*buf_index) - cur; + offset = (long)(sizeof(BIGBUF_DATA)*buf_index) - cur + bigbuf->head; err = fseek(bigbuf->file, offset, SEEK_CUR); if(err < 0) { res = RES_IO_ERR; diff --git a/src/test_big_buffer.c b/src/test_big_buffer.c @@ -42,7 +42,8 @@ test_byte(struct mem_allocator* allocator) size_t i; char byte; - CHECK(bigbuf_byte_init(allocator, NULL, &bytes), RES_OK); + CHECK(bigbuf_byte_init(allocator, NULL, BIGBUF_LOAD_STREAM, &bytes), RES_BAD_ARG); + CHECK(bigbuf_byte_init(allocator, NULL, 0, &bytes), RES_OK); CHECK(bigbuf_byte_size_get(&bytes), 0); FOR_EACH(i, 0, 32) { @@ -81,10 +82,14 @@ static void test_integer(void) { struct bigbuf_integer ints; + struct bigbuf_integer ints2; + FILE* stream; size_t i; int integer; - CHECK(bigbuf_integer_init(NULL, NULL, &ints), RES_OK); + NCHECK(stream = tmpfile(), NULL); + + CHECK(bigbuf_integer_init(NULL, stream, 0, &ints), RES_OK); FOR_EACH(i, 0, 999) { integer = (int)i; @@ -102,12 +107,43 @@ test_integer(void) CHECK(bigbuf_integer_push_back(&ints, &integer), RES_OK); } + CHECK(bigbuf_integer_size_get(&ints), 1665); FOR_EACH(i, 0, bigbuf_integer_size_get(&ints)) { CHECK(bigbuf_integer_at(&ints, i, &integer), RES_OK); CHECK((size_t)integer, i); } bigbuf_integer_release(&ints); + + rewind(stream); + CHECK(bigbuf_integer_init(NULL, stream, BIGBUF_LOAD_STREAM, &ints), RES_OK); + CHECK(bigbuf_integer_size_get(&ints), 1665); + + FOR_EACH(i, 0, 666) { + size_t id = (size_t)(rand_canonic() * (double)bigbuf_integer_size_get(&ints)); + CHECK(bigbuf_integer_at(&ints, id, &integer), RES_OK); + CHECK((size_t)integer, id); + } + + FOR_EACH(i, 0, 1024) { + integer = (int)i + 1665; + CHECK(bigbuf_integer_push_back(&ints, &integer), RES_OK); + } + CHECK(bigbuf_integer_size_get(&ints), 2689); + bigbuf_integer_release(&ints); + + rewind(stream); + CHECK(bigbuf_integer_init(NULL, stream, BIGBUF_LOAD_STREAM, &ints2), RES_OK); + CHECK(bigbuf_integer_size_get(&ints2), bigbuf_integer_size_get(&ints)); + CHECK(bigbuf_integer_size_get(&ints2), 2689); + + FOR_EACH(i, 0, bigbuf_integer_size_get(&ints2)) { + CHECK(bigbuf_integer_at(&ints2, i, &integer), RES_OK); + CHECK((size_t)integer, i); + } + + bigbuf_integer_release(&ints2); + fclose(stream); } int