new auto-extend strategy for CxBuffer - resolves #641

Sun, 13 Apr 2025 14:30:51 +0200

author
Mike Becker <universe@uap-core.de>
date
Sun, 13 Apr 2025 14:30:51 +0200
changeset 1291
5942859fd76c
parent 1290
4ac889e14211
child 1292
1e7ee17777f4

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 = &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));

mercurial