Sat, 30 Apr 2022 09:47:20 +0200
#184 start implementation of the flush feature
src/buffer.c | file | annotate | diff | comparison | revisions | |
src/cx/buffer.h | file | annotate | diff | comparison | revisions | |
test/test_buffer.cpp | file | annotate | diff | comparison | revisions |
1.1 --- a/src/buffer.c Sat Apr 30 09:03:17 2022 +0200 1.2 +++ b/src/buffer.c Sat Apr 30 09:47:20 2022 +0200 1.3 @@ -32,6 +32,7 @@ 1.4 #include <stdlib.h> 1.5 #include <stdio.h> 1.6 #include <string.h> 1.7 +#include <stdint.h> 1.8 1.9 int cxBufferInit( 1.10 CxBuffer *buffer, 1.11 @@ -53,8 +54,12 @@ 1.12 } 1.13 buffer->capacity = capacity; 1.14 buffer->size = 0; 1.15 + buffer->pos = 0; 1.16 1.17 - buffer->pos = 0; 1.18 + buffer->flush_func = NULL; 1.19 + buffer->flush_blkmax = 0; 1.20 + buffer->flush_blksize = 4096; 1.21 + buffer->flush_threshold = SIZE_MAX; 1.22 1.23 return 0; 1.24 } 1.25 @@ -134,6 +139,7 @@ 1.26 size_t nitems, 1.27 CxBuffer *buffer 1.28 ) { 1.29 + // TODO: optimize for special case size == nitems == 1 1.30 size_t len; 1.31 if (cx_szmul(size, nitems, &len)) { 1.32 return 0; 1.33 @@ -143,15 +149,27 @@ 1.34 return 0; 1.35 } 1.36 1.37 + bool perform_flush = false; 1.38 if (required > buffer->capacity) { 1.39 - if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { 1.40 - if (cxBufferMinimumCapacity(buffer, required)) { 1.41 - return 0; 1.42 + if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { 1.43 + if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { 1.44 + perform_flush = true; 1.45 + } else { 1.46 + if (cxBufferMinimumCapacity(buffer, required)) { 1.47 + return 0; 1.48 + } 1.49 } 1.50 } else { 1.51 - len = buffer->capacity - buffer->pos; 1.52 - if (size > 1) { 1.53 - len -= len % size; 1.54 + if (buffer->flush_blkmax > 0) { 1.55 + perform_flush = true; 1.56 + } else { 1.57 + // truncate data to be written, if we can neither extend nor flush 1.58 + len = buffer->capacity - buffer->pos; 1.59 + if (size > 1) { 1.60 + // TODO: this is bugged - it would only discard one element and not as many as required 1.61 + len -= len % size; 1.62 + nitems = len / size; 1.63 + } 1.64 } 1.65 } 1.66 } 1.67 @@ -160,13 +178,25 @@ 1.68 return len; 1.69 } 1.70 1.71 - memcpy(buffer->bytes + buffer->pos, ptr, len); 1.72 - buffer->pos += len; 1.73 - if (buffer->pos > buffer->size) { 1.74 - buffer->size = buffer->pos; 1.75 + if (perform_flush) { 1.76 + // TODO: implement flushing 1.77 + // (1) determine how many bytes to flush (use flushmax = blkmax * blksize) 1.78 + // (2) if len is larger than the number computed in (1) we need more flush cycles, compute how many 1.79 + // (3) determine how many bytes from the buffer shall be flushed 1.80 + // (4) if something remains in the buffer, shift the buffer to the left 1.81 + // (4a) if buffer was shifted, append the new data to the buffer 1.82 + // (4b) if the buffer was flushed entirely AND the new data also fits into flushmax, 1.83 + // directly write the new data to the flush sink 1.84 + return 0; // remove this after implementation 1.85 + } else { 1.86 + memcpy(buffer->bytes + buffer->pos, ptr, len); 1.87 + buffer->pos += len; 1.88 + if (buffer->pos > buffer->size) { 1.89 + buffer->size = buffer->pos; 1.90 + } 1.91 } 1.92 1.93 - return len / size; 1.94 + return nitems; 1.95 } 1.96 1.97 int cxBufferPut(
2.1 --- a/src/cx/buffer.h Sat Apr 30 09:03:17 2022 +0200 2.2 +++ b/src/cx/buffer.h Sat Apr 30 09:47:20 2022 +0200 2.3 @@ -91,6 +91,40 @@ 2.4 /** Current size of the buffer content. */ 2.5 size_t size; 2.6 /** 2.7 + * The buffer may not extend beyond this threshold before starting to flush. 2.8 + * Default is \c SIZE_MAX (flushing disabled when auto extension is enabled). 2.9 + */ 2.10 + size_t flush_threshold; 2.11 + /** 2.12 + * The block size for the elements to flush. 2.13 + * Default is 4096 bytes. 2.14 + */ 2.15 + size_t flush_blksize; 2.16 + /** 2.17 + * The maximum number of blocks to flush in one cycle. 2.18 + * Zero disables flushing entirely (this is the default). 2.19 + * Set this to \c SIZE_MAX to flush the entire buffer. 2.20 + * 2.21 + * @attention if the maximum number of blocks multiplied with the block size 2.22 + * is smaller than the expected contents written to this buffer within one write 2.23 + * operation, multiple flush cycles are performed after that write. 2.24 + * That means the total number of blocks flushed after one write to this buffer may 2.25 + * be larger than \c flush_blkmax. 2.26 + */ 2.27 + size_t flush_blkmax; 2.28 + 2.29 + /** 2.30 + * The write function used for flushing. 2.31 + * If NULL, the flushed content gets discarded. 2.32 + */ 2.33 + size_t (*flush_func)( 2.34 + void const *, 2.35 + size_t, 2.36 + size_t, 2.37 + void * 2.38 + ); 2.39 + 2.40 + /** 2.41 * Flag register for buffer features. 2.42 * @see #CX_BUFFER_DEFAULT 2.43 * @see #CX_BUFFER_FREE_CONTENTS
3.1 --- a/test/test_buffer.cpp Sat Apr 30 09:03:17 2022 +0200 3.2 +++ b/test/test_buffer.cpp Sat Apr 30 09:47:20 2022 +0200 3.3 @@ -31,11 +31,19 @@ 3.4 #include <gtest/gtest.h> 3.5 #include "util_allocator.h" 3.6 3.7 +static void expect_default_flush_config(CxBuffer *buf) { 3.8 + EXPECT_EQ(buf->flush_blkmax, 0); 3.9 + EXPECT_EQ(buf->flush_blksize, 4096); 3.10 + EXPECT_EQ(buf->flush_threshold, SIZE_MAX); 3.11 + EXPECT_EQ(buf->flush_func, nullptr); 3.12 +} 3.13 + 3.14 TEST(BufferInit, WrapSpace) { 3.15 CxTestingAllocator alloc; 3.16 CxBuffer buf; 3.17 void *space = cxMalloc(&alloc, 16); 3.18 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT); 3.19 + expect_default_flush_config(&buf); 3.20 EXPECT_EQ(buf.space, space); 3.21 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); 3.22 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); 3.23 @@ -49,11 +57,31 @@ 3.24 EXPECT_TRUE(alloc.verify()); 3.25 } 3.26 3.27 +TEST(BufferInit, WrapSpaceAutoExtend) { 3.28 + CxTestingAllocator alloc; 3.29 + CxBuffer buf; 3.30 + void *space = cxMalloc(&alloc, 16); 3.31 + cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND); 3.32 + expect_default_flush_config(&buf); 3.33 + EXPECT_EQ(buf.space, space); 3.34 + EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND); 3.35 + EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0); 3.36 + EXPECT_EQ(buf.pos, 0); 3.37 + EXPECT_EQ(buf.size, 0); 3.38 + EXPECT_EQ(buf.capacity, 16); 3.39 + EXPECT_EQ(buf.allocator, &alloc); 3.40 + cxBufferDestroy(&buf); 3.41 + EXPECT_FALSE(alloc.verify()); 3.42 + cxFree(&alloc, space); 3.43 + EXPECT_TRUE(alloc.verify()); 3.44 +} 3.45 + 3.46 TEST(BufferInit, WrapSpaceAutoFree) { 3.47 CxTestingAllocator alloc; 3.48 CxBuffer buf; 3.49 void *space = cxMalloc(&alloc, 16); 3.50 cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS); 3.51 + expect_default_flush_config(&buf); 3.52 EXPECT_EQ(buf.space, space); 3.53 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); 3.54 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS); 3.55 @@ -70,6 +98,7 @@ 3.56 CxTestingAllocator alloc; 3.57 CxBuffer buf; 3.58 cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT); 3.59 + expect_default_flush_config(&buf); 3.60 EXPECT_NE(buf.space, nullptr); 3.61 EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0); 3.62 EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);