copy on git

This commit is contained in:
Thomas Rieg 2025-11-28 19:50:58 +01:00
commit 42653de246
205 changed files with 7459 additions and 0 deletions

419
main.c Normal file
View file

@ -0,0 +1,419 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
/*
** These match your allocators thresholds
** (only used here for shaping size distributions)
*/
#define TINY_MAX 64
#define SMALL_MAX 4096
#define ARRAY_SIZE 10000 // number of live slots we manage
#define RANDOM_ITERS 200000 // number of random operations
#define PATTERN_BASE 0xA5
#define MAX_LARGE_SIZE (SMALL_MAX * 8) // up to 32k-ish
typedef struct s_block
{
void *ptr;
size_t size;
unsigned char pattern;
int is_calloc;
} t_block;
static t_block g_blocks[ARRAY_SIZE];
/* Fill a block with a simple pattern byte */
static void fill_pattern(void *ptr, size_t size, unsigned char pattern)
{
if (!ptr || size == 0)
return;
memset(ptr, pattern, size);
}
/* Check that a block still has the expected pattern */
static int check_pattern(void *ptr, size_t size, unsigned char pattern)
{
if (!ptr || size == 0)
return 1;
unsigned char *p = (unsigned char *)ptr;
for (size_t i = 0; i < size; ++i)
{
if (p[i] != pattern)
{
fprintf(stderr,
"[ERROR] Pattern mismatch at %p (offset %zu): "
"expected 0x%02X, got 0x%02X\n",
ptr, i, (unsigned)pattern, (unsigned)p[i]);
return 0;
}
}
return 1;
}
/* Check that a calloc'd block is zeroed */
static int check_zero(void *ptr, size_t size)
{
if (!ptr || size == 0)
return 1;
unsigned char *p = (unsigned char *)ptr;
for (size_t i = 0; i < size; ++i)
{
if (p[i] != 0)
{
fprintf(stderr,
"[ERROR] Calloc block not zeroed at %p (offset %zu): got 0x%02X\n",
ptr, i, (unsigned)p[i]);
return 0;
}
}
return 1;
}
/* Generate a pseudo-random size skewed across tiny/small/large */
static size_t random_size(void)
{
int r = rand() % 100;
if (r < 40)
{
/* TINY range [1..TINY_MAX] */
return (size_t)(1 + rand() % TINY_MAX);
}
else if (r < 85)
{
/* SMALL range [TINY_MAX+1 .. SMALL_MAX] */
return (size_t)(TINY_MAX + 1 + rand() % (SMALL_MAX - TINY_MAX));
}
else
{
/* LARGE range [SMALL_MAX+1 .. MAX_LARGE_SIZE] */
return (size_t)(SMALL_MAX + 1 + rand() % (MAX_LARGE_SIZE - SMALL_MAX - 1));
}
}
/* Tear down all blocks cleanly (for the end of tests) */
static void free_all_blocks(void)
{
for (int i = 0; i < ARRAY_SIZE; ++i)
{
if (g_blocks[i].ptr)
{
free(g_blocks[i].ptr);
g_blocks[i].ptr = NULL;
g_blocks[i].size = 0;
}
}
}
/* Simple deterministic sanity tests */
static void basic_tests(void)
{
printf("== basic_tests ==\n");
/* TINY */
void *a = malloc(10);
void *b = malloc(TINY_MAX);
fill_pattern(a, 10, 0x11);
fill_pattern(b, TINY_MAX, 0x22);
check_pattern(a, 10, 0x11);
check_pattern(b, TINY_MAX, 0x22);
free(a);
free(b);
/* SMALL */
void *c = malloc(SMALL_MAX / 2);
fill_pattern(c, SMALL_MAX / 2, 0x33);
check_pattern(c, SMALL_MAX / 2, 0x33);
c = realloc(c, SMALL_MAX / 2 + 100); // grow within SMALL
check_pattern(c, SMALL_MAX / 2, 0x33);
free(c);
/* LARGE */
size_t large_sz = SMALL_MAX * 4;
void *d = malloc(large_sz);
fill_pattern(d, large_sz, 0x44);
check_pattern(d, large_sz, 0x44);
d = realloc(d, large_sz * 2); // grow LARGE
check_pattern(d, large_sz, 0x44);
d = realloc(d, large_sz / 2); // shrink LARGE
check_pattern(d, large_sz / 2, 0x44);
free(d);
/* CALLOC */
void *e = calloc(1000, 1);
check_zero(e, 1000);
memset(e, 0xAB, 1000);
e = realloc(e, 2000);
check_pattern(e, 1000, 0xAB);
free(e);
printf("basic_tests: OK\n\n");
}
static void random_stress(void)
{
printf("== random_stress ==\n");
for (int i = 0; i < ARRAY_SIZE; ++i)
{
g_blocks[i].ptr = NULL;
g_blocks[i].size = 0;
g_blocks[i].pattern = 0;
g_blocks[i].is_calloc = 0;
}
for (int iter = 0; iter < RANDOM_ITERS; ++iter)
{
int idx = rand() % ARRAY_SIZE;
t_block *blk = &g_blocks[idx];
int op = rand() % 4; /* 0 = alloc/new, 1 = free, 2 = realloc up, 3 = realloc down */
/* Invariant: for non-NULL blocks we always keep full [0..size) filled with pattern */
if (blk->ptr && !blk->is_calloc)
{
if (!check_pattern(blk->ptr, blk->size, blk->pattern))
{
fprintf(stderr, "[FATAL] pattern mismatch before op; idx=%d\n", idx);
abort();
}
}
if (op == 0)
{
/* allocate new if empty, otherwise grow via realloc */
if (blk->ptr == NULL)
{
size_t sz = random_size();
int use_calloc = (rand() % 4) == 0; /* 25% calloc */
if (use_calloc)
{
blk->ptr = calloc(sz, 1);
blk->is_calloc = 1;
blk->size = sz;
if (!blk->ptr)
{
fprintf(stderr, "[ERROR] calloc returned NULL\n");
continue;
}
/* we only check zero once; then we switch to pattern mode */
if (!check_zero(blk->ptr, blk->size))
fprintf(stderr, "[ERROR] calloc block not zeroed (idx=%d)\n", idx);
blk->pattern = (unsigned char)((PATTERN_BASE + idx) & 0xFF);
fill_pattern(blk->ptr, blk->size, blk->pattern);
blk->is_calloc = 0;
}
else
{
blk->ptr = malloc(sz);
blk->is_calloc = 0;
blk->size = sz;
if (!blk->ptr)
{
fprintf(stderr, "[ERROR] malloc returned NULL\n");
continue;
}
blk->pattern = (unsigned char)((PATTERN_BASE + idx) & 0xFF);
fill_pattern(blk->ptr, blk->size, blk->pattern);
}
}
else
{
/* grow via realloc */
size_t old_size = blk->size;
size_t new_sz = old_size + 1 + (rand() % (old_size + 1));
unsigned char pattern = blk->pattern;
void *new_ptr = realloc(blk->ptr, new_sz);
if (!new_ptr)
{
fprintf(stderr, "[ERROR] realloc (grow) returned NULL\n");
continue; /* keep old block as-is */
}
blk->ptr = new_ptr;
blk->size = new_sz;
blk->pattern = pattern;
/* Only the first old_size bytes must be preserved */
if (!check_pattern(blk->ptr, old_size, pattern))
{
fprintf(stderr, "[ERROR] pattern mismatch after realloc grow (idx=%d)\n", idx);
abort();
}
/* Now enforce our invariant for the future */
fill_pattern(blk->ptr, blk->size, blk->pattern);
}
}
else if (op == 1)
{
/* free */
if (blk->ptr)
{
free(blk->ptr);
blk->ptr = NULL;
blk->size = 0;
blk->is_calloc = 0;
}
}
else if (op == 2)
{
/* explicit realloc up (if exists) */
if (blk->ptr)
{
size_t old_size = blk->size;
size_t new_sz = old_size + 1 + (rand() % (old_size + 1));
unsigned char pattern = blk->pattern;
void *new_ptr = realloc(blk->ptr, new_sz);
if (!new_ptr)
{
fprintf(stderr, "[ERROR] realloc (grow2) returned NULL\n");
continue;
}
blk->ptr = new_ptr;
blk->size = new_sz;
blk->pattern = pattern;
if (!check_pattern(blk->ptr, old_size, pattern))
{
fprintf(stderr, "[ERROR] pattern mismatch after realloc grow2 (idx=%d)\n", idx);
abort();
}
fill_pattern(blk->ptr, blk->size, blk->pattern);
}
}
else /* op == 3, realloc down */
{
if (blk->ptr && blk->size > 1)
{
size_t old_size = blk->size;
size_t new_sz = 1 + (rand() % blk->size);
unsigned char pattern = blk->pattern;
void *new_ptr = realloc(blk->ptr, new_sz);
if (!new_ptr)
{
fprintf(stderr, "[ERROR] realloc (shrink) returned NULL\n");
continue;
}
blk->ptr = new_ptr;
blk->size = new_sz;
blk->pattern = pattern;
/* For shrink, we only expect the first new_sz bytes to still match */
if (!check_pattern(blk->ptr, blk->size, blk->pattern))
{
fprintf(stderr, "[ERROR] pattern mismatch after realloc shrink (idx=%d)\n", idx);
abort();
}
/* Still keep invariant explicitly (optional here) */
fill_pattern(blk->ptr, blk->size, blk->pattern);
}
}
if ((iter % 50000) == 0 && iter != 0)
printf(" random_stress progress: %d / %d\n", iter, RANDOM_ITERS);
}
free_all_blocks();
printf("random_stress: DONE\n\n");
}
/* Fragmentation test: allocate many small then free some, then big allocs, etc. */
static void fragmentation_test(void)
{
printf("== fragmentation_test ==\n");
const int count = 10000;
void **arr = malloc(sizeof(void *) * count);
size_t *sizes = malloc(sizeof(size_t) * count);
if (!arr || !sizes)
{
fprintf(stderr, "[ERROR] fragmentation_test: malloc failed\n");
free(arr);
free(sizes);
return;
}
/* Step 1: allocate random small blocks */
for (int i = 0; i < count; ++i)
{
size_t sz = 1 + (rand() % SMALL_MAX);
arr[i] = malloc(sz);
sizes[i] = sz;
if (!arr[i])
{
fprintf(stderr, "[ERROR] fragmentation_test: malloc failed at i=%d\n", i);
continue;
}
memset(arr[i], 0x5A, sz);
}
/* Step 2: free every other block */
for (int i = 0; i < count; i += 2)
{
if (arr[i])
{
free(arr[i]);
arr[i] = NULL;
sizes[i] = 0;
}
}
/* Step 3: allocate a bunch of bigger blocks, try to reuse fragmentation */
const int big_count = 1000;
void **big = malloc(sizeof(void *) * big_count);
size_t *big_sz = malloc(sizeof(size_t) * big_count);
if (!big || !big_sz)
{
fprintf(stderr, "[ERROR] fragmentation_test: big malloc failed\n");
free(big);
free(big_sz);
free(arr);
free(sizes);
return;
}
for (int i = 0; i < big_count; ++i)
{
size_t sz = SMALL_MAX + (rand() % (SMALL_MAX * 4));
big[i] = malloc(sz);
big_sz[i] = sz;
if (!big[i])
{
fprintf(stderr, "[ERROR] fragmentation_test: big malloc failed at i=%d\n", i);
continue;
}
memset(big[i], 0x6B, sz);
}
/* cleanup */
for (int i = 0; i < count; ++i)
free(arr[i]);
for (int i = 0; i < big_count; ++i)
free(big[i]);
free(arr);
free(sizes);
free(big);
free(big_sz);
printf("fragmentation_test: DONE\n\n");
}
int main(void)
{
srand((unsigned int)time(NULL));
basic_tests();
random_stress();
fragmentation_test();
printf("ALL TESTS DONE\n");
return 0;
}