feat: added some more tests, added some free logic

This commit is contained in:
jackjohn7 2026-06-16 00:12:55 -05:00
parent d6d4f2a0d9
commit 134e27d38b
3 changed files with 191 additions and 98 deletions

View file

@ -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;
}

View file

@ -1,3 +1,4 @@
#include <stddef.h>
void* jalloc(size_t size);
void jfree(void* ptr);

View file

@ -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);
}
// --- 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);
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;
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++;
}
ASSERT(ok, "no overlap: writes are isolated");
jfree(a);
jfree(b);
}
// --- 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++;
}
}
// --- 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);
}
printf("\n=== Results: %d passed, %d failed ===\n", passed, failed);
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;
}
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;
}