feat: added some more tests, added some free logic
This commit is contained in:
parent
d6d4f2a0d9
commit
134e27d38b
3 changed files with 191 additions and 98 deletions
39
src/jalloc.c
39
src/jalloc.c
|
|
@ -1,22 +1,51 @@
|
|||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HEAP_SIZE (1024 * 1024 * 2)
|
||||
#define ALIGNMENT 8
|
||||
#define ALIGNMENT 16
|
||||
#define HEADER_SIZE 32
|
||||
#define FOOTER_SIZE 16
|
||||
|
||||
static uint8_t heap[HEAP_SIZE];
|
||||
static uint8_t *heap_ptr = heap;
|
||||
|
||||
struct Header {
|
||||
size_t size;
|
||||
bool is_free;
|
||||
struct Header *next;
|
||||
};
|
||||
|
||||
struct Footer {
|
||||
struct Header *header;
|
||||
};
|
||||
|
||||
size_t next_multiple_of_align(size_t size) {
|
||||
return ((size + (ALIGNMENT - 1)) >> 4) << 4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocate memory via bump allocation
|
||||
*/
|
||||
void *jalloc(size_t size) {
|
||||
if (heap_ptr + size > heap + sizeof(heap)) return NULL;
|
||||
|
||||
void *ptr = heap_ptr;
|
||||
heap_ptr += size;
|
||||
// Total size of allocation = HEADER(padded to ALIGNMENT) + size(padded to ALIGNMENT) + FOOTER(padded to ALIGNMENT)
|
||||
size_t aligned_size = next_multiple_of_align(size);
|
||||
void *ptr = heap_ptr + HEADER_SIZE;
|
||||
struct Header *hdr = (struct Header *)heap_ptr;
|
||||
struct Footer *ftr = (struct Footer *)(ptr + aligned_size);
|
||||
if (ftr + FOOTER_SIZE > heap + sizeof(heap)) return NULL;
|
||||
hdr->size = size;
|
||||
hdr->next = (struct Header *)heap_ptr;
|
||||
hdr->is_free = false;
|
||||
ftr->header = hdr;
|
||||
heap_ptr = (void *) ftr + FOOTER_SIZE; // set next ptr
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void jfree(void *ptr) {}
|
||||
void jfree(void *ptr) {
|
||||
struct Header *hdr = (struct Header *)(ptr-HEADER_SIZE);
|
||||
struct Footer *ftr = (struct Footer *)(hdr->next-FOOTER_SIZE);
|
||||
hdr->is_free = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#include <stddef.h>
|
||||
|
||||
void* jalloc(size_t size);
|
||||
void jfree(void* ptr);
|
||||
|
|
|
|||
235
src/main.c
235
src/main.c
|
|
@ -1,106 +1,169 @@
|
|||
#include "jalloc.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
void test_jalloc(void) {
|
||||
int passed = 0, failed = 0;
|
||||
static int g_passed = 0;
|
||||
static int g_failed = 0;
|
||||
|
||||
// --- Test 1: Basic write/read ---
|
||||
{
|
||||
size_t size = 64;
|
||||
uint8_t *buf = (uint8_t *)jalloc(size);
|
||||
if (!buf) {
|
||||
printf("[FAIL] Test 1: jalloc returned NULL for size %zu\n", size);
|
||||
failed++;
|
||||
} else {
|
||||
for (size_t i = 0; i < size; i++) buf[i] = (uint8_t)i;
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (buf[i] != (uint8_t)i) { ok = 0; break; }
|
||||
}
|
||||
printf("[%s] Test 1: sequential byte pattern (%zu bytes)\n",
|
||||
ok ? "PASS" : "FAIL", size);
|
||||
ok ? passed++ : failed++;
|
||||
}
|
||||
}
|
||||
#define ASSERT(cond, msg) \
|
||||
do { \
|
||||
if (cond) { \
|
||||
printf("[PASS] %s\n", (msg)); \
|
||||
g_passed++; \
|
||||
} else { \
|
||||
printf("[FAIL] %s (line %d)\n", (msg), __LINE__); \
|
||||
g_failed++; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
static void test_single_byte(void) {
|
||||
uint8_t *p = jalloc(1);
|
||||
ASSERT(p != NULL, "single byte: allocation succeeds");
|
||||
if (!p) return;
|
||||
|
||||
*p = 0x42;
|
||||
ASSERT(*p == 0x42, "single byte: read-back correct");
|
||||
jfree(p);
|
||||
}
|
||||
|
||||
static void test_sequential_pattern(void) {
|
||||
const size_t n = 256;
|
||||
uint8_t *p = jalloc(n);
|
||||
ASSERT(p != NULL, "sequential pattern: allocation succeeds");
|
||||
if (!p) return;
|
||||
|
||||
for (size_t i = 0; i < n; i++) p[i] = (uint8_t)i;
|
||||
|
||||
// --- Test 2: Multiple independent allocations don't overlap ---
|
||||
{
|
||||
size_t size = 128;
|
||||
uint8_t *a = (uint8_t *)jalloc(size);
|
||||
uint8_t *b = (uint8_t *)jalloc(size);
|
||||
if (!a || !b) {
|
||||
printf("[FAIL] Test 2: jalloc returned NULL\n");
|
||||
failed++;
|
||||
} else {
|
||||
memset(a, 0xAA, size);
|
||||
memset(b, 0xBB, size);
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (p[i] != (uint8_t)i) { ok = 0; break; }
|
||||
|
||||
ASSERT(ok, "sequential pattern: all bytes intact");
|
||||
jfree(p);
|
||||
}
|
||||
|
||||
static void test_no_overlap(void) {
|
||||
const size_t n = 256;
|
||||
uint8_t *a = jalloc(n);
|
||||
uint8_t *b = jalloc(n);
|
||||
ASSERT(a != NULL && b != NULL, "no overlap: both allocations succeed");
|
||||
if (!a || !b) { jfree(a); jfree(b); return; }
|
||||
|
||||
memset(a, 0xAA, n);
|
||||
memset(b, 0xBB, n);
|
||||
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (a[i] != 0xAA || b[i] != 0xBB) { ok = 0; break; }
|
||||
}
|
||||
printf("[%s] Test 2: two allocations don't overlap (%zu bytes each)\n",
|
||||
ok ? "PASS" : "FAIL", size);
|
||||
ok ? passed++ : failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Test 3: Small allocation (1 byte) ---
|
||||
{
|
||||
uint8_t *buf = (uint8_t *)jalloc(1);
|
||||
if (!buf) {
|
||||
printf("[FAIL] Test 3: jalloc returned NULL for 1 byte\n");
|
||||
failed++;
|
||||
} else {
|
||||
*buf = 0x42;
|
||||
int ok = (*buf == 0x42);
|
||||
printf("[%s] Test 3: single byte allocation\n", ok ? "PASS" : "FAIL");
|
||||
ok ? passed++ : failed++;
|
||||
}
|
||||
}
|
||||
ASSERT(ok, "no overlap: writes are isolated");
|
||||
jfree(a);
|
||||
jfree(b);
|
||||
}
|
||||
|
||||
// --- Test 4: Large allocation ---
|
||||
{
|
||||
size_t size = 1024 * 1024; // 1 MiB
|
||||
uint8_t *buf = (uint8_t *)jalloc(size);
|
||||
if (!buf) {
|
||||
printf("[FAIL] Test 4: jalloc returned NULL for %zu bytes\n", size);
|
||||
failed++;
|
||||
} else {
|
||||
memset(buf, 0xCD, size);
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (buf[i] != 0xCD) { ok = 0; break; }
|
||||
}
|
||||
printf("[%s] Test 4: large allocation (%zu bytes)\n",
|
||||
ok ? "PASS" : "FAIL", size);
|
||||
ok ? passed++ : failed++;
|
||||
}
|
||||
}
|
||||
|
||||
// --- Test 5: String write/read ---
|
||||
{
|
||||
static void test_string(void) {
|
||||
const char *msg = "Hello, jalloc!";
|
||||
size_t size = strlen(msg) + 1;
|
||||
char *buf = (char *)jalloc(size);
|
||||
if (!buf) {
|
||||
printf("[FAIL] Test 5: jalloc returned NULL\n");
|
||||
failed++;
|
||||
} else {
|
||||
size_t len = strlen(msg) + 1;
|
||||
char *buf = jalloc(len);
|
||||
ASSERT(buf != NULL, "string: allocation succeeds");
|
||||
if (!buf) return;
|
||||
|
||||
strcpy(buf, msg);
|
||||
int ok = (strcmp(buf, msg) == 0);
|
||||
printf("[%s] Test 5: string integrity (\"%s\")\n",
|
||||
ok ? "PASS" : "FAIL", buf);
|
||||
ok ? passed++ : failed++;
|
||||
ASSERT(strcmp(buf, msg) == 0, "string: content matches");
|
||||
jfree(buf);
|
||||
}
|
||||
|
||||
static void test_large_allocation(void) {
|
||||
const size_t n = 1024 * 1024; // 1 MiB
|
||||
uint8_t *p = jalloc(n);
|
||||
ASSERT(p != NULL, "large alloc: allocation succeeds (1 MiB)");
|
||||
if (!p) return;
|
||||
|
||||
memset(p, 0xCD, n);
|
||||
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (p[i] != 0xCD) { ok = 0; break; }
|
||||
|
||||
ASSERT(ok, "large alloc: all bytes intact");
|
||||
jfree(p);
|
||||
}
|
||||
|
||||
static void test_many_allocations(void) {
|
||||
const int count = 1000;
|
||||
const size_t n = 64;
|
||||
uint8_t *ptrs[1000] = {0};
|
||||
int alloc_ok = 1;
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
ptrs[i] = jalloc(n);
|
||||
if (!ptrs[i]) { alloc_ok = 0; break; }
|
||||
memset(ptrs[i], (uint8_t)i, n);
|
||||
}
|
||||
ASSERT(alloc_ok, "many allocs: 1000 allocations all succeed");
|
||||
|
||||
int content_ok = 1;
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!ptrs[i]) break;
|
||||
for (size_t j = 0; j < n; j++)
|
||||
if (ptrs[i][j] != (uint8_t)i) { content_ok = 0; break; }
|
||||
jfree(ptrs[i]);
|
||||
}
|
||||
ASSERT(content_ok, "many allocs: all bytes intact across siblings");
|
||||
}
|
||||
|
||||
static void test_free_and_reuse(void) {
|
||||
const size_t n = 128;
|
||||
uint8_t *a = jalloc(n);
|
||||
ASSERT(a != NULL, "free/reuse: first allocation succeeds");
|
||||
if (!a) return;
|
||||
|
||||
memset(a, 0x11, n);
|
||||
jfree(a);
|
||||
|
||||
uint8_t *b = jalloc(n);
|
||||
ASSERT(b != NULL, "free/reuse: allocation after free succeeds");
|
||||
if (!b) return;
|
||||
|
||||
memset(b, 0x22, n);
|
||||
|
||||
int ok = 1;
|
||||
for (size_t i = 0; i < n; i++)
|
||||
if (b[i] != 0x22) { ok = 0; break; }
|
||||
|
||||
ASSERT(ok, "free/reuse: memory is writable after reuse");
|
||||
jfree(b);
|
||||
}
|
||||
|
||||
static void test_alignment(void) {
|
||||
// Test power-of-two sizes from 1 to 128 bytes
|
||||
void *ptrs[8];
|
||||
int ok = 1;
|
||||
|
||||
for (int i = 0; i < 8; i++) {
|
||||
ptrs[i] = jalloc((size_t)1 << i);
|
||||
if (!ptrs[i] || (uintptr_t)ptrs[i] % sizeof(void *) != 0)
|
||||
ok = 0;
|
||||
}
|
||||
|
||||
printf("\n=== Results: %d passed, %d failed ===\n", passed, failed);
|
||||
ASSERT(ok, "alignment: all allocations are pointer-aligned");
|
||||
|
||||
for (int i = 0; i < 8; i++) jfree(ptrs[i]);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
test_jalloc();
|
||||
return 0;
|
||||
printf("=== jalloc test suite ===\n\n");
|
||||
|
||||
test_single_byte();
|
||||
test_sequential_pattern();
|
||||
test_no_overlap();
|
||||
test_string();
|
||||
test_large_allocation();
|
||||
test_many_allocations();
|
||||
test_free_and_reuse();
|
||||
test_alignment();
|
||||
|
||||
printf("\n=== %d passed, %d failed ===\n", g_passed, g_failed);
|
||||
return g_failed > 0 ? 1 : 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue