--- a/ucx/buffer.c Sun Aug 31 14:39:13 2025 +0200 +++ b/ucx/buffer.c Sat Nov 08 23:06:11 2025 +0100 @@ -32,6 +32,24 @@ #include <string.h> #include <errno.h> +#ifdef _WIN32 +#include <Windows.h> +#include <sysinfoapi.h> +static unsigned long system_page_size() { + static unsigned long ps = 0; + if (ps == 0) { + SYSTEM_INFO sysinfo; + GetSystemInfo(&sysinfo); + ps = sysinfo.dwPageSize; + } + return ps; +} +#define SYSTEM_PAGE_SIZE system_page_size() +#else +#include <unistd.h> +#define SYSTEM_PAGE_SIZE sysconf(_SC_PAGESIZE) +#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); @@ -80,7 +98,7 @@ CxBuffer *buffer, CxBufferFlushConfig config ) { - buffer->flush = malloc(sizeof(CxBufferFlushConfig)); + buffer->flush = cxMallocDefault(sizeof(CxBufferFlushConfig)); if (buffer->flush == NULL) return -1; // LCOV_EXCL_LINE memcpy(buffer->flush, &config, sizeof(CxBufferFlushConfig)); return 0; @@ -90,7 +108,7 @@ if (buffer->flags & CX_BUFFER_FREE_CONTENTS) { cxFree(buffer->allocator, buffer->bytes); } - free(buffer->flush); + cxFreeDefault(buffer->flush); memset(buffer, 0, sizeof(CxBuffer)); } @@ -139,6 +157,7 @@ npos = 0; break; default: + errno = EINVAL; return -1; } @@ -146,11 +165,16 @@ npos += offset; if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { - errno = EOVERFLOW; + // to be compliant with fseek() specification + // we return EINVAL on underflow + errno = EINVAL; return -1; } if (npos > buffer->size) { + // not compliant with fseek() specification + // but this is the better behavior for CxBuffer + errno = EINVAL; return -1; } else { buffer->pos = npos; @@ -184,6 +208,28 @@ 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); @@ -203,6 +249,28 @@ } } +void cxBufferShrink( + CxBuffer *buffer, + size_t reserve +) { + // Ensure buffer is in a reallocatable state + const int force_copy_flags = CX_BUFFER_COPY_ON_WRITE | CX_BUFFER_COPY_ON_EXTEND; + if (buffer->flags & force_copy_flags) { + // do nothing when we are not allowed to reallocate + return; + } + + // calculate new capacity + size_t newCapacity = buffer->size + reserve; + + // If new capacity is smaller than current capacity, resize the buffer + if (newCapacity < buffer->capacity) { + if (0 == cxReallocate(buffer->allocator, &buffer->bytes, newCapacity)) { + buffer->capacity = newCapacity; + } + } +} + static size_t cx_buffer_flush_helper( const CxBuffer *buffer, const unsigned char *src, @@ -399,10 +467,8 @@ } int cxBufferTerminate(CxBuffer *buffer) { - bool success = 0 == cxBufferPut(buffer, 0); - if (success) { - buffer->pos--; - buffer->size--; + if (0 == cxBufferPut(buffer, 0)) { + buffer->size = buffer->pos - 1; return 0; } else { return -1;