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:
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