src/buffer.c

changeset 1024
8f99f6c28bd3
parent 1007
81b2986d2b04
--- a/src/buffer.c	Sun Dec 15 15:23:29 2024 +0100
+++ b/src/buffer.c	Wed Dec 18 15:35:42 2024 +0100
@@ -31,6 +31,19 @@
 #include <stdio.h>
 #include <string.h>
 
+static int buffer_copy_on_write(CxBuffer* buffer, size_t newcap) {
+    if (0 == (buffer->flags & CX_BUFFER_COPY_ON_WRITE)) return 0;
+    if (newcap == 0) newcap = buffer->capacity;
+    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 &= ~CX_BUFFER_COPY_ON_WRITE;
+    buffer->flags |= CX_BUFFER_FREE_CONTENTS;
+    return 0;
+}
+
 int cxBufferInit(
         CxBuffer *buffer,
         void *space,
@@ -46,7 +59,7 @@
     if (!space) {
         buffer->bytes = cxMalloc(allocator, capacity);
         if (buffer->bytes == NULL) {
-            return 1;
+            return -1;
         }
         buffer->flags |= CX_BUFFER_FREE_CONTENTS;
     } else {
@@ -66,7 +79,7 @@
 }
 
 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);
     }
 }
@@ -133,7 +146,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;
 }
@@ -155,7 +170,9 @@
         return 0;
     }
 
-    if (cxReallocate(buffer->allocator,
+    if (buffer->flags & CX_BUFFER_COPY_ON_WRITE) {
+        return buffer_copy_on_write(buffer, newcap);
+    } else if (cxReallocate(buffer->allocator,
                      (void **) &buffer->bytes, newcap) == 0) {
         buffer->capacity = newcap;
         return 0;
@@ -207,6 +224,7 @@
 ) {
     // optimize for easy case
     if (size == 1 && (buffer->capacity - buffer->pos) >= nitems) {
+        if (buffer_copy_on_write(buffer, 0)) return 0;
         memcpy(buffer->bytes + buffer->pos, ptr, nitems);
         buffer->pos += nitems;
         if (buffer->pos > buffer->size) {
@@ -227,7 +245,7 @@
 
     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 {
@@ -293,6 +311,7 @@
             return cxBufferWrite(ptr, size, nitems, buffer);
         }
     } else {
+        if (buffer_copy_on_write(buffer, 0)) return 0;
         memcpy(buffer->bytes + buffer->pos, ptr, len);
         buffer->pos += len;
         if (buffer->pos > buffer->size) {
@@ -323,7 +342,7 @@
         buffer->size--;
         return 0;
     } else {
-        return 1;
+        return -1;
     }
 }
 
@@ -376,6 +395,7 @@
     if (shift >= buffer->size) {
         buffer->pos = buffer->size = 0;
     } else {
+        if (buffer_copy_on_write(buffer, 0)) return -1;
         memmove(buffer->bytes, buffer->bytes + shift, buffer->size - shift);
         buffer->size -= shift;
 
@@ -397,9 +417,9 @@
 
     // 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;
             }
             movebytes = buffer->size;
         } else {
@@ -409,8 +429,11 @@
         movebytes = buffer->size;
     }
 
-    memmove(buffer->bytes + shift, buffer->bytes, movebytes);
-    buffer->size = shift + movebytes;
+    if (movebytes > 0) {
+        if (buffer_copy_on_write(buffer, 0)) return -1;
+        memmove(buffer->bytes + shift, buffer->bytes, movebytes);
+        buffer->size = shift + movebytes;
+    }
 
     buffer->pos += shift;
     if (buffer->pos > buffer->size) {

mercurial