change cxBufferReserve() to allow reducing the capacity default tip

Fri, 05 Dec 2025 16:38:17 +0100

author
Mike Becker <universe@uap-core.de>
date
Fri, 05 Dec 2025 16:38:17 +0100
changeset 1542
197450c2b0b3
parent 1541
d06aa9db0408

change cxBufferReserve() to allow reducing the capacity

resolves #773

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	Fri Dec 05 16:36:10 2025 +0100
+++ b/CHANGELOG	Fri Dec 05 16:38:17 2025 +0100
@@ -2,6 +2,7 @@
 ------------------------
 
  * adds cx_system_page_size() to allocator.h
+ * change cxBufferReserve() to allow reducing the capacity
  * fix that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
    were not returning zero after freeing the memory when passed a size of zero
 
--- a/docs/Writerside/topics/about.md	Fri Dec 05 16:36:10 2025 +0100
+++ b/docs/Writerside/topics/about.md	Fri Dec 05 16:38:17 2025 +0100
@@ -29,6 +29,7 @@
 ### Version 4.0 - preview {collapsible="true"}
 
 * adds cx_system_page_size() to allocator.h
+* change cxBufferReserve() to allow reducing the capacity
 * fix that cxReallocate(), cxReallocateArray(), cx_reallocate(), and cx_reallocatearray()
   were not returning zero after freeing the memory when passed a size of zero
 
--- a/docs/Writerside/topics/buffer.h.md	Fri Dec 05 16:36:10 2025 +0100
+++ b/docs/Writerside/topics/buffer.h.md	Fri Dec 05 16:38:17 2025 +0100
@@ -129,12 +129,14 @@
 void cxBufferShrink(CxBuffer *buffer, size_t reserve);
 ```
 
-The functions `cxBufferReserve()` and `cxBufferMinimumCapacity()` guarantee a buffer capacity of _at least_ `capacity`.
-The difference is, that `cxBufferReserve()` will not increase the capacity beyond the specified `capacity`,
-while `cxBufferMinimumCapacity()` will grow the capacity in powers of two until the system's page size is reached.
+The function `cxBufferMinimumCapacity()` guarantees a buffer capacity of _at least_ `capacity`.
+It will grow the capacity in powers 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 `cxBufferReserve()`, on the other hand, will reallocate the buffer's space to match exactly the requested `capacity`
+and the contents are truncated if required.
+
 You should use `cxBufferReserve()` when you know precisely the required capacity beforehand
 and `cxBufferMinimumCapacity()` when it is likely that the buffer needs to regrow soon.
 
--- a/src/buffer.c	Fri Dec 05 16:36:10 2025 +0100
+++ b/src/buffer.c	Fri Dec 05 16:38:17 2025 +0100
@@ -210,7 +210,7 @@
 }
 
 int cxBufferReserve(CxBuffer *buffer, size_t newcap) {
-    if (newcap <= buffer->capacity) {
+    if (newcap == buffer->capacity) {
         return 0;
     }
     const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND;
@@ -225,7 +225,11 @@
         return 0;
     } else if (cxReallocate(buffer->allocator,
                      (void **) &buffer->bytes, newcap) == 0) {
+        buffer->flags |= CX_BUFFER_FREE_CONTENTS;
         buffer->capacity = newcap;
+        if (buffer->size > newcap) {
+            buffer->size = newcap;
+        }
         return 0;
     } else {
         return -1; // LCOV_EXCL_LINE
--- a/src/cx/buffer.h	Fri Dec 05 16:36:10 2025 +0100
+++ b/src/cx/buffer.h	Fri Dec 05 16:38:17 2025 +0100
@@ -443,6 +443,8 @@
  * Ensures that the buffer has the required capacity.
  *
  * If the current capacity is not sufficient, the buffer will be extended.
+ * If the current capacity is larger, the buffer is shrunk and superfluous
+ * content is discarded.
  *
  * This function will reserve no more bytes than requested, in contrast to
  * cxBufferMinimumCapacity(), which may reserve more bytes to improve the
@@ -450,7 +452,7 @@
  *
  * @param buffer the buffer
  * @param capacity the required capacity for this buffer
- * @retval zero the capacity was already sufficient or successfully increased
+ * @retval zero on success
  * @retval non-zero on allocation failure
  * @see cxBufferShrink()
  * @see cxBufferMinimumCapacity()
--- a/tests/test_buffer.c	Fri Dec 05 16:36:10 2025 +0100
+++ b/tests/test_buffer.c	Fri Dec 05 16:38:17 2025 +0100
@@ -277,6 +277,41 @@
     cx_testing_allocator_destroy(&talloc);
 }
 
+CX_TEST(test_buffer_reserve) {
+    CxTestingAllocator talloc;
+    cx_testing_allocator_init(&talloc);
+    CxAllocator *alloc = &talloc.base;
+    CX_TEST_DO {
+        CxBuffer buf;
+        char space[16] = "Testing";
+        cxBufferInit(&buf, space, 16, alloc, CX_BUFFER_COPY_ON_EXTEND);
+        buf.size = 8;
+        CX_TEST_ASSERT(buf.capacity == 16);
+        CX_TEST_ASSERT(talloc.alloc_total == 0);
+        // reserve to grow
+        cxBufferReserve(&buf, 32);
+        CX_TEST_ASSERT(buf.capacity == 32);
+        CX_TEST_ASSERT(buf.size == 8);
+        CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0);
+        CX_TEST_ASSERT(talloc.alloc_total > 0);
+        CX_TEST_ASSERT((buf.flags & CX_BUFFER_COPY_ON_EXTEND) == 0);
+        // reserve to shrink
+        buf.size = 24;
+        cxBufferReserve(&buf, 16);
+        CX_TEST_ASSERT(buf.capacity == 16);
+        CX_TEST_ASSERT(buf.size == 16);
+        CX_TEST_ASSERT(memcmp(buf.space, "Testing", 8) == 0);
+        // reserve to free
+        cxBufferReserve(&buf, 0);
+        CX_TEST_ASSERT(buf.capacity == 0);
+        CX_TEST_ASSERT(buf.size == 0);
+        CX_TEST_ASSERT(buf.space == NULL);
+        CX_TEST_ASSERT(cx_testing_allocator_verify(&talloc));
+        cxBufferDestroy(&buf);
+    }
+    cx_testing_allocator_destroy(&talloc);
+}
+
 CX_TEST(test_buffer_clear) {
     char space[16];
     strcpy(space, "clear test");
@@ -1769,6 +1804,7 @@
     cx_test_register(suite, test_buffer_shrink);
     cx_test_register(suite, test_buffer_shrink_copy_on_write);
     cx_test_register(suite, test_buffer_shrink_copy_on_extend);
+    cx_test_register(suite, test_buffer_reserve);
     cx_test_register(suite, test_buffer_clear);
     cx_test_register(suite, test_buffer_clear_copy_on_write);
     cx_test_register(suite, test_buffer_reset);

mercurial