Repository: tailix/libkernaux (language: python)

Issue:
Implement realloc



Discussion / hints:

Fix patch:
diff --git a/src/free_list.c b/src/free_list.c
index 38f58c58..9a9024ea 100644
--- a/src/free_list.c
+++ b/src/free_list.c
@@ -43,7 +43,6 @@
         }                                             \
     } while (0)
 
-static void *KernAux_FreeList_calloc (void *malloc, size_t nmemb, size_t size);
 static void  KernAux_FreeList_free   (void *malloc, void *ptr);
 static void *KernAux_FreeList_malloc (void *malloc, size_t size);
 static void *KernAux_FreeList_realloc(void *malloc, void *ptr, size_t size);
@@ -73,7 +72,7 @@ void KernAux_FreeList_init(
 ) {
     KERNAUX_ASSERT(free_list);
 
-    free_list->malloc.calloc  = KernAux_FreeList_calloc;
+    free_list->malloc.calloc  = NULL;
     free_list->malloc.free    = KernAux_FreeList_free;
     free_list->malloc.malloc  = KernAux_FreeList_malloc;
     free_list->malloc.realloc = KernAux_FreeList_realloc;
@@ -123,35 +122,16 @@ void KernAux_FreeList_add_zone(
     UNLOCK(free_list);
 }
 
-void *KernAux_FreeList_calloc(
-    void *const malloc,
-    const size_t nmemb,
-    const size_t size
-) {
-    KERNAUX_ASSERT(malloc);
-
-    const size_t total_size = nmemb * size;
-    KERNAUX_ASSERT(total_size >= nmemb);
-    KERNAUX_ASSERT(total_size >= size);
-    KERNAUX_ASSERT(total_size / nmemb == size);
-
-    void *const ptr = KernAux_FreeList_malloc(malloc, total_size);
-    if (ptr) memset(ptr, 0, total_size);
-    return ptr;
-}
-
 void KernAux_FreeList_free(void *const malloc, void *const ptr)
 {
     const KernAux_FreeList free_list = malloc;
-
     KERNAUX_ASSERT(free_list);
-    if (!ptr) return;
+    KERNAUX_ASSERT(ptr);
 
     LOCK(free_list);
 
     KernAux_FreeList_Node node =
         CONTAINER_OF(ptr, struct KernAux_FreeList_Node, block);
-
     KernAux_FreeList_Node last_node = NULL;
 
     for (
@@ -183,9 +163,8 @@ void KernAux_FreeList_free(void *const malloc, void *const ptr)
 void *KernAux_FreeList_malloc(void *const malloc, const size_t size)
 {
     const KernAux_FreeList free_list = malloc;
-
     KERNAUX_ASSERT(free_list);
-    if (size == 0) return NULL;
+    KERNAUX_ASSERT(size);
 
     LOCK(free_list);
 
@@ -226,18 +205,29 @@ void *KernAux_FreeList_malloc(void *const malloc, const size_t size)
 
 void *KernAux_FreeList_realloc(
     void *const malloc,
-    void *const ptr,
-    const size_t size
+    void *const old_ptr,
+    const size_t new_size
 ) {
     const KernAux_FreeList free_list = malloc;
     KERNAUX_ASSERT(free_list);
+    KERNAUX_ASSERT(old_ptr);
+    KERNAUX_ASSERT(new_size);
+
+    LOCK(free_list);
 
-    KERNAUX_ASSERT(0); // TODO
-    (void)free_list;
-    (void)ptr;
-    (void)size;
+    KernAux_FreeList_Node node =
+        CONTAINER_OF(old_ptr, struct KernAux_FreeList_Node, block);
+    const size_t old_size = node->size - NODE_HEADER_SIZE;
+
+    void *new_ptr = KernAux_FreeList_malloc(free_list, new_size);
 
-    return NULL;
+    if (new_ptr) {
+        const size_t min_size = old_size < new_size ? old_size : new_size;
+        memcpy(new_ptr, old_ptr, min_size);
+    }
+
+    UNLOCK(free_list);
+    return new_ptr;
 }
 
 void KernAux_FreeList_defrag(const KernAux_FreeList free_list)
diff --git a/src/generic/malloc.c b/src/generic/malloc.c
index 809e489a..7de56bff 100644
--- a/src/generic/malloc.c
+++ b/src/generic/malloc.c
@@ -6,13 +6,24 @@
 #include <kernaux/generic/malloc.h>
 
 #include <stddef.h>
+#include <string.h>
 
 void *KernAux_Malloc_calloc(KernAux_Malloc malloc, size_t nmemb, size_t size)
 {
     KERNAUX_ASSERT(malloc);
-    KERNAUX_ASSERT(malloc->calloc);
 
-    return malloc->calloc((void*)malloc, nmemb, size);
+    // Common implementation
+    const size_t total_size = nmemb * size;
+    if (!total_size) return NULL;
+    if (total_size / nmemb != size) return NULL;
+
+    // Inherited implementation
+    if (malloc->calloc) return malloc->calloc((void*)malloc, nmemb, size);
+
+    // Default implementation
+    void *const ptr = KernAux_Malloc_malloc(malloc, total_size);
+    if (ptr) memset(ptr, 0, total_size);
+    return ptr;
 }
 
 void KernAux_Malloc_free(KernAux_Malloc malloc, void *ptr)
@@ -20,6 +31,10 @@ void KernAux_Malloc_free(KernAux_Malloc malloc, void *ptr)
     KERNAUX_ASSERT(malloc);
     KERNAUX_ASSERT(malloc->free);
 
+    // Common implementation
+    if (!ptr) return;
+
+    // Inherited implementation
     malloc->free((void*)malloc, ptr);
 }
 
@@ -28,6 +43,10 @@ void *KernAux_Malloc_malloc(KernAux_Malloc malloc, size_t size)
     KERNAUX_ASSERT(malloc);
     KERNAUX_ASSERT(malloc->malloc);
 
+    // Common implementation
+    if (!size) return NULL;
+
+    // Inherited implementation
     return malloc->malloc((void*)malloc, size);
 }
 
@@ -36,5 +55,13 @@ void *KernAux_Malloc_realloc(KernAux_Malloc malloc, void *ptr, size_t size)
     KERNAUX_ASSERT(malloc);
     KERNAUX_ASSERT(malloc->realloc);
 
+    // Common implementation
+    if (!ptr) return KernAux_Malloc_malloc(malloc, size);
+    if (!size) {
+        KernAux_Malloc_free(malloc, ptr);
+        return NULL;
+    }
+
+    // Inherited implementation
     return malloc->realloc((void*)malloc, ptr, size);
 }


Test patch:
diff --git a/tests/test_free_list.c b/tests/test_free_list.c
index 9c632d5a..b3450814 100644
--- a/tests/test_free_list.c
+++ b/tests/test_free_list.c
@@ -8,19 +8,55 @@
 
 #include <assert.h>
 #include <stddef.h>
+#include <stdint.h>
 #include <string.h>
 
 static void test_default();
+static void test_cross_zone_defrag();
+
 static void test_calloc();
 static void test_calloc_nomem();
-static void test_cross_zone_defrag();
+static void test_calloc_overflow();
+static void test_calloc_zero();
+
+static void test_realloc_alloc();
+static void test_realloc_free();
+static void test_realloc_memcpy();
+static void test_realloc_increase();
+static void test_realloc_decrease();
+
+static size_t nodes_count(KernAux_FreeList free_list);
+
+static const char *const hello = "Hello, World!";
 
 void test_main()
 {
     test_default();
+    test_cross_zone_defrag();
+
     test_calloc();
     test_calloc_nomem();
-    test_cross_zone_defrag();
+    test_calloc_overflow();
+    test_calloc_zero();
+
+    test_realloc_alloc();
+    test_realloc_free();
+    test_realloc_memcpy();
+    test_realloc_increase();
+    test_realloc_decrease();
+}
+
+size_t nodes_count(const KernAux_FreeList free_list)
+{
+    size_t nodes_count = 0;
+    for (
+        KernAux_FreeList_Node item_node = free_list->head;
+        item_node;
+        item_node = item_node->next
+    ) {
+        ++nodes_count;
+    }
+    return nodes_count;
 }
 
 void test_default()
@@ -65,6 +101,16 @@ void test_default()
     assert(ptr7 == ptr2);
 }
 
+void test_cross_zone_defrag()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, &zone[0],   500);
+    KernAux_FreeList_add_zone(&free_list, &zone[500], 500);
+
+    assert(nodes_count(&free_list) == 1);
+}
+
 void test_calloc()
 {
     char zone[1000];
@@ -86,21 +132,113 @@ void test_calloc_nomem()
     assert(ptr == NULL);
 }
 
-void test_cross_zone_defrag()
+void test_calloc_overflow()
 {
     char zone[1000];
     struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
-    KernAux_FreeList_add_zone(&free_list, &zone[0],   500);
-    KernAux_FreeList_add_zone(&free_list, &zone[500], 500);
-
-    size_t nodes_count = 0;
-    for (
-        KernAux_FreeList_Node item_node = free_list.head;
-        item_node;
-        item_node = item_node->next
-    ) {
-        ++nodes_count;
+    KernAux_FreeList_add_zone(&free_list, zone, SIZE_MAX);
+    {
+        void *const ptr = KernAux_Malloc_calloc(&free_list.malloc, 2, SIZE_MAX);
+        assert(ptr == NULL);
+    }
+    {
+        void *const ptr = KernAux_Malloc_calloc(&free_list.malloc, SIZE_MAX, 2);
+        assert(ptr == NULL);
     }
+}
+
+void test_calloc_zero()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+    void *const ptr1 = KernAux_Malloc_calloc(&free_list.malloc, 0, 900);
+    assert(ptr1 == NULL);
+    void *const ptr2 = KernAux_Malloc_calloc(&free_list.malloc, 900, 0);
+    assert(ptr2 == NULL);
+}
+
+void test_realloc_alloc()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+    void *const ptr = KernAux_Malloc_realloc(&free_list.malloc, NULL, 900);
+    assert(ptr != NULL);
+}
+
+void test_realloc_free()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+
+    void *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 900);
+    assert(ptr1 != NULL);
+
+    void *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 0);
+    assert(ptr2 == NULL);
+
+    void *const ptr3 = KernAux_Malloc_malloc(&free_list.malloc, 900);
+    assert(ptr3 != NULL);
+}
+
+void test_realloc_memcpy()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+
+    char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 400);
+    assert(ptr1 != NULL);
+
+    memset(ptr1, 0, 400);
+    strcpy(ptr1, hello);
+    strcpy(&ptr1[400 - strlen(hello) - 1], hello);
+
+    char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 400);
+    assert(ptr2 != NULL);
+
+    assert(strcmp(ptr2, hello) == 0);
+    assert(strcmp(&ptr2[400 - strlen(hello) - 1], hello) == 0);
+}
+
+void test_realloc_increase()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+
+    char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 300);
+    assert(ptr1 != NULL);
+
+    memset(ptr1, 0, 300);
+    strcpy(ptr1, hello);
+    strcpy(&ptr1[300 - strlen(hello) - 1], hello);
+
+    char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 500);
+    assert(ptr2 != NULL);
+
+    assert(strcmp(ptr2, hello) == 0);
+    assert(strcmp(&ptr2[300 - strlen(hello) - 1], hello) == 0);
+}
+
+void test_realloc_decrease()
+{
+    char zone[1000];
+    struct KernAux_FreeList free_list = KernAux_FreeList_create(NULL);
+    KernAux_FreeList_add_zone(&free_list, zone, sizeof(zone));
+
+    char *const ptr1 = KernAux_Malloc_malloc(&free_list.malloc, 500);
+    assert(ptr1 != NULL);
+
+    memset(ptr1, 0, 300);
+    strcpy(ptr1, hello);
+    strcpy(&ptr1[300 - strlen(hello) - 1], hello);
+
+    char *const ptr2 = KernAux_Malloc_realloc(&free_list.malloc, ptr1, 300);
+    assert(ptr2 != NULL);
 
-    assert(nodes_count == 1);
+    assert(strcmp(ptr2, hello) == 0);
+    assert(strcmp(&ptr2[300 - strlen(hello) - 1], hello) == 0);
 }