#include #include #include #include #include "../source/stdlib/_mem.h" #define RUN_TEST(func) \ printf("---\tRUN TEST: %s\t---\n", #func); \ if (func()) { \ printf("[SUCCESS]\tTest %s is ok.\n\n", #func); \ } else { \ fprintf(stderr, "[FAIL]\tTest %s failed.\n\n", #func); \ exit(EXIT_FAILURE); \ } // c between 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]) { fprintf(stderr, "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)) { fprintf(stderr, "node %p in node %p", GET_MEM_NODE_HEADER(ptr[i]), GET_MEM_NODE_HEADER(ptr[j])); // additional info, may help with debug fprintf(stderr, "node %p\n size:%p\n free:%p\n next: %p\n last: %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); fprintf(stderr, "node %p\n size:%p\n free:%p\n next: %p\n last: %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; } unsigned char* byte_ptr = (unsigned char*)ptr; for (size_t i = 0; i < size; ++i) { if (byte_ptr[i] != pattern) { fprintf(stderr, "Error: Byte %u does not match pattern. Expected %02X, got %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); if (new_ptr == NULL) { printf("realloc(ptr, 0) return NULL.\n"); free(ptr); } else { printf("realloc(ptr, 0) return: %p.\n", new_ptr); free(new_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; }