Generic memory allocator (#82)

This commit is contained in:
Alex Kotov 2022-06-21 11:37:52 +03:00 committed by GitHub
parent 655efb4d71
commit 9f374475e0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 296 additions and 44 deletions

1
.gitignore vendored
View File

@ -110,6 +110,7 @@
/examples/cmdline
/examples/fprintf
/examples/fprintf_va
/examples/generic_malloc
/examples/generic_mutex
/examples/memmap
/examples/ntoa

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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 #
#################

91
examples/generic_malloc.c Normal file
View File

@ -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);
}

View File

@ -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;

View File

@ -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

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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 @@ block_found:
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_ASSERT(0); // TODO
(void)alloc;
(void)ptr;
(void)size;
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);
return NULL;
}
void KernAux_Alloc_defrag(const KernAux_Alloc alloc)

36
src/generic/malloc.c Normal file
View File

@ -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);
}

View File

@ -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];