Repository: tailix/libkernaux (language: python)

Issue:
Generic memory allocator



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 7a3a7aa7..fa56d74d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -110,6 +110,7 @@
 /examples/cmdline
 /examples/fprintf
 /examples/fprintf_va
+/examples/generic_malloc
 /examples/generic_mutex
 /examples/memmap
 /examples/ntoa
diff --git a/ChangeLog b/ChangeLog
index dddbe4ce..ceab3fbb 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2022-06-21  Alex Kotov  <kotovalexarian@gmail.com>
+
+	* include/kernaux/generic/malloc.h: Added
+
 2022-06-20  Alex Kotov  <kotovalexarian@gmail.com>
 
 	* include/kernaux/alloc.h: Finished
diff --git a/Makefile.am b/Makefile.am
index 859cf7d0..9a3ed72d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -27,6 +27,7 @@ libkernaux_la_LIBADD =
 libkernaux_la_SOURCES = \
 	src/assert.c \
 	src/libc.h \
+	src/generic/malloc.c \
 	src/generic/mutex.c
 
 if ASM_I386
diff --git a/README.md b/README.md
index 244aff6a..3f6e8861 100644
--- a/README.md
+++ b/README.md
@@ -49,6 +49,8 @@ zero). Work-in-progress APIs can change at any time.
     * [Declarations](/include/kernaux/arch/)
     * [Functions](/include/kernaux/asm/)
 * Generic types
+  * [Memory allocator](/include/kernaux/generic/malloc.h) (*non-breaking since* **?.?.?**)
+    * [Example](/examples/generic_malloc.c)
   * [Mutex](/include/kernaux/generic/mutex.h) (*non-breaking since* **?.?.?**)
     * [Example](/examples/generic_mutex.c)
 * Device drivers (for debugging only)
diff --git a/examples/Makefile.am b/examples/Makefile.am
index 79740d22..880486af 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -45,6 +45,14 @@ fprintf_va_SOURCES = main.c fprintf_va.c
 endif
 endif
 
+##################
+# generic_malloc #
+##################
+
+TESTS += generic_malloc
+generic_malloc_LDADD = $(top_builddir)/libkernaux.la
+generic_malloc_SOURCES = main.c generic_malloc.c
+
 #################
 # generic_mutex #
 #################
diff --git a/examples/generic_malloc.c b/examples/generic_malloc.c
new file mode 100644
index 00000000..cda73632
--- /dev/null
+++ b/examples/generic_malloc.c
@@ -0,0 +1,91 @@
+//=============
+// my_malloc.c
+//=============
+
+// To not always use macro "KERNAUX_PROTECTED_FIELD" around the names of
+// structure fields you may define "KERNAUX_ACCESS_PROTECTED" before including
+// any other headers, but ONLY in the file where you implement a generic type.
+//
+#define KERNAUX_ACCESS_PROTECTED
+
+//=============
+// my_malloc.h
+//=============
+
+#include <kernaux/generic/malloc.h>
+
+typedef struct MyMalloc {
+    struct KernAux_Malloc malloc;
+} *MyMalloc;
+
+struct MyMalloc MyMalloc_create();
+
+//=============
+// my_malloc.c
+//=============
+
+#include <kernaux/generic/malloc.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+static void *MyMalloc_calloc (void *malloc, size_t nmemb, size_t size);
+static void  MyMalloc_free   (void *malloc, void *ptr);
+static void *MyMalloc_malloc (void *malloc, size_t size);
+static void *MyMalloc_realloc(void *malloc, void *ptr, size_t size);
+
+struct MyMalloc MyMalloc_create()
+{
+    struct MyMalloc my_malloc;
+    my_malloc.malloc.calloc  = MyMalloc_calloc;
+    my_malloc.malloc.free    = MyMalloc_free;
+    my_malloc.malloc.malloc  = MyMalloc_malloc;
+    my_malloc.malloc.realloc = MyMalloc_realloc;
+    return my_malloc;
+}
+
+void *MyMalloc_calloc(void *const malloc, const size_t nmemb, const size_t size)
+{
+    (void)malloc; // unused
+    return calloc(nmemb, size);
+}
+
+void MyMalloc_free(void *const malloc, void *const ptr)
+{
+    (void)malloc; // unused
+    free(ptr);
+}
+
+void *MyMalloc_malloc(void *const malloc_, const size_t size)
+{
+    (void)malloc_; // unused
+    return malloc(size);
+}
+
+void *MyMalloc_realloc(void *const malloc, void *const ptr, const size_t size)
+{
+    (void)malloc; // unused
+    return realloc(ptr, size);
+}
+
+//========
+// main.c
+//========
+
+#include <assert.h>
+#include <string.h>
+
+void example_main()
+{
+    // Create memory allocator
+    struct MyMalloc my_malloc = MyMalloc_create();
+
+    // Allocate memory
+    void *ptr = KernAux_Malloc_malloc(&my_malloc.malloc, 1000);
+
+    // Use memory
+    strcpy(ptr, "Hello, World!");
+    assert(strcmp(ptr, "Hello, World!") == 0);
+
+    // Free memory
+    KernAux_Malloc_free(&my_malloc.malloc, ptr);
+}
diff --git a/examples/generic_mutex.c b/examples/generic_mutex.c
index 370bbbb4..7da30441 100644
--- a/examples/generic_mutex.c
+++ b/examples/generic_mutex.c
@@ -36,7 +36,7 @@ static void MyMutex_unlock(void *mutex);
 struct MyMutex MyMutex_create()
 {
     struct MyMutex my_mutex;
-    my_mutex.mutex.lock = MyMutex_lock;
+    my_mutex.mutex.lock   = MyMutex_lock;
     my_mutex.mutex.unlock = MyMutex_unlock;
     if (pthread_mutex_init(&my_mutex.pthread_mutex, NULL) != 0) abort();
     return my_mutex;
diff --git a/include/Makefile.am b/include/Makefile.am
index 16539f65..ffe30c3d 100644
--- a/include/Makefile.am
+++ b/include/Makefile.am
@@ -6,6 +6,7 @@ nobase_include_HEADERS = \
 	kernaux/assert.h \
 	kernaux/macro.h \
 	kernaux/version.h \
+	kernaux/generic/malloc.h \
 	kernaux/generic/mutex.h
 
 if ASM_I386
diff --git a/include/kernaux.h.in b/include/kernaux.h.in
index 834e462d..be4340d7 100644
--- a/include/kernaux.h.in
+++ b/include/kernaux.h.in
@@ -7,6 +7,7 @@
 #include <kernaux/macro.h>
 #include <kernaux/version.h>
 
+#include <kernaux/generic/malloc.h>
 #include <kernaux/generic/mutex.h>
 
 @comment_line_alloc@#include <kernaux/alloc.h>
diff --git a/include/kernaux/alloc.h b/include/kernaux/alloc.h
index 15c25bc1..604e3e6e 100644
--- a/include/kernaux/alloc.h
+++ b/include/kernaux/alloc.h
@@ -6,6 +6,7 @@ extern "C" {
 #endif
 
 #include <kernaux/macro.h>
+#include <kernaux/generic/malloc.h>
 #include <kernaux/generic/mutex.h>
 
 #include <stddef.h>
@@ -18,6 +19,7 @@ typedef struct KernAux_Alloc_Node {
 } *KernAux_Alloc_Node;
 
 typedef struct KernAux_Alloc {
+    struct KernAux_Malloc malloc;
     KernAux_Mutex KERNAUX_PRIVATE_FIELD(mutex);
     KernAux_Alloc_Node KERNAUX_PRIVATE_FIELD(head);
 } *KernAux_Alloc;
@@ -27,9 +29,6 @@ void KernAux_Alloc_init(KernAux_Alloc alloc, KernAux_Mutex mutex);
 
 void KernAux_Alloc_add_zone(KernAux_Alloc alloc, void *ptr, size_t size);
 
-void *KernAux_Alloc_malloc(KernAux_Alloc alloc, size_t size);
-void  KernAux_Alloc_free  (KernAux_Alloc alloc, void *ptr);
-
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/kernaux/generic/malloc.h b/include/kernaux/generic/malloc.h
new file mode 100644
index 00000000..5a3bc741
--- /dev/null
+++ b/include/kernaux/generic/malloc.h
@@ -0,0 +1,33 @@
+#ifndef KERNAUX_INCLUDED_MALLOC
+#define KERNAUX_INCLUDED_MALLOC
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <kernaux/macro.h>
+
+#include <stddef.h>
+
+typedef void *(*KernAux_Malloc_Calloc) (void *malloc, size_t nmemb, size_t size);
+typedef void  (*KernAux_Malloc_Free)   (void *malloc, void *ptr);
+typedef void *(*KernAux_Malloc_Malloc) (void *malloc, size_t size);
+typedef void *(*KernAux_Malloc_Realloc)(void *malloc, void *ptr, size_t size);
+
+typedef struct KernAux_Malloc {
+    KernAux_Malloc_Calloc  KERNAUX_PROTECTED_FIELD(calloc);
+    KernAux_Malloc_Free    KERNAUX_PROTECTED_FIELD(free);
+    KernAux_Malloc_Malloc  KERNAUX_PROTECTED_FIELD(malloc);
+    KernAux_Malloc_Realloc KERNAUX_PROTECTED_FIELD(realloc);
+} *KernAux_Malloc;
+
+void *KernAux_Malloc_malloc (KernAux_Malloc malloc, size_t size);
+void  KernAux_Malloc_free   (KernAux_Malloc malloc, void *ptr);
+void *KernAux_Malloc_calloc (KernAux_Malloc malloc, size_t nmemb, size_t size);
+void *KernAux_Malloc_realloc(KernAux_Malloc malloc, void *ptr, size_t size);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/alloc.c b/src/alloc.c
index b720ae83..7c910de9 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -10,9 +10,12 @@
 
 #include <kernaux/alloc.h>
 #include <kernaux/assert.h>
+#include <kernaux/generic/malloc.h>
+#include <kernaux/generic/mutex.h>
 
 #include <stddef.h>
 #include <stdint.h>
+#include <string.h>
 
 #define NODE_HEADER_SIZE (offsetof(struct KernAux_Alloc_Node, block))
 #define MIN_ZONE_SIZE (2 * NODE_HEADER_SIZE)
@@ -39,6 +42,11 @@
         }                                         \
     } while (0)
 
+static void *KernAux_Alloc_calloc (void *malloc, size_t nmemb, size_t size);
+static void  KernAux_Alloc_free   (void *malloc, void *ptr);
+static void *KernAux_Alloc_malloc (void *malloc, size_t size);
+static void *KernAux_Alloc_realloc(void *malloc, void *ptr, size_t size);
+
 static void KernAux_Alloc_defrag(KernAux_Alloc alloc);
 static void KernAux_Alloc_insert(
     KernAux_Alloc alloc,
@@ -51,6 +59,10 @@ static void KernAux_Alloc_remove(KernAux_Alloc alloc, KernAux_Alloc_Node node);
 struct KernAux_Alloc KernAux_Alloc_create(const KernAux_Mutex mutex)
 {
     struct KernAux_Alloc alloc;
+    alloc.malloc.calloc  = KernAux_Alloc_calloc;
+    alloc.malloc.free    = KernAux_Alloc_free;
+    alloc.malloc.malloc  = KernAux_Alloc_malloc;
+    alloc.malloc.realloc = KernAux_Alloc_realloc;
     KernAux_Alloc_init(&alloc, mutex);
     return alloc;
 }
@@ -105,8 +117,62 @@ void KernAux_Alloc_add_zone(
     UNLOCK(alloc);
 }
 
-void *KernAux_Alloc_malloc(const KernAux_Alloc alloc, const size_t size)
+void *KernAux_Alloc_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_Alloc_malloc(malloc, total_size);
+    if (ptr) memset(ptr, 0, total_size);
+    return ptr;
+}
+
+void KernAux_Alloc_free(void *const malloc, void *const ptr)
 {
+    const KernAux_Alloc alloc = malloc;
+
+    KERNAUX_ASSERT(alloc);
+    if (!ptr) return;
+
+    LOCK(alloc);
+
+    KernAux_Alloc_Node node =
+        CONTAINER_OF(ptr, struct KernAux_Alloc_Node, block);
+
+    KernAux_Alloc_Node last_node = NULL;
+
+    for (
+        KernAux_Alloc_Node item_node = alloc->head;
+        item_node;
+        item_node = item_node->next
+    ) {
+        last_node = item_node;
+
+        if (item_node > node) {
+            KernAux_Alloc_insert(alloc, node, item_node->prev, item_node);
+            goto block_added;
+        }
+    }
+
+    KernAux_Alloc_insert(alloc, node, last_node, NULL);
+
+block_added:
+    KernAux_Alloc_defrag(alloc);
+
+    UNLOCK(alloc);
+}
+
+void *KernAux_Alloc_malloc(void *const malloc, const size_t size)
+{
+    const KernAux_Alloc alloc = malloc;
+
     KERNAUX_ASSERT(alloc);
     if (size == 0) return NULL;
 
@@ -147,37 +213,20 @@ void *KernAux_Alloc_malloc(const KernAux_Alloc alloc, const size_t size)
     }
 }
 
-void KernAux_Alloc_free(const KernAux_Alloc alloc, void *const ptr)
-{
+void *KernAux_Alloc_realloc(
+    void *const malloc,
+    void *const ptr,
+    const size_t size
+) {
+    const KernAux_Alloc alloc = malloc;
     KERNAUX_ASSERT(alloc);
-    if (!ptr) return;
-
-    LOCK(alloc);
 
-    KernAux_Alloc_Node node =
-        CONTAINER_OF(ptr, struct KernAux_Alloc_Node, block);
+    KERNAUX_ASSERT(0); // TODO
+    (void)alloc;
+    (void)ptr;
+    (void)size;
 
-    KernAux_Alloc_Node last_node = NULL;
-
-    for (
-        KernAux_Alloc_Node item_node = alloc->head;
-        item_node;
-        item_node = item_node->next
-    ) {
-        last_node = item_node;
-
-        if (item_node > node) {
-            KernAux_Alloc_insert(alloc, node, item_node->prev, item_node);
-            goto block_added;
-        }
-    }
-
-    KernAux_Alloc_insert(alloc, node, last_node, NULL);
-
-block_added:
-    KernAux_Alloc_defrag(alloc);
-
-    UNLOCK(alloc);
+    return NULL;
 }
 
 void KernAux_Alloc_defrag(const KernAux_Alloc alloc)
diff --git a/src/generic/malloc.c b/src/generic/malloc.c
new file mode 100644
index 00000000..3ac1b89c
--- /dev/null
+++ b/src/generic/malloc.c
@@ -0,0 +1,36 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <kernaux/assert.h>
+#include <kernaux/generic/malloc.h>
+
+#include <stddef.h>
+
+void *KernAux_Malloc_calloc(KernAux_Malloc malloc, size_t nmemb, size_t size)
+{
+    KERNAUX_ASSERT(malloc);
+
+    return malloc->calloc(malloc, nmemb, size);
+}
+
+void KernAux_Malloc_free(KernAux_Malloc malloc, void *ptr)
+{
+    KERNAUX_ASSERT(malloc);
+
+    malloc->free(malloc, ptr);
+}
+
+void *KernAux_Malloc_malloc(KernAux_Malloc malloc, size_t size)
+{
+    KERNAUX_ASSERT(malloc);
+
+    return malloc->malloc(malloc, size);
+}
+
+void *KernAux_Malloc_realloc(KernAux_Malloc malloc, void *ptr, size_t size)
+{
+    KERNAUX_ASSERT(malloc);
+
+    return malloc->realloc(malloc, ptr, size);
+}


Test patch:
diff --git a/tests/test_alloc.c b/tests/test_alloc.c
index 420bc8e3..9fc51d7f 100644
--- a/tests/test_alloc.c
+++ b/tests/test_alloc.c
@@ -8,13 +8,18 @@
 
 #include <assert.h>
 #include <stddef.h>
+#include <string.h>
 
 static void test_default();
+static void test_calloc();
+static void test_calloc_nomem();
 static void test_cross_zone_defrag();
 
 void test_main()
 {
     test_default();
+    test_calloc();
+    test_calloc_nomem();
     test_cross_zone_defrag();
 }
 
@@ -24,42 +29,63 @@ void test_default()
     struct KernAux_Alloc alloc = KernAux_Alloc_create(NULL);
     KernAux_Alloc_add_zone(&alloc, memory_block, sizeof(memory_block));
 
-    char *const ptr1 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr1 = KernAux_Malloc_malloc(&alloc.malloc, 100);
     assert(ptr1);
     assert(ptr1 > memory_block);
     assert(ptr1 < &memory_block[1000]);
 
-    char *const ptr2 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr2 = KernAux_Malloc_calloc(&alloc.malloc, 100, 1);
     assert(ptr2);
     assert(ptr2 > ptr1);
     assert(ptr2 < &memory_block[1000]);
 
-    char *const ptr3 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr3 = KernAux_Malloc_calloc(&alloc.malloc, 1, 100);
     assert(ptr3);
     assert(ptr3 > ptr2);
     assert(ptr3 < &memory_block[1000]);
 
-    char *const ptr4 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr4 = KernAux_Malloc_malloc(&alloc.malloc, 100);
     assert(ptr4);
     assert(ptr4 > ptr3);
     assert(ptr4 < &memory_block[1000]);
 
-    KernAux_Alloc_free(&alloc, ptr2);
-    KernAux_Alloc_free(&alloc, ptr3);
+    KernAux_Malloc_free(&alloc.malloc, ptr2);
+    KernAux_Malloc_free(&alloc.malloc, ptr3);
 
-    char *const ptr5 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr5 = KernAux_Malloc_malloc(&alloc.malloc, 100);
     assert(ptr5 == ptr2);
 
-    char *const ptr6 = KernAux_Alloc_malloc(&alloc, 100);
+    char *const ptr6 = KernAux_Malloc_calloc(&alloc.malloc, 10, 10);
     assert(ptr6 == ptr3);
 
-    KernAux_Alloc_free(&alloc, ptr2);
-    KernAux_Alloc_free(&alloc, ptr3);
+    KernAux_Malloc_free(&alloc.malloc, ptr2);
+    KernAux_Malloc_free(&alloc.malloc, ptr3);
 
-    char *const ptr7 = KernAux_Alloc_malloc(&alloc, 200);
+    char *const ptr7 = KernAux_Malloc_malloc(&alloc.malloc, 200);
     assert(ptr7 == ptr2);
 }
 
+void test_calloc()
+{
+    char zone[1000];
+    struct KernAux_Alloc alloc = KernAux_Alloc_create(NULL);
+    KernAux_Alloc_add_zone(&alloc, zone, 1000);
+    char *const ptr = KernAux_Malloc_calloc(&alloc.malloc, 1, 900);
+    for (size_t index = 0; index < 900; ++index) {
+        assert(ptr[index] == 0);
+    }
+    KernAux_Malloc_free(&alloc.malloc, ptr);
+}
+
+void test_calloc_nomem()
+{
+    char zone[1000];
+    struct KernAux_Alloc alloc = KernAux_Alloc_create(NULL);
+    KernAux_Alloc_add_zone(&alloc, zone, sizeof(zone));
+    void *const ptr = KernAux_Malloc_calloc(&alloc.malloc, 1, sizeof(zone));
+    assert(ptr == NULL);
+}
+
 void test_cross_zone_defrag()
 {
     char zone[1000];