Sun, 13 Apr 2025 14:30:51 +0200
new auto-extend strategy for CxBuffer - resolves #641
CHANGELOG | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/about.md | file | annotate | diff | comparison | revisions | |
docs/Writerside/topics/buffer.h.md | file | annotate | diff | comparison | revisions | |
src/buffer.c | file | annotate | diff | comparison | revisions | |
src/cx/buffer.h | file | annotate | diff | comparison | revisions | |
tests/test_buffer.c | file | annotate | diff | comparison | revisions |
--- a/CHANGELOG Sun Apr 13 13:02:54 2025 +0200 +++ b/CHANGELOG Sun Apr 13 14:30:51 2025 +0200 @@ -5,6 +5,7 @@ * adds cxListSet() * adds cxBufferShrink() * changes grow strategy for the mempory pool to reduce reallocations + * changes grow strategy for CxBuffer, which does now take the page size into account * fixes errno value after failing cxBufferSeek() to be consistently EINVAL * fixes implementation of cxBufferTerminate() * fixes allocator arguments for some printf.h functions not being const
--- a/docs/Writerside/topics/about.md Sun Apr 13 13:02:54 2025 +0200 +++ b/docs/Writerside/topics/about.md Sun Apr 13 14:30:51 2025 +0200 @@ -32,6 +32,7 @@ * adds cxListSet() * adds cxBufferShrink() * changes grow strategy for the mempory pool to reduce reallocations +* changes grow strategy for CxBuffer, which does now take the page size into account * fixes errno value after failing cxBufferSeek() to be consistently EINVAL * fixes implementation of cxBufferTerminate() * fixes allocator arguments for some printf.h functions not being const
--- a/docs/Writerside/topics/buffer.h.md Sun Apr 13 13:02:54 2025 +0200 +++ b/docs/Writerside/topics/buffer.h.md Sun Apr 13 14:30:51 2025 +0200 @@ -128,7 +128,8 @@ ``` The function `cxBufferMinimumCapacity()` guarantees a buffer capacity of _at least_ `capacity`. -It may allocate more space, depending on the allocation strategy. +The actual capacity will be a power of two until the system's page size is reached. +Then, the new capacity will be a multiple of the page size. The function returns non-zero if increasing the capacity was attempted unsuccessfully. The function `cxBufferShrink()` can be used to shrink the capacity of the buffer to its current size
--- a/src/buffer.c Sun Apr 13 13:02:54 2025 +0200 +++ b/src/buffer.c Sun Apr 13 14:30:51 2025 +0200 @@ -29,6 +29,7 @@ #include "cx/buffer.h" #include <stdio.h> +#include <unistd.h> #include <string.h> #include <errno.h> @@ -190,6 +191,28 @@ return 0; } + unsigned long pagesize = sysconf(_SC_PAGESIZE); + // if page size is larger than 64 KB - for some reason - truncate to 64 KB + if (pagesize > 65536) pagesize = 65536; + if (newcap < pagesize) { + // when smaller as one page, map to the next power of two + newcap--; + newcap |= newcap >> 1; + newcap |= newcap >> 2; + newcap |= newcap >> 4; + // last operation only needed for pages larger 4096 bytes + // but if/else would be more expensive than just doing this + newcap |= newcap >> 8; + newcap++; + } else { + // otherwise, map to a multiple of the page size + newcap -= newcap % pagesize; + newcap += pagesize; + // note: if newcap is already page aligned, + // this gives a full additional page (which is good) + } + + const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND; if (buffer->flags & force_copy_flags) { void *newspace = cxMalloc(buffer->allocator, newcap);
--- a/src/cx/buffer.h Sun Apr 13 13:02:54 2025 +0200 +++ b/src/cx/buffer.h Sun Apr 13 14:30:51 2025 +0200 @@ -474,6 +474,9 @@ * * If the current capacity is not sufficient, the buffer will be extended. * + * The new capacity will be a power of two until the system's page size is reached. + * Then, the new capacity will be a multiple of the page size. + * * @param buffer the buffer * @param capacity the minimum required capacity for this buffer * @retval zero the capacity was already sufficient or successfully increased
--- a/tests/test_buffer.c Sun Apr 13 13:02:54 2025 +0200 +++ b/tests/test_buffer.c Sun Apr 13 14:30:51 2025 +0200 @@ -1217,7 +1217,7 @@ cxBufferPutString(&buf, "prep"); CX_TEST_DO { CxBufferFlushConfig flush; - flush.threshold = 12; + flush.threshold = 16; flush.blksize = 32; flush.blkmax = 1; flush.target = ⌖ @@ -1227,15 +1227,14 @@ CX_TEST_ASSERT(written == 6); CX_TEST_ASSERT(buf.pos == 10); CX_TEST_ASSERT(buf.size == 10); - CX_TEST_ASSERT(buf.capacity >= 10); - CX_TEST_ASSERT(buf.capacity <= 12); + CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(target.pos == 0); CX_TEST_ASSERT(target.size == 0); - written = cxBufferWrite("hello", 1, 5, &buf); - CX_TEST_ASSERT(written == 5); - CX_TEST_ASSERT(buf.pos == 5); - CX_TEST_ASSERT(buf.size == 5); - CX_TEST_ASSERT(buf.capacity <= 12); + written = cxBufferWrite("hello world", 1, 11, &buf); + CX_TEST_ASSERT(written == 11); + CX_TEST_ASSERT(buf.pos == 11); + CX_TEST_ASSERT(buf.size == 11); + CX_TEST_ASSERT(buf.capacity == 16); CX_TEST_ASSERT(target.pos == 10); CX_TEST_ASSERT(target.size == 10); CX_TEST_ASSERT(0 == memcmp(buf.space, "hello", 5));