libc.obj: fixes and optimizations for allocator && add new samples to sh build
fix: in `__mem_MERGE_MEM_NODES` `base->free = base->size`, its wrong. forgot to set size for new block optimization: add `__last_mem_node`. usually its node with max free space among other nodes. firstly `malloc` try find space in it. update(fix and optimizations) `realloc`. now sdltest is working!
This commit is contained in:
@@ -23,5 +23,7 @@ cp clayer/logo.png /tmp0/1/tcc_samples/logo.png
|
|||||||
../tcc defgen.c -o /tmp0/1/tcc_samples/defgen
|
../tcc defgen.c -o /tmp0/1/tcc_samples/defgen
|
||||||
../tcc pipe.c -o /tmp0/1/tcc_samples/pipe
|
../tcc pipe.c -o /tmp0/1/tcc_samples/pipe
|
||||||
../tcc futex.c -o /tmp0/1/tcc_samples/futex
|
../tcc futex.c -o /tmp0/1/tcc_samples/futex
|
||||||
|
../tcc malloc_test.c -o /tmp0/1/tcc_samples/malloc_test
|
||||||
|
../tcc atexit_test.c -o /tmp0/1/tcc_samples/atexit_test
|
||||||
"/sys/File managers/Eolite" /tmp0/1/tcc_samples
|
"/sys/File managers/Eolite" /tmp0/1/tcc_samples
|
||||||
exit
|
exit
|
||||||
|
|||||||
@@ -16,11 +16,16 @@
|
|||||||
} while (0)
|
} while (0)
|
||||||
|
|
||||||
// c behind a and b
|
// c behind a and b
|
||||||
#define IN_RANGE(a, b, c) (a > c && c > b)
|
#define IN_RANGE(a, b, c, len) ((a > c && c > b) || ((a > c + len && c + len > b)))
|
||||||
|
|
||||||
bool test_malloc_basic_allocation()
|
bool test_malloc_basic_allocation()
|
||||||
{
|
{
|
||||||
return malloc(sizeof(int));
|
void* ptr = malloc(sizeof(int));
|
||||||
|
|
||||||
|
if (ptr)
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
bool test_malloc_zero_bytes()
|
bool test_malloc_zero_bytes()
|
||||||
{
|
{
|
||||||
@@ -29,24 +34,25 @@ bool test_malloc_zero_bytes()
|
|||||||
|
|
||||||
bool test_malloc_multiple_allocations()
|
bool test_malloc_multiple_allocations()
|
||||||
{
|
{
|
||||||
void* ptr[1024];
|
void* ptr[512];
|
||||||
|
|
||||||
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||||||
ptr[i] = malloc(i);
|
ptr[i] = malloc(i);
|
||||||
if (ptr[i] == NULL) {
|
if (ptr[i] == NULL) {
|
||||||
printf("fail alloc %d bytes\n", i);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||||||
for (int j = i + 1; j < sizeof(ptr) / sizeof(*ptr) - i - 1; j++) {
|
for (int j = 1; j < sizeof(ptr) / sizeof(*ptr); j++) {
|
||||||
|
if (i != j) {
|
||||||
if (ptr[i] == ptr[j]) {
|
if (ptr[i] == ptr[j]) {
|
||||||
printf("ptrs[%d] == ptrs[%d].\n", i, j);
|
printf("ptrs[%d] == ptrs[%d].\n", i, j);
|
||||||
return false;
|
return false;
|
||||||
} else if (IN_RANGE(
|
} else if (IN_RANGE(
|
||||||
GET_MEM_NODE_HEADER(ptr[i])->size + (char*)GET_MEM_NODE_HEADER(ptr[i]),
|
(char*)GET_MEM_NODE_HEADER(ptr[i]) + GET_MEM_NODE_HEADER(ptr[i])->size,
|
||||||
(char*)ptr[i],
|
(char*)GET_MEM_NODE_HEADER(ptr[i]),
|
||||||
(char*)GET_MEM_NODE_HEADER(ptr[j]))) {
|
(char*)GET_MEM_NODE_HEADER(ptr[j]),
|
||||||
|
GET_MEM_NODE_HEADER(ptr[j])->size)) {
|
||||||
printf("node %p in node %p", GET_MEM_NODE_HEADER(ptr[i]), GET_MEM_NODE_HEADER(ptr[j]));
|
printf("node %p in node %p", GET_MEM_NODE_HEADER(ptr[i]), GET_MEM_NODE_HEADER(ptr[j]));
|
||||||
// additional info
|
// additional info
|
||||||
printf("node %p\nsize:%p\nfree:%p\nnext: %p\nlast: %p\n", GET_MEM_NODE_HEADER(ptr[i]), GET_MEM_NODE_HEADER(ptr[i])->size, GET_MEM_NODE_HEADER(ptr[i])->free, GET_MEM_NODE_HEADER(ptr[i])->next, GET_MEM_NODE_HEADER(ptr[i])->last);
|
printf("node %p\nsize:%p\nfree:%p\nnext: %p\nlast: %p\n", GET_MEM_NODE_HEADER(ptr[i]), GET_MEM_NODE_HEADER(ptr[i])->size, GET_MEM_NODE_HEADER(ptr[i])->free, GET_MEM_NODE_HEADER(ptr[i])->next, GET_MEM_NODE_HEADER(ptr[i])->last);
|
||||||
@@ -55,11 +61,19 @@ bool test_malloc_multiple_allocations()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||||||
|
free(ptr[i]);
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
bool test_malloc_data_integrity()
|
bool test_malloc_data_integrity()
|
||||||
{
|
{
|
||||||
|
const char* As = "AAA";
|
||||||
|
const char* Cs = "CCC";
|
||||||
|
|
||||||
char* A = (char*)malloc(10);
|
char* A = (char*)malloc(10);
|
||||||
char* B = (char*)malloc(10);
|
char* B = (char*)malloc(10);
|
||||||
char* C = (char*)malloc(10);
|
char* C = (char*)malloc(10);
|
||||||
@@ -72,18 +86,18 @@ bool test_malloc_data_integrity()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
strcpy(A, "AAA");
|
strcpy(A, As);
|
||||||
strcpy(C, "CCC");
|
strcpy(C, Cs);
|
||||||
|
|
||||||
free(B);
|
free(B);
|
||||||
|
|
||||||
if (strcmp(A, "AAA") != 0) {
|
if (strcmp(A, As) != 0) {
|
||||||
printf("A data is broken after free(B). A = '%s'\n", A);
|
printf("A data is broken after free(B). A = '%s'\n", A);
|
||||||
free(A);
|
free(A);
|
||||||
free(C);
|
free(C);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (strcmp(C, "CCC") != 0) {
|
if (strcmp(C, Cs) != 0) {
|
||||||
printf("C data is broken after free(B). C = '%s'\n", C);
|
printf("C data is broken after free(B). C = '%s'\n", C);
|
||||||
free(A);
|
free(A);
|
||||||
free(C);
|
free(C);
|
||||||
@@ -96,7 +110,12 @@ bool test_malloc_data_integrity()
|
|||||||
}
|
}
|
||||||
bool test_malloc_large_allocation()
|
bool test_malloc_large_allocation()
|
||||||
{
|
{
|
||||||
return malloc(1024 * 1024 * 8); // alloc 4mb
|
void* ptr = malloc(1024 * 1024 * 16); // alloc 16mb
|
||||||
|
|
||||||
|
if (ptr)
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
return ptr;
|
||||||
}
|
}
|
||||||
bool test_malloc_allocation_and_free()
|
bool test_malloc_allocation_and_free()
|
||||||
{
|
{
|
||||||
@@ -104,6 +123,160 @@ bool test_malloc_allocation_and_free()
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void fill_buffer(void *ptr, size_t size, unsigned char pattern) {
|
||||||
|
if (ptr) {
|
||||||
|
memset(ptr, pattern, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool check_buffer(void *ptr, size_t size, unsigned char pattern) {
|
||||||
|
if (!ptr) {
|
||||||
|
return false; // Нельзя проверить NULL
|
||||||
|
}
|
||||||
|
unsigned char *byte_ptr = (unsigned char *)ptr;
|
||||||
|
for (size_t i = 0; i < size; ++i) {
|
||||||
|
if (byte_ptr[i] != pattern) {
|
||||||
|
fprintf(stderr, "Ошибка: Байт %zu не соответствует паттерну. Ожидалось %02X, получено %02X\n",
|
||||||
|
i, pattern, byte_ptr[i]);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_realloc_basic_grow() {
|
||||||
|
size_t old_size = 10;
|
||||||
|
size_t new_size = 20;
|
||||||
|
int *ptr = (int *)malloc(old_size * sizeof(int));
|
||||||
|
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fill_buffer(ptr, old_size * sizeof(int), 0xAA);
|
||||||
|
|
||||||
|
int *new_ptr = (int *)realloc(ptr, new_size * sizeof(int));
|
||||||
|
|
||||||
|
if (new_ptr == NULL) {
|
||||||
|
free(ptr); // Оригинальный блок все еще действителен
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что старые данные сохранились
|
||||||
|
if (!check_buffer(new_ptr, old_size * sizeof(int), 0xAA)) {
|
||||||
|
free(new_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что новый участок доступен для записи
|
||||||
|
fill_buffer(new_ptr + old_size, (new_size - old_size) * sizeof(int), 0xBB);
|
||||||
|
if (!check_buffer(new_ptr + old_size, (new_size - old_size) * sizeof(int), 0xBB)) {
|
||||||
|
free(new_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(new_ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_realloc_basic_shrink() {
|
||||||
|
size_t old_size = 20;
|
||||||
|
size_t new_size = 10;
|
||||||
|
int *ptr = (int *)malloc(old_size * sizeof(int));
|
||||||
|
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fill_buffer(ptr, old_size * sizeof(int), 0xCC);
|
||||||
|
|
||||||
|
int *new_ptr = (int *)realloc(ptr, new_size * sizeof(int));
|
||||||
|
|
||||||
|
if (new_ptr == NULL) {
|
||||||
|
free(ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!check_buffer(new_ptr, new_size * sizeof(int), 0xCC)) {
|
||||||
|
free(new_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(new_ptr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_realloc_same_size() {
|
||||||
|
size_t size = 15;
|
||||||
|
int *ptr = (int *)malloc(size * sizeof(int));
|
||||||
|
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fill_buffer(ptr, size * sizeof(int), 0xDD);
|
||||||
|
|
||||||
|
int *new_ptr = (int *)realloc(ptr, size * sizeof(int));
|
||||||
|
|
||||||
|
if (new_ptr == NULL) {
|
||||||
|
free(ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что данные сохранились
|
||||||
|
if (!check_buffer(new_ptr, size * sizeof(int), 0xDD)) {
|
||||||
|
free(new_ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(new_ptr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_realloc_null_ptr() {
|
||||||
|
size_t size = 25;
|
||||||
|
void *ptr = realloc(NULL, size);
|
||||||
|
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Проверяем, что память доступна
|
||||||
|
fill_buffer(ptr, size, 0xEE);
|
||||||
|
if (!check_buffer(ptr, size, 0xEE)) {
|
||||||
|
free(ptr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(ptr);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool test_realloc_to_zero_size() {
|
||||||
|
size_t old_size = 30;
|
||||||
|
void *ptr = malloc(old_size);
|
||||||
|
|
||||||
|
if (ptr == NULL) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
fill_buffer(ptr, old_size, 0xFF);
|
||||||
|
|
||||||
|
void *new_ptr = realloc(ptr, 0);
|
||||||
|
|
||||||
|
// Стандарт C11 (7.22.3.5) говорит, что realloc(ptr, 0) может вернуть NULL
|
||||||
|
// или указатель, который можно передать free().
|
||||||
|
// Если возвращается NULL, оригинальный указатель ptr все еще действителен и должен быть освобожден.
|
||||||
|
// Если возвращается не-NULL, оригинальный ptr недействителен, а новый ptr должен быть освобожден.
|
||||||
|
if (new_ptr == NULL) {
|
||||||
|
printf("realloc(ptr, 0) return NULL.\n");
|
||||||
|
free(ptr); // Освобождаем оригинальный ptr
|
||||||
|
} else {
|
||||||
|
printf("realloc(ptr, 0) return: %p.\n", new_ptr);
|
||||||
|
free(new_ptr); // Освобождаем новый ptr
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
RUN_TEST(test_malloc_basic_allocation);
|
RUN_TEST(test_malloc_basic_allocation);
|
||||||
@@ -112,6 +285,12 @@ int main()
|
|||||||
RUN_TEST(test_malloc_data_integrity);
|
RUN_TEST(test_malloc_data_integrity);
|
||||||
RUN_TEST(test_malloc_large_allocation);
|
RUN_TEST(test_malloc_large_allocation);
|
||||||
RUN_TEST(test_malloc_basic_allocation);
|
RUN_TEST(test_malloc_basic_allocation);
|
||||||
|
RUN_TEST(test_malloc_allocation_and_free);
|
||||||
|
RUN_TEST(test_realloc_basic_grow);
|
||||||
|
RUN_TEST(test_realloc_basic_shrink);
|
||||||
|
RUN_TEST(test_realloc_same_size);
|
||||||
|
RUN_TEST(test_realloc_null_ptr);
|
||||||
|
RUN_TEST(test_realloc_to_zero_size);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,6 @@ if tup.getconfig("NO_TCC") ~= "" then
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
function AddPrefix(prefix, t)
|
|
||||||
for i, v in pairs(t) do
|
|
||||||
t[i] = prefix .. v
|
|
||||||
end
|
|
||||||
return t
|
|
||||||
end
|
|
||||||
|
|
||||||
CC = "kos32-tcc"
|
CC = "kos32-tcc"
|
||||||
|
|
||||||
CFLAGS = " -r -nostdinc -nostdlib -DGNUC -D_BUILD_LIBC -Wall -Werror"
|
CFLAGS = " -r -nostdinc -nostdlib -DGNUC -D_BUILD_LIBC -Wall -Werror"
|
||||||
@@ -43,6 +36,10 @@ GAS_SRC = {
|
|||||||
"string/memmove.s",
|
"string/memmove.s",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FASM_SRC = {
|
||||||
|
"crt/crt0.asm",
|
||||||
|
}
|
||||||
|
|
||||||
LIBC_OBJS = { "libc.c" }
|
LIBC_OBJS = { "libc.c" }
|
||||||
|
|
||||||
tup.append_table(LIBC_OBJS, tup.foreach_rule(GAS_SRC, "as --32 %f -o %o", "%B.o"))
|
tup.append_table(LIBC_OBJS, tup.foreach_rule(GAS_SRC, "as --32 %f -o %o", "%B.o"))
|
||||||
@@ -50,8 +47,4 @@ tup.append_table(LIBC_OBJS, tup.foreach_rule(GAS_SRC, "as --32 %f -o %o", "%B.o"
|
|||||||
tup.rule(LIBC_OBJS, CC .. CFLAGS .. INCLUDES .. " %f -o %o " .. " && strip %o --strip-unneeded ", "libc.o")
|
tup.rule(LIBC_OBJS, CC .. CFLAGS .. INCLUDES .. " %f -o %o " .. " && strip %o --strip-unneeded ", "libc.o")
|
||||||
tup.rule("libc.o", "objconv -fcoff32 %f %o " .. tup.getconfig("KPACK_CMD"), "%B.obj")
|
tup.rule("libc.o", "objconv -fcoff32 %f %o " .. tup.getconfig("KPACK_CMD"), "%B.obj")
|
||||||
|
|
||||||
CRT0_ASM_SRC = AddPrefix("crt/", {
|
tup.rule(FASM_SRC, "fasm %f %o", "%B.o")
|
||||||
"crt0.asm",
|
|
||||||
})
|
|
||||||
|
|
||||||
tup.rule(CRT0_ASM_SRC, "fasm %f %o", "%B.o")
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ void _exit(int status)
|
|||||||
{
|
{
|
||||||
// return error and this is not abort
|
// return error and this is not abort
|
||||||
if (status && status != 128) {
|
if (status && status != 128) {
|
||||||
printf("exit code: %d\n", status);
|
fprintf(stderr, "exit code: %d\n", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (__con_is_load) {
|
if (__con_is_load) {
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ struct mem_node {
|
|||||||
|
|
||||||
struct mem_block {
|
struct mem_block {
|
||||||
size_t size; // Size of the allocated memory block.
|
size_t size; // Size of the allocated memory block.
|
||||||
|
|
||||||
|
size_t a; // align to 8bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
// Macro to get a pointer to the user data area from a mem_node pointer.
|
// Macro to get a pointer to the user data area from a mem_node pointer.
|
||||||
@@ -32,13 +34,39 @@ struct mem_block {
|
|||||||
|
|
||||||
// Macro to check if two adjacent memory nodes are in the same block.
|
// Macro to check if two adjacent memory nodes are in the same block.
|
||||||
// Checks if the end of the left node's allocated space is the start of the right node.
|
// Checks if the end of the left node's allocated space is the start of the right node.
|
||||||
#define MEM_NODES_ARE_IN_ONE_BLOCK(left, right) (GET_MEM_NODE_PTR(left) + left->size == (char*)right)
|
#define MEM_NODES_ARE_IN_ONE_BLOCK(left, right) (GET_MEM_NODE_PTR(left) + ((struct mem_node*)left)->size == (char*)right)
|
||||||
|
|
||||||
// Size of the blocks allocated at a time.
|
// Size of the blocks allocated by `_ksys_alloc`
|
||||||
#define ALLOC_BLOCK_SIZE 4096
|
#define ALLOC_BLOCK_SIZE 4096
|
||||||
|
|
||||||
|
// Macro to merge two adjacent memory nodes.
|
||||||
|
#define CHECK_SIDE_IN_OTHER_BLOCK(node, side) (side == NULL || ((side != NULL) && !MEM_NODES_ARE_IN_ONE_BLOCK(node, side)))
|
||||||
|
|
||||||
|
inline struct mem_node* __mem_MERGE_MEM_NODES(struct mem_node* base, struct mem_node* addition)
|
||||||
|
{
|
||||||
|
// addition is free && nodes base and addition both in one block, else merge is impossible
|
||||||
|
if (MEM_NODE_IS_FREE(addition) && MEM_NODES_ARE_IN_ONE_BLOCK(base, addition)) {
|
||||||
|
// just change size
|
||||||
|
const size_t s = addition->size + sizeof(struct mem_node);
|
||||||
|
base->size += s;
|
||||||
|
base->free += s;
|
||||||
|
|
||||||
|
// and delete addition from list
|
||||||
|
if (addition->next != NULL) {
|
||||||
|
addition->next->last = base;
|
||||||
|
base->next = addition->next;
|
||||||
|
} else {
|
||||||
|
base->next = NULL;
|
||||||
|
}
|
||||||
|
return base;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// Static pointer to the first memory node in the linked list.
|
// Static pointer to the first memory node in the linked list.
|
||||||
// This acts as the head of the memory pool.
|
// This acts as the head of the memory pool.
|
||||||
static struct mem_node* __mem_node = NULL;
|
static struct mem_node* __mem_node = NULL;
|
||||||
|
|
||||||
|
static struct mem_node* __last_biggest_mem_node = NULL;
|
||||||
|
|
||||||
#endif // _LIBC_STDLIB_MEM_
|
#endif // _LIBC_STDLIB_MEM_
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ void abort()
|
|||||||
{
|
{
|
||||||
ksys_thread_t t;
|
ksys_thread_t t;
|
||||||
_ksys_thread_info(&t, -1);
|
_ksys_thread_info(&t, -1);
|
||||||
printf("\nAbort in %d\n", t.pid);
|
fprintf(stderr, "\nAbort in %d\n", t.pid);
|
||||||
|
|
||||||
_exit(128);
|
_exit(128);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,27 +3,6 @@
|
|||||||
#include <sys/ksys.h>
|
#include <sys/ksys.h>
|
||||||
#include "_mem.h"
|
#include "_mem.h"
|
||||||
|
|
||||||
// Macro to merge two adjacent memory nodes.
|
|
||||||
#define CHECK_SIDE_IN_OTHER_BLOCK(node, side) (side == NULL || ((side != NULL) && !MEM_NODES_ARE_IN_ONE_BLOCK(node, side)))
|
|
||||||
|
|
||||||
inline void __mem_MERGE_MEM_NODES(struct mem_node* base, struct mem_node* addition)
|
|
||||||
{
|
|
||||||
if (MEM_NODE_IS_FREE(addition) && MEM_NODES_ARE_IN_ONE_BLOCK(base, addition)) {
|
|
||||||
// just change size
|
|
||||||
const size_t s = addition->size + sizeof(struct mem_node);
|
|
||||||
base->size += s;
|
|
||||||
base->free = base->size;
|
|
||||||
|
|
||||||
// and delete addition from list
|
|
||||||
if (addition->next != NULL) {
|
|
||||||
addition->next->last = base;
|
|
||||||
base->next = addition->next;
|
|
||||||
} else {
|
|
||||||
base->next = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void* ptr)
|
void free(void* ptr)
|
||||||
{
|
{
|
||||||
// Handle NULL pointer.
|
// Handle NULL pointer.
|
||||||
@@ -36,18 +15,28 @@ void free(void* ptr)
|
|||||||
// Mark the memory node as free.
|
// Mark the memory node as free.
|
||||||
node->free = node->size;
|
node->free = node->size;
|
||||||
|
|
||||||
|
if (__last_biggest_mem_node == node) {
|
||||||
|
if (node->last) {
|
||||||
|
__last_biggest_mem_node = node->last; // anyway node will be merged with next
|
||||||
|
// and last and last will have size = last + node + next(if its free too)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Merge with the next node if possible.
|
// Merge with the next node if possible.
|
||||||
if (node->next != NULL)
|
if (node->next != NULL)
|
||||||
__mem_MERGE_MEM_NODES(node, node->next);
|
__mem_MERGE_MEM_NODES(node, node->next);
|
||||||
|
|
||||||
// Merge with the previous node if possible.
|
// Merge with the previous node if possible.
|
||||||
if (node->last != NULL)
|
if (node->last != NULL)
|
||||||
__mem_MERGE_MEM_NODES(node->last, node);
|
node = __mem_MERGE_MEM_NODES(node->last, node);
|
||||||
|
|
||||||
|
if (node) {
|
||||||
|
|
||||||
// If the current node is not adjacent to either the next or previous node,
|
// If the current node is not adjacent to either the next or previous node,
|
||||||
// it might be a separate block that can be freed.
|
// it might be a separate block that can be freed.
|
||||||
if ((node->next == NULL || ((node->next != NULL) && !MEM_NODES_ARE_IN_ONE_BLOCK(node, node->next)))
|
if (MEM_NODE_IS_FREE(node) // check it because node maybe was merged with last
|
||||||
&& (node->last == NULL || ((node->last != NULL) && !MEM_NODES_ARE_IN_ONE_BLOCK(node->last, node)))) {
|
&& (node->last == NULL || !MEM_NODES_ARE_IN_ONE_BLOCK(node, node->next))
|
||||||
|
&& (node->last == NULL || !MEM_NODES_ARE_IN_ONE_BLOCK(node->last, node))) {
|
||||||
|
|
||||||
// Get a pointer to the mem_block header from the mem_node header.
|
// Get a pointer to the mem_block header from the mem_node header.
|
||||||
struct mem_block* block = (struct mem_block*)(((char*)node) - sizeof(struct mem_block));
|
struct mem_block* block = (struct mem_block*)(((char*)node) - sizeof(struct mem_block));
|
||||||
@@ -73,8 +62,36 @@ void free(void* ptr)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct mem_node* a = node->next;
|
||||||
|
struct mem_node* b = node->last;
|
||||||
|
|
||||||
|
if (!a && !b) {
|
||||||
|
__last_biggest_mem_node = NULL;
|
||||||
|
} else if (a && !b) {
|
||||||
|
__last_biggest_mem_node = a;
|
||||||
|
} else if (!a && b) {
|
||||||
|
__last_biggest_mem_node = b;
|
||||||
|
} else if (a && b) {
|
||||||
|
__last_biggest_mem_node = (a->free > b->free) ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__last_biggest_mem_node == node) {
|
||||||
|
if (node->next && !(node->last)) {
|
||||||
|
__last_biggest_mem_node = node->next;
|
||||||
|
} else if (node->last && !(node->next)) {
|
||||||
|
__last_biggest_mem_node = node->last;
|
||||||
|
} else if (node->next && node->last) {
|
||||||
|
if (node->last->free > node->next->free) {
|
||||||
|
__last_biggest_mem_node = node->last;
|
||||||
|
} else {
|
||||||
|
__last_biggest_mem_node = node->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Free the memory block using the ksys_free function.
|
// Free the memory block using the ksys_free function.
|
||||||
_ksys_free(block);
|
_ksys_free(block);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,49 +2,75 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/ksys.h>
|
#include <sys/ksys.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include "_mem.h"
|
#include "_mem.h"
|
||||||
|
|
||||||
// Macro to align a value to a specified alignment.
|
// Macro to align a value to a specified alignment.
|
||||||
// Ensures that the allocated memory is aligned to a certain boundary (e.g., 16 bytes).
|
// Ensures that the allocated memory is aligned to a certain boundary (e.g., 16 bytes).
|
||||||
#define __mem_align(value, align) ((value + align - 1) & ~(align - 1))
|
#define __mem_align(value, align) ((value + align - 1) & ~(align - 1))
|
||||||
|
|
||||||
void* malloc(size_t size)
|
static struct mem_node* __new_mem_node_from_exist(struct mem_node* current_node, size_t size, bool* from_empty_node)
|
||||||
{
|
{
|
||||||
// Handle zero-size allocation.
|
struct mem_node* new_node = NULL;
|
||||||
if (size == 0) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Align the size to 16 bytes.
|
|
||||||
size = __mem_align(size, 16);
|
|
||||||
|
|
||||||
struct mem_node* current_node = __mem_node; // Start at the head of the linked list.
|
|
||||||
struct mem_node* new_node = NULL; // Pointer to the new node that will be created.
|
|
||||||
|
|
||||||
// Iterate through the linked list of memory nodes.
|
|
||||||
while (current_node != NULL) {
|
|
||||||
// Check if the current node has enough free space for the requested size.
|
// Check if the current node has enough free space for the requested size.
|
||||||
if (size + sizeof(struct mem_node) <= current_node->free) {
|
if (size + sizeof(struct mem_node) <= current_node->free) {
|
||||||
|
|
||||||
|
*from_empty_node = MEM_NODE_IS_FREE(current_node);
|
||||||
|
if (*from_empty_node) {
|
||||||
|
new_node = current_node;
|
||||||
|
} else {
|
||||||
// Calculate the used memory in current node
|
// Calculate the used memory in current node
|
||||||
const size_t s = GET_MEM_NODE_USED_MEM(current_node);
|
const size_t s = GET_MEM_NODE_USED_MEM(current_node);
|
||||||
|
|
||||||
// Create a new memory node after the current node's used space.
|
// Create a new memory node after the current node's used space.
|
||||||
new_node = (struct mem_node*)(GET_MEM_NODE_PTR(current_node) + s);
|
new_node = (struct mem_node*)(GET_MEM_NODE_PTR(current_node) + s);
|
||||||
|
|
||||||
// Set the size of the new node.
|
|
||||||
new_node->size = current_node->free - sizeof(struct mem_node);
|
|
||||||
|
|
||||||
// Update current node's size
|
// Update current node's size
|
||||||
current_node->size = s;
|
current_node->size = s;
|
||||||
|
|
||||||
|
// Set the size of the new node.
|
||||||
|
// for new node give all free space in current node
|
||||||
|
new_node->size = current_node->free - sizeof(struct mem_node);
|
||||||
|
|
||||||
// Mark current node as used.
|
// Mark current node as used.
|
||||||
current_node->free = 0;
|
current_node->free = 0;
|
||||||
|
|
||||||
break; // Found a suitable node, exit the loop.
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* malloc(size_t size)
|
||||||
|
{
|
||||||
|
char b[32];
|
||||||
|
|
||||||
|
// Handle zero-size allocation.
|
||||||
|
if (size == 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Align the size to 8 bytes.
|
||||||
|
size = __mem_align(size, 8);
|
||||||
|
|
||||||
|
struct mem_node* current_node = NULL;
|
||||||
|
struct mem_node* new_node = NULL; // Pointer to the new node that will be created.
|
||||||
|
bool from_empty_node = false;
|
||||||
|
|
||||||
|
if (__last_biggest_mem_node != NULL)
|
||||||
|
new_node = __new_mem_node_from_exist(__last_biggest_mem_node, size, &from_empty_node); // try find free space in last created node
|
||||||
|
|
||||||
|
// if cant find in __last_biggest_mem_node
|
||||||
|
if (new_node == NULL) {
|
||||||
|
current_node = __mem_node; // Start at the head of the linked list.
|
||||||
|
|
||||||
|
// Iterate through the linked list of memory nodes.
|
||||||
|
while (current_node != NULL) {
|
||||||
|
new_node = __new_mem_node_from_exist(current_node, size, &from_empty_node);
|
||||||
|
if (new_node)
|
||||||
|
break; // Found a suitable node, exit the loop.
|
||||||
|
|
||||||
current_node = current_node->next; // Move to the next node in the list.
|
current_node = current_node->next; // Move to the next node in the list.
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If no suitable node was found in the existing list:
|
// If no suitable node was found in the existing list:
|
||||||
if (new_node == NULL) {
|
if (new_node == NULL) {
|
||||||
@@ -60,6 +86,8 @@ void* malloc(size_t size)
|
|||||||
return NULL; // Return NULL to indicate allocation failure.
|
return NULL; // Return NULL to indicate allocation failure.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
block->size = s;
|
||||||
|
|
||||||
// Create a new memory node after the mem_block header.
|
// Create a new memory node after the mem_block header.
|
||||||
new_node = (struct mem_node*)(block + sizeof(struct mem_block));
|
new_node = (struct mem_node*)(block + sizeof(struct mem_block));
|
||||||
|
|
||||||
@@ -70,6 +98,7 @@ void* malloc(size_t size)
|
|||||||
// Set the free space in the new node.
|
// Set the free space in the new node.
|
||||||
new_node->free = new_node->size - size;
|
new_node->free = new_node->size - size;
|
||||||
|
|
||||||
|
if (!from_empty_node) {
|
||||||
// Set the last pointer of the new node to the current node.
|
// Set the last pointer of the new node to the current node.
|
||||||
new_node->last = current_node;
|
new_node->last = current_node;
|
||||||
|
|
||||||
@@ -87,12 +116,17 @@ void* malloc(size_t size)
|
|||||||
// If the current node is NULL, the new node is the first node in the list.
|
// If the current node is NULL, the new node is the first node in the list.
|
||||||
new_node->next = NULL;
|
new_node->next = NULL;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If the linked list was empty, set the head to the new node.
|
// If the linked list was empty, set the head to the new node.
|
||||||
if (__mem_node == NULL) {
|
if (__mem_node == NULL) {
|
||||||
__mem_node = new_node;
|
__mem_node = new_node;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (__last_biggest_mem_node == NULL || new_node->free > __last_biggest_mem_node->free) {
|
||||||
|
__last_biggest_mem_node = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
// Return a pointer to the user data area of the new node.
|
// Return a pointer to the user data area of the new node.
|
||||||
return GET_MEM_NODE_PTR(new_node);
|
return GET_MEM_NODE_PTR(new_node);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,33 +3,60 @@
|
|||||||
#include <sys/ksys.h>
|
#include <sys/ksys.h>
|
||||||
#include "_mem.h"
|
#include "_mem.h"
|
||||||
|
|
||||||
void* realloc(void* ptr, size_t newsize)
|
// realloc mem. using if other ways not working
|
||||||
|
void* fail_realloc(void* ptr, size_t newsize)
|
||||||
{
|
{
|
||||||
// Handle NULL pointer.
|
|
||||||
if (ptr == NULL)
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
// Get a pointer to the mem_node header from the user data pointer.
|
|
||||||
struct mem_node* node = GET_MEM_NODE_HEADER(ptr);
|
|
||||||
void* new_ptr;
|
|
||||||
|
|
||||||
// If the new size is smaller than or equal to the current size:
|
|
||||||
if (node->size >= newsize) {
|
|
||||||
// Update the free space in the current node.
|
|
||||||
node->free = node->size - newsize;
|
|
||||||
// Return the original pointer.
|
|
||||||
new_ptr = ptr;
|
|
||||||
} else {
|
|
||||||
// Allocate a new block of memory with the new size.
|
// Allocate a new block of memory with the new size.
|
||||||
new_ptr = malloc(newsize);
|
void* new_ptr = malloc(newsize);
|
||||||
|
|
||||||
// If both the old pointer and the new pointer are not NULL:
|
// If both the old pointer and the new pointer are not NULL:
|
||||||
if (ptr != NULL && new_ptr != NULL) {
|
if (ptr != NULL && new_ptr != NULL) {
|
||||||
// Copy the data from the old block to the new block.
|
// Copy the data from the old block to the new block.
|
||||||
memcpy(new_ptr, ptr, min(newsize, node->size));
|
memcpy(new_ptr, ptr, min(newsize, GET_MEM_NODE_USED_MEM(GET_MEM_NODE_HEADER(ptr))));
|
||||||
// Free the old block.
|
|
||||||
free(ptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ptr) {
|
||||||
|
free(ptr); // Free the old block.
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void* realloc(void* ptr, size_t newsize)
|
||||||
|
{
|
||||||
|
|
||||||
|
void* new_ptr = NULL;
|
||||||
|
struct mem_node* node = NULL;
|
||||||
|
|
||||||
|
if (ptr && newsize) {
|
||||||
|
|
||||||
|
// Get a pointer to the mem_node header from the user data pointer.
|
||||||
|
node = GET_MEM_NODE_HEADER(ptr);
|
||||||
|
|
||||||
|
if (node->size >= newsize) { // current node have enough mem
|
||||||
|
// it work always if newsize is smaller
|
||||||
|
// Update the free space in the current node.
|
||||||
|
node->free = node->size - newsize;
|
||||||
|
// Return the original pointer.
|
||||||
|
new_ptr = ptr;
|
||||||
|
} else if (node->next && MEM_NODE_IS_FREE(node->next) && node->size + node->next->size >= newsize) {
|
||||||
|
// So what happens here is that the node merges with the next node if their volume is sufficient.
|
||||||
|
// And a reallock is called in the hopes that the first condition will be met.
|
||||||
|
// if merge failed realloc anyway return pointer, but it will got from `fail_realloc`
|
||||||
|
// it faster than merge with last node, because its not make a copy/move
|
||||||
|
new_ptr = realloc(GET_MEM_NODE_PTR(__mem_MERGE_MEM_NODES(node, node->next)), newsize);
|
||||||
|
} else if (node->last && MEM_NODE_IS_FREE(node->last) && node->size + node->last->size >= newsize) {
|
||||||
|
struct mem_node* l = node->last;
|
||||||
|
|
||||||
|
l = __mem_MERGE_MEM_NODES(l, node);
|
||||||
|
if (l)
|
||||||
|
memmove(GET_MEM_NODE_PTR(l), ptr, GET_MEM_NODE_USED_MEM(node));
|
||||||
|
new_ptr = realloc(GET_MEM_NODE_PTR(l), newsize);
|
||||||
|
} else {
|
||||||
|
new_ptr = fail_realloc(ptr, newsize);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
new_ptr = fail_realloc(ptr, newsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the new pointer.
|
// Return the new pointer.
|
||||||
|
|||||||
Reference in New Issue
Block a user