ucx/buffer.c

changeset 11
0aa8cbd7912e
parent 0
1a157da63d7c
child 16
04c9f8d8f03b
--- 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) {

mercurial