src/buffer.c

changeset 1571
25ead2ffb9b5
parent 1542
197450c2b0b3
--- a/src/buffer.c	Wed Dec 10 23:27:32 2025 +0100
+++ b/src/buffer.c	Thu Dec 11 17:08:17 2025 +0100
@@ -66,21 +66,10 @@
         buffer->bytes = space;
     }
     buffer->capacity = capacity;
+    buffer->max_capacity = SIZE_MAX;
     buffer->size = 0;
     buffer->pos = 0;
 
-    buffer->flush = NULL;
-
-    return 0;
-}
-
-int cxBufferEnableFlushing(
-    CxBuffer *buffer,
-    CxBufferFlushConfig config
-) {
-    buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig));
-    if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE
-    memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig));
     return 0;
 }
 
@@ -88,7 +77,6 @@
     if (buffer->flags & CX_BUFFER_FREE_CONTENTS) {
         cxFree(buffer->allocator, buffer->bytes);
     }
-    cxFreeDefault(buffer->flush);
     memset(buffer, 0, sizeof(CxBuffer));
 }
 
@@ -213,6 +201,9 @@
     if (newcap == buffer->capacity) {
         return 0;
     }
+    if (newcap > buffer->max_capacity) {
+        return -1;
+    }
     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);
@@ -236,35 +227,46 @@
     }
 }
 
-static size_t cx_buffer_calculate_minimum_capacity(size_t mincap) {
-    unsigned long pagesize = cx_system_page_size();
-    // if page size is larger than 64 KB - for some reason - truncate to 64 KB
-    if (pagesize > 65536) pagesize = 65536;
-    if (mincap < pagesize) {
-        // when smaller as one page, map to the next power of two
-        mincap--;
-        mincap |= mincap >> 1;
-        mincap |= mincap >> 2;
-        mincap |= mincap >> 4;
-        // last operation only needed for pages larger 4096 bytes
-        // but if/else would be more expensive than just doing this
-        mincap |= mincap >> 8;
-        mincap++;
-    } else {
-        // otherwise, map to a multiple of the page size
-        mincap -= mincap % pagesize;
-        mincap += pagesize;
-        // note: if newcap is already page aligned,
-        // this gives a full additional page (which is good)
+int cxBufferMaximumCapacity(CxBuffer *buffer, size_t capacity) {
+    if (capacity < buffer->capacity) {
+        return -1;
     }
-    return mincap;
+    buffer->max_capacity = capacity;
+    return 0;
 }
 
 int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) {
     if (newcap <= buffer->capacity) {
         return 0;
     }
-    newcap = cx_buffer_calculate_minimum_capacity(newcap);
+    if (newcap > buffer->max_capacity) {
+        return -1;
+    }
+    if (newcap < buffer->max_capacity) {
+        unsigned long pagesize = cx_system_page_size();
+        // 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)
+        }
+        if (newcap > buffer->max_capacity) {
+            newcap = buffer->max_capacity;
+        }
+    }
     return cxBufferReserve(buffer, newcap);
 }
 
@@ -290,54 +292,6 @@
     }
 }
 
-static size_t cx_buffer_flush_helper(
-        const CxBuffer *buffer,
-        const unsigned char *src,
-        size_t size,
-        size_t nitems
-) {
-    // flush data from an arbitrary source
-    // does not need to be the buffer's contents
-    size_t max_items = buffer->flush->blksize / size;
-    size_t fblocks = 0;
-    size_t flushed_total = 0;
-    while (nitems > 0 && fblocks < buffer->flush->blkmax) {
-        fblocks++;
-        size_t items = nitems > max_items ? max_items : nitems;
-        size_t flushed = buffer->flush->wfunc(
-            src, size, items, buffer->flush->target);
-        if (flushed > 0) {
-            flushed_total += flushed;
-            src += flushed * size;
-            nitems -= flushed;
-        } else {
-            // if no bytes can be flushed out anymore, we give up
-            break;
-        }
-    }
-    return flushed_total;
-}
-
-static size_t cx_buffer_flush_impl(CxBuffer *buffer, size_t size) {
-    // flush the current contents of the buffer
-    unsigned char *space = buffer->bytes;
-    size_t remaining = buffer->pos / size;
-    size_t flushed_total = cx_buffer_flush_helper(
-        buffer, space, size, remaining);
-
-    // shift the buffer left after flushing
-    // IMPORTANT: up to this point, copy on write must have been
-    // performed already, because we can't do error handling here
-    cxBufferShiftLeft(buffer, flushed_total*size);
-
-    return flushed_total;
-}
-
-size_t cxBufferFlush(CxBuffer *buffer) {
-    if (buffer_copy_on_write(buffer)) return 0;
-    return cx_buffer_flush_impl(buffer, 1);
-}
-
 size_t cxBufferWrite(
         const void *ptr,
         size_t size,
@@ -355,107 +309,52 @@
         return nitems;
     }
 
-    size_t len, total_flushed = 0;
-cx_buffer_write_retry:
+    size_t len;
     if (cx_szmul(size, nitems, &len)) {
         errno = EOVERFLOW;
-        return total_flushed;
+        return 0;
     }
     if (buffer->pos > SIZE_MAX - len) {
         errno = EOVERFLOW;
-        return total_flushed;
+        return 0;
     }
+    const size_t required = buffer->pos + len;
 
-    size_t required = buffer->pos + len;
-    bool perform_flush = false;
+    // check if we need to auto-extend
     if (required > buffer->capacity) {
         if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
-            if (buffer->flush != NULL) {
-                size_t newcap = cx_buffer_calculate_minimum_capacity(required);
-                if (newcap > buffer->flush->threshold) {
-                    newcap = buffer->flush->threshold;
-                }
-                if (cxBufferReserve(buffer, newcap)) {
-                    return total_flushed; // LCOV_EXCL_LINE
-                }
-                if (required > newcap) {
-                    perform_flush = true;
-                }
-            } else {
-                if (cxBufferMinimumCapacity(buffer, required)) {
-                    return total_flushed; // LCOV_EXCL_LINE
-                }
-            }
-        } else {
-            if (buffer->flush != NULL) {
-                perform_flush = true;
-            } else {
-                // truncate data, if we can neither extend nor flush
-                len = buffer->capacity - buffer->pos;
-                if (size > 1) {
-                    len -= len % size;
-                }
-                nitems = len / size;
+            size_t newcap = required < buffer->max_capacity
+                    ? required : buffer->max_capacity;
+            if (cxBufferMinimumCapacity(buffer, newcap)) {
+                return 0; // LCOV_EXCL_LINE
             }
         }
     }
 
+    // check again and truncate data if capacity is still not enough
+    if (required > buffer->capacity) {
+        len = buffer->capacity - buffer->pos;
+        if (size > 1) {
+            len -= len % size;
+        }
+        nitems = len / size;
+    }
+
     // check here and not above because of possible truncation
     if (len == 0) {
-        return total_flushed;
+        return 0;
     }
 
     // check if we need to copy
     if (buffer_copy_on_write(buffer)) return 0;
 
     // perform the operation
-    if (perform_flush) {
-        size_t items_flushed;
-        if (buffer->pos == 0) {
-            // if we don't have data in the buffer, but are instructed
-            // to flush, it means that we are supposed to relay the data
-            items_flushed = cx_buffer_flush_helper(buffer, ptr, size, nitems);
-            if (items_flushed == 0) {
-                // we needed to relay data, but could not flush anything
-                // i.e. we have to give up to avoid endless trying
-                return 0;
-            }
-            nitems -= items_flushed;
-            total_flushed += items_flushed;
-            if (nitems > 0) {
-                ptr = ((unsigned char*)ptr) + items_flushed * size;
-                goto cx_buffer_write_retry;
-            }
-            return total_flushed;
-        } else {
-            items_flushed = cx_buffer_flush_impl(buffer, size);
-            if (items_flushed == 0) {
-                // flush target is full, let's try to truncate
-                size_t remaining_space;
-                if (buffer->flags & CX_BUFFER_AUTO_EXTEND) {
-                    remaining_space = buffer->flush->threshold > buffer->pos
-                                          ? buffer->flush->threshold - buffer->pos
-                                          : 0;
-                } else {
-                    remaining_space = buffer->capacity > buffer->pos
-                                          ? buffer->capacity - buffer->pos
-                                          : 0;
-                }
-                nitems = remaining_space / size;
-                if (nitems == 0) {
-                    return total_flushed;
-                }
-            }
-            goto cx_buffer_write_retry;
-        }
-    } else {
-        memcpy(buffer->bytes + buffer->pos, ptr, len);
-        buffer->pos += len;
-        if (buffer->pos > buffer->size) {
-            buffer->size = buffer->pos;
-        }
-        return total_flushed + nitems;
+    memcpy(buffer->bytes + buffer->pos, ptr, len);
+    buffer->pos += len;
+    if (buffer->pos > buffer->size) {
+        buffer->size = buffer->pos;
     }
+    return nitems;
 }
 
 size_t cxBufferAppend(

mercurial