src/array_list.c

changeset 998
bb196054f3fd
parent 995
d3d4f245b843
child 999
84fc42b04d3b
--- a/src/array_list.c	Thu Nov 28 20:59:11 2024 +0100
+++ b/src/array_list.c	Mon Dec 02 20:58:17 2024 +0100
@@ -30,6 +30,7 @@
 #include "cx/compare.h"
 #include <assert.h>
 #include <string.h>
+#include <errno.h>
 
 // Default array reallocator
 
@@ -90,8 +91,9 @@
 
 int cx_array_copy(
         void **target,
-        size_t *size,
-        size_t *capacity,
+        void *size,
+        void *capacity,
+        unsigned width,
         size_t index,
         const void *src,
         size_t elem_size,
@@ -105,29 +107,64 @@
     assert(src != NULL);
     assert(reallocator != NULL);
 
-    // determine capacity
-    size_t cap = *capacity;
-    assert(*target != NULL || cap == 0);
+    // determine size and capacity
+    size_t oldcap;
+    size_t oldsize;
+    size_t max_size;
+    if (width == 0 || width == __WORDSIZE) {
+        oldcap = *(size_t*) capacity;
+        oldsize = *(size_t*) size;
+        max_size = SIZE_MAX;
+    } else if (width == 16) {
+        oldcap = *(uint16_t*) capacity;
+        oldsize = *(uint16_t*) size;
+        max_size = UINT16_MAX;
+    } else if (width == 8) {
+        oldcap = *(uint8_t*) capacity;
+        oldsize = *(uint8_t*) size;
+        max_size = UINT8_MAX;
+    }
+#if __WORDSIZE == 64
+    else if (width == 32) {
+        oldcap = *(uint32_t*) capacity;
+        oldsize = *(uint32_t*) size;
+        max_size = UINT32_MAX;
+    }
+#endif
+    else {
+        errno = EINVAL;
+        return 1;
+    }
+
+    // assert that the array is allocated when it has capacity
+    assert(*target != NULL || oldcap == 0);
 
     // check if resize is required
     size_t minsize = index + elem_count;
-    size_t newsize = *size < minsize ? minsize : *size;
+    size_t newsize = oldsize < minsize ? minsize : oldsize;
+
+    // check for overflow
+    if (newsize > max_size) {
+        errno = EOVERFLOW;
+        return 1;
+    }
 
     // reallocate if possible
-    if (newsize > cap) {
+    size_t newcap = oldcap;
+    if (newsize > oldcap) {
         // check, if we need to repair the src pointer
         uintptr_t targetaddr = (uintptr_t) *target;
         uintptr_t srcaddr = (uintptr_t) src;
         bool repairsrc = targetaddr <= srcaddr
-                         && srcaddr < targetaddr + cap * elem_size;
+                         && srcaddr < targetaddr + oldcap * elem_size;
 
         // calculate new capacity (next number divisible by 16)
-        cap = newsize - (newsize % 16) + 16;
-        assert(cap > newsize);
+        newcap = newsize - (newsize % 16) + 16;
+        assert(newcap > newsize);
 
         // perform reallocation
         void *newmem = reallocator->realloc(
-                *target, cap, elem_size, reallocator
+                *target, newcap, elem_size, reallocator
         );
         if (newmem == NULL) {
             return 1;
@@ -138,9 +175,8 @@
             src = ((char *) newmem) + (srcaddr - targetaddr);
         }
 
-        // store new pointer and capacity
+        // store new pointer
         *target = newmem;
-        *capacity = cap;
     }
 
     // determine target pointer
@@ -149,7 +185,26 @@
 
     // copy elements and set new size
     memmove(start, src, elem_count * elem_size);
-    *size = newsize;
+
+    // if any of size or capacity changed, store them back
+    if (newsize != oldsize || newcap != oldcap) {
+        if (width == 0 || width == __WORDSIZE) {
+            *(size_t*) capacity = newcap;
+            *(size_t*) size = newsize;
+        } else if (width == 16) {
+            *(uint16_t*) capacity = newcap;
+            *(uint16_t*) size = newsize;
+        } else if (width == 8) {
+            *(uint8_t*) capacity = newcap;
+            *(uint8_t*) size = newsize;
+        }
+#if __WORDSIZE == 64
+        else if (width == 32) {
+            *(uint32_t*) capacity = newcap;
+            *(uint32_t*) size = newsize;
+        }
+#endif
+    }
 
     // return successfully
     return 0;
@@ -458,6 +513,7 @@
                 &arl->data,
                 &list->collection.size,
                 &arl->capacity,
+                0,
                 start_of_moved,
                 first_to_move,
                 list->collection.elem_size,
@@ -478,6 +534,7 @@
             &arl->data,
             &list->collection.size,
             &arl->capacity,
+            0,
             index,
             array,
             list->collection.elem_size,
@@ -602,6 +659,7 @@
             &arl->data,
             &list->collection.size,
             &arl->capacity,
+            0,
             index,
             ((char *) arl->data) + (index + remove) * list->collection.elem_size,
             list->collection.elem_size,

mercurial