--- a/src/ucx/buffer.c Sat Nov 29 19:03:52 2025 +0100 +++ b/src/ucx/buffer.c Sun Nov 30 18:25:55 2025 +0100 @@ -35,7 +35,7 @@ #ifdef _WIN32 #include <Windows.h> #include <sysinfoapi.h> -static unsigned long system_page_size() { +static unsigned long system_page_size(void) { static unsigned long ps = 0; if (ps == 0) { SYSTEM_INFO sysinfo; @@ -44,16 +44,27 @@ } return ps; } -#define SYSTEM_PAGE_SIZE system_page_size() #else #include <unistd.h> -#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE) +static unsigned long system_page_size(void) { + static unsigned long ps = 0; + if (ps == 0) { + long sc = sysconf(_SC_PAGESIZE); + if (sc < 0) { + // fallback for systems which do not report a value here + ps = 4096; // LCOV_EXCL_LINE + } else { + ps = (unsigned long) sc; + } + } + return ps; +} #endif static int buffer_copy_on_write(CxBuffer* buffer) { if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0; void *newspace = cxMalloc(buffer->allocator, buffer->capacity); - if (NULL == newspace) return -1; + if (NULL == newspace) return -1; // LCOV_EXCL_LINE memcpy(newspace, buffer->space, buffer->size); buffer->space = newspace; buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE; @@ -78,9 +89,7 @@ buffer->flags = flags; if (!space) { buffer->bytes = cxMalloc(allocator, capacity); - if (buffer->bytes == NULL) { - return -1; // LCOV_EXCL_LINE - } + if (buffer->bytes == NULL) return -1; // LCOV_EXCL_LINE buffer->flags |= CX_BUFFER_FREE_CONTENTS; } else { buffer->bytes = space; @@ -122,7 +131,7 @@ allocator = cxDefaultAllocator; } CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); - if (buf == NULL) return NULL; + if (buf == NULL) return NULL; // LCOV_EXCL_LINE if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { return buf; } else { @@ -183,6 +192,35 @@ } +size_t cxBufferPop(CxBuffer *buffer, size_t size, size_t nitems) { + size_t len; + if (cx_szmul(size, nitems, &len)) { + // LCOV_EXCL_START + errno = EOVERFLOW; + return 0; + // LCOV_EXCL_STOP + } + if (len == 0) return 0; + if (len > buffer->size) { + if (size == 1) { + // simple case: everything can be discarded + len = buffer->size; + } else { + // complicated case: misaligned bytes must stay + size_t misalignment = buffer->size % size; + len = buffer->size - misalignment; + } + } + buffer->size -= len; + + // adjust position, if required + if (buffer->pos > buffer->size) { + buffer->pos = buffer->size; + } + + return len / size; +} + void cxBufferClear(CxBuffer *buffer) { if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) { memset(buffer->bytes, 0, buffer->size); @@ -200,36 +238,10 @@ return buffer->pos >= buffer->size; } -int cxBufferMinimumCapacity( - CxBuffer *buffer, - size_t newcap -) { +int cxBufferReserve(CxBuffer *buffer, size_t newcap) { if (newcap <= buffer->capacity) { return 0; } - - unsigned long pagesize = 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) - } - - 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); @@ -249,6 +261,38 @@ } } +static size_t cx_buffer_calculate_minimum_capacity(size_t mincap) { + unsigned long pagesize = 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) + } + return mincap; +} + +int cxBufferMinimumCapacity(CxBuffer *buffer, size_t newcap) { + if (newcap <= buffer->capacity) { + return 0; + } + newcap = cx_buffer_calculate_minimum_capacity(newcap); + return cxBufferReserve(buffer, newcap); +} + void cxBufferShrink( CxBuffer *buffer, size_t reserve @@ -351,8 +395,17 @@ bool perform_flush = false; if (required > buffer->capacity) { if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { - if (buffer->flush != NULL && required > buffer->flush->threshold) { - perform_flush = true; + 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