--- a/ucx/buffer.c Fri Jan 03 21:40:57 2025 +0100 +++ b/ucx/buffer.c Sat Jan 04 13:03:01 2025 +0100 @@ -27,25 +27,41 @@ */ #include "cx/buffer.h" -#include "cx/utils.h" #include <stdio.h> #include <string.h> +#include <errno.h> + +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; + memcpy(newspace, buffer->space, buffer->size); + buffer->space = newspace; + buffer->flags &= ~CX_BUFFER_COPY_ON_WRITE; + buffer->flags |= CX_BUFFER_FREE_CONTENTS; + return 0; +} int cxBufferInit( CxBuffer *buffer, void *space, size_t capacity, - CxAllocator const *allocator, + const CxAllocator *allocator, int flags ) { - if (allocator == NULL) allocator = cxDefaultAllocator; + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } + if (flags & CX_BUFFER_COPY_ON_EXTEND) { + flags |= CX_BUFFER_AUTO_EXTEND; + } buffer->allocator = allocator; buffer->flags = flags; if (!space) { buffer->bytes = cxMalloc(allocator, capacity); if (buffer->bytes == NULL) { - return 1; + return -1; // LCOV_EXCL_LINE } buffer->flags |= CX_BUFFER_FREE_CONTENTS; } else { @@ -65,32 +81,38 @@ } void cxBufferDestroy(CxBuffer *buffer) { - if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { + if (buffer->flags & CX_BUFFER_FREE_CONTENTS) { cxFree(buffer->allocator, buffer->bytes); } + memset(buffer, 0, sizeof(CxBuffer)); } CxBuffer *cxBufferCreate( void *space, size_t capacity, - CxAllocator const *allocator, + const CxAllocator *allocator, int flags ) { + if (allocator == NULL) { + allocator = cxDefaultAllocator; + } CxBuffer *buf = cxMalloc(allocator, sizeof(CxBuffer)); if (buf == NULL) return NULL; if (0 == cxBufferInit(buf, space, capacity, allocator, flags)) { return buf; } else { + // LCOV_EXCL_START cxFree(allocator, buf); return NULL; + // LCOV_EXCL_STOP } } void cxBufferFree(CxBuffer *buffer) { - if ((buffer->flags & CX_BUFFER_FREE_CONTENTS) == CX_BUFFER_FREE_CONTENTS) { - cxFree(buffer->allocator, buffer->bytes); - } - cxFree(buffer->allocator, buffer); + if (buffer == NULL) return; + const CxAllocator *allocator = buffer->allocator; + cxBufferDestroy(buffer); + cxFree(allocator, buffer); } int cxBufferSeek( @@ -117,10 +139,11 @@ npos += offset; if ((offset > 0 && npos < opos) || (offset < 0 && npos > opos)) { + errno = EOVERFLOW; return -1; } - if (npos >= buffer->size) { + if (npos > buffer->size) { return -1; } else { buffer->pos = npos; @@ -130,7 +153,9 @@ } void cxBufferClear(CxBuffer *buffer) { - memset(buffer->bytes, 0, buffer->size); + if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) { + memset(buffer->bytes, 0, buffer->size); + } buffer->size = 0; buffer->pos = 0; } @@ -140,7 +165,7 @@ buffer->pos = 0; } -int cxBufferEof(CxBuffer const *buffer) { +bool cxBufferEof(const CxBuffer *buffer) { return buffer->pos >= buffer->size; } @@ -152,12 +177,22 @@ return 0; } - if (cxReallocate(buffer->allocator, + 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); + if (NULL == newspace) return -1; + memcpy(newspace, buffer->space, buffer->size); + buffer->space = newspace; + buffer->capacity = newcap; + buffer->flags &= ~force_copy_flags; + buffer->flags |= CX_BUFFER_FREE_CONTENTS; + return 0; + } else if (cxReallocate(buffer->allocator, (void **) &buffer->bytes, newcap) == 0) { buffer->capacity = newcap; return 0; } else { - return -1; + return -1; // LCOV_EXCL_LINE } } @@ -172,7 +207,7 @@ */ static size_t cx_buffer_write_flush_helper( CxBuffer *buffer, - unsigned char const *space, + const unsigned char *space, size_t size, size_t nitems ) { @@ -197,13 +232,14 @@ } size_t cxBufferWrite( - void const *ptr, + const void *ptr, size_t size, size_t nitems, CxBuffer *buffer ) { // optimize for easy case if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) { + if (buffer_copy_on_write(buffer)) return 0; memcpy(buffer->bytes + buffer->pos, ptr, nitems); buffer->pos += nitems; if (buffer->pos > buffer->size) { @@ -215,6 +251,7 @@ size_t len; size_t nitems_out = nitems; if (cx_szmul(size, nitems, &len)) { + errno = EOVERFLOW; return 0; } size_t required = buffer->pos + len; @@ -224,12 +261,12 @@ bool perform_flush = false; if (required > buffer->capacity) { - if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND && required) { + if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { if (buffer->flush_blkmax > 0 && required > buffer->flush_threshold) { perform_flush = true; } else { if (cxBufferMinimumCapacity(buffer, required)) { - return 0; + return 0; // LCOV_EXCL_LINE } } } else { @@ -253,6 +290,7 @@ if (perform_flush) { size_t flush_max; if (cx_szmul(buffer->flush_blkmax, buffer->flush_blksize, &flush_max)) { + errno = EOVERFLOW; return 0; } size_t flush_pos = buffer->flush_func == NULL || buffer->flush_target == NULL @@ -273,7 +311,7 @@ items_keep = nitems - items_flush; if (items_keep > 0) { // try again with the remaining stuff - unsigned char const *new_ptr = ptr; + const unsigned char *new_ptr = ptr; new_ptr += items_flush * size; // report the directly flushed items as written plus the remaining stuff return items_flush + cxBufferWrite(new_ptr, size, items_keep, buffer); @@ -290,6 +328,7 @@ return cxBufferWrite(ptr, size, nitems, buffer); } } else { + if (buffer_copy_on_write(buffer)) return 0; memcpy(buffer->bytes + buffer->pos, ptr, len); buffer->pos += len; if (buffer->pos > buffer->size) { @@ -300,6 +339,19 @@ } +size_t cxBufferAppend( + const void *ptr, + size_t size, + size_t nitems, + CxBuffer *buffer +) { + size_t pos = buffer->pos; + buffer->pos = buffer->size; + size_t written = cxBufferWrite(ptr, size, nitems, buffer); + buffer->pos = pos; + return written; +} + int cxBufferPut( CxBuffer *buffer, int c @@ -313,6 +365,17 @@ } } +int cxBufferTerminate(CxBuffer *buffer) { + bool success = 0 == cxBufferPut(buffer, 0); + if (success) { + buffer->pos--; + buffer->size--; + return 0; + } else { + return -1; + } +} + size_t cxBufferPutString( CxBuffer *buffer, const char *str @@ -328,6 +391,7 @@ ) { size_t len; if (cx_szmul(size, nitems, &len)) { + errno = EOVERFLOW; return 0; } if (buffer->pos + len > buffer->size) { @@ -362,6 +426,7 @@ if (shift >= buffer->size) { buffer->pos = buffer->size = 0; } else { + if (buffer_copy_on_write(buffer)) return -1; memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift); buffer->size -= shift; @@ -378,14 +443,18 @@ CxBuffer *buffer, size_t shift ) { + if (buffer->size > SIZE_MAX - shift) { + errno = EOVERFLOW; + return -1; + } size_t req_capacity = buffer->size + shift; size_t movebytes; // auto extend buffer, if required and enabled if (buffer->capacity < req_capacity) { - if ((buffer->flags & CX_BUFFER_AUTO_EXTEND) == CX_BUFFER_AUTO_EXTEND) { + if (buffer->flags & CX_BUFFER_AUTO_EXTEND) { if (cxBufferMinimumCapacity(buffer, req_capacity)) { - return 1; + return -1; // LCOV_EXCL_LINE } movebytes = buffer->size; } else { @@ -395,8 +464,11 @@ movebytes = buffer->size; } - memmove(buffer->bytes + shift, buffer->bytes, movebytes); - buffer->size = shift + movebytes; + if (movebytes > 0) { + if (buffer_copy_on_write(buffer)) return -1; + memmove(buffer->bytes + shift, buffer->bytes, movebytes); + buffer->size = shift + movebytes; + } buffer->pos += shift; if (buffer->pos > buffer->size) {