mirror of
https://github.com/tailix/libkernaux.git
synced 2025-04-14 17:32:55 -04:00
Generic memory allocator (#82)
This commit is contained in:
parent
655efb4d71
commit
9f374475e0
14 changed files with 296 additions and 44 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -110,6 +110,7 @@
|
|||
/examples/cmdline
|
||||
/examples/fprintf
|
||||
/examples/fprintf_va
|
||||
/examples/generic_malloc
|
||||
/examples/generic_mutex
|
||||
/examples/memmap
|
||||
/examples/ntoa
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
91
examples/generic_malloc.c
Normal 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);
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
33
include/kernaux/generic/malloc.h
Normal file
33
include/kernaux/generic/malloc.h
Normal 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
|
107
src/alloc.c
107
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 @@ 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
36
src/generic/malloc.c
Normal 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);
|
||||
}
|
|
@ -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];
|
||||
|
|
Loading…
Add table
Reference in a new issue