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!
296 lines
8.2 KiB
C
296 lines
8.2 KiB
C
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdbool.h>
|
||
#include "../source/stdlib/_mem.h"
|
||
|
||
#define RUN_TEST(func) \
|
||
do { \
|
||
printf("---\tRUN TEST: %s\t---\n", #func); \
|
||
if (func()) { \
|
||
printf("[SUCCESS]\tTest %s is ok.\n\n", #func); \
|
||
} else { \
|
||
printf("[FAIL]\tTest %s failed.\n\n", #func); \
|
||
exit(EXIT_FAILURE); \
|
||
} \
|
||
} while (0)
|
||
|
||
// c behind a and b
|
||
#define IN_RANGE(a, b, c, len) ((a > c && c > b) || ((a > c + len && c + len > b)))
|
||
|
||
bool test_malloc_basic_allocation()
|
||
{
|
||
void* ptr = malloc(sizeof(int));
|
||
|
||
if (ptr)
|
||
free(ptr);
|
||
|
||
return ptr;
|
||
}
|
||
bool test_malloc_zero_bytes()
|
||
{
|
||
return malloc(0) == NULL;
|
||
}
|
||
|
||
bool test_malloc_multiple_allocations()
|
||
{
|
||
void* ptr[512];
|
||
|
||
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||
ptr[i] = malloc(i);
|
||
if (ptr[i] == NULL) {
|
||
return false;
|
||
}
|
||
}
|
||
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||
for (int j = 1; j < sizeof(ptr) / sizeof(*ptr); j++) {
|
||
if (i != j) {
|
||
if (ptr[i] == ptr[j]) {
|
||
printf("ptrs[%d] == ptrs[%d].\n", i, j);
|
||
return false;
|
||
} else if (IN_RANGE(
|
||
(char*)GET_MEM_NODE_HEADER(ptr[i]) + GET_MEM_NODE_HEADER(ptr[i])->size,
|
||
(char*)GET_MEM_NODE_HEADER(ptr[i]),
|
||
(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]));
|
||
// 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[j]), GET_MEM_NODE_HEADER(ptr[j])->size, GET_MEM_NODE_HEADER(ptr[j])->free, GET_MEM_NODE_HEADER(ptr[j])->next, GET_MEM_NODE_HEADER(ptr[j])->last);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
for (int i = 1; i < sizeof(ptr) / sizeof(*ptr); i++) {
|
||
free(ptr[i]);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool test_malloc_data_integrity()
|
||
{
|
||
const char* As = "AAA";
|
||
const char* Cs = "CCC";
|
||
|
||
char* A = (char*)malloc(10);
|
||
char* B = (char*)malloc(10);
|
||
char* C = (char*)malloc(10);
|
||
|
||
if (!A || !B || !C) {
|
||
printf("can't alloc\n");
|
||
free(A);
|
||
free(B);
|
||
free(C);
|
||
return false;
|
||
}
|
||
|
||
strcpy(A, As);
|
||
strcpy(C, Cs);
|
||
|
||
free(B);
|
||
|
||
if (strcmp(A, As) != 0) {
|
||
printf("A data is broken after free(B). A = '%s'\n", A);
|
||
free(A);
|
||
free(C);
|
||
return false;
|
||
}
|
||
if (strcmp(C, Cs) != 0) {
|
||
printf("C data is broken after free(B). C = '%s'\n", C);
|
||
free(A);
|
||
free(C);
|
||
return false;
|
||
}
|
||
|
||
free(A);
|
||
free(C);
|
||
return true;
|
||
}
|
||
bool test_malloc_large_allocation()
|
||
{
|
||
void* ptr = malloc(1024 * 1024 * 16); // alloc 16mb
|
||
|
||
if (ptr)
|
||
free(ptr);
|
||
|
||
return ptr;
|
||
}
|
||
bool test_malloc_allocation_and_free()
|
||
{
|
||
free(malloc(sizeof(int)));
|
||
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()
|
||
{
|
||
RUN_TEST(test_malloc_basic_allocation);
|
||
RUN_TEST(test_malloc_zero_bytes);
|
||
RUN_TEST(test_malloc_multiple_allocations);
|
||
RUN_TEST(test_malloc_data_integrity);
|
||
RUN_TEST(test_malloc_large_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;
|
||
} |