#include "common_allocator.h"
#include <stdlib.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdbool.h>
static atomic_size_t total_allocations = 0;
static atomic_size_t total_deallocations = 0;
static atomic_size_t current_bytes = 0;
static atomic_size_t peak_bytes = 0;
static atomic_size_t total_allocated_bytes = 0;
static atomic_size_t total_deallocated_bytes = 0;
static atomic_bool tracking_enabled = false;
typedef struct {
size_t size;
size_t magic; } alloc_header_t;
#define ALLOC_MAGIC 0xDEADBEEF
#define HEADER_SIZE sizeof(alloc_header_t)
static void* (*original_malloc)(size_t) = NULL;
static void (*original_free)(void*) = NULL;
static bool tracking_initialized = false;
#ifdef EXP_RS_CUSTOM_ALLOC
static void* rust_heap_memory = NULL;
static size_t rust_heap_size = 1024 * 1024; #endif
extern size_t exp_rs_get_total_allocated(void);
extern size_t exp_rs_get_total_freed(void);
extern size_t exp_rs_get_allocation_count(void);
extern size_t exp_rs_get_free_count(void);
extern size_t exp_rs_get_current_allocated(void);
static atomic_bool custom_allocator_used = false;
bool using_custom_allocator() {
return atomic_load(&custom_allocator_used);
}
void init_memory_tracking() {
if (!tracking_initialized) {
#ifdef EXP_RS_CUSTOM_ALLOC
if (rust_heap_memory == NULL) {
rust_heap_memory = malloc(rust_heap_size);
if (rust_heap_memory == NULL) {
printf("ERROR: Failed to allocate heap memory\n");
return;
}
}
exp_rs_heap_init((uint8_t*)rust_heap_memory, rust_heap_size);
#endif
#ifdef __APPLE__
original_malloc = malloc; original_free = free;
#else
original_malloc = malloc;
original_free = free;
#endif
tracking_initialized = true;
}
atomic_store(&total_allocations, 0);
atomic_store(&total_deallocations, 0);
atomic_store(¤t_bytes, 0);
atomic_store(&peak_bytes, 0);
atomic_store(&total_allocated_bytes, 0);
atomic_store(&total_deallocated_bytes, 0);
}
void enable_allocation_tracking() {
atomic_store(&tracking_enabled, true);
}
void disable_allocation_tracking() {
atomic_store(&tracking_enabled, false);
}
static void* tracked_malloc(size_t size) {
void* ptr = original_malloc(size + HEADER_SIZE);
if (ptr && atomic_load(&tracking_enabled)) {
alloc_header_t* header = (alloc_header_t*)ptr;
header->size = size;
header->magic = ALLOC_MAGIC;
atomic_fetch_add(&total_allocations, 1);
atomic_fetch_add(&total_allocated_bytes, size);
size_t new_current = atomic_fetch_add(¤t_bytes, size) + size;
size_t current_peak = atomic_load(&peak_bytes);
while (new_current > current_peak) {
if (atomic_compare_exchange_weak(&peak_bytes, ¤t_peak, new_current)) {
break;
}
}
return (char*)ptr + HEADER_SIZE;
}
return ptr;
}
static void tracked_free(void* ptr) {
if (!ptr) return;
if (atomic_load(&tracking_enabled)) {
alloc_header_t* header = (alloc_header_t*)((char*)ptr - HEADER_SIZE);
if (header->magic == ALLOC_MAGIC) {
size_t size = header->size;
header->magic = 0;
atomic_fetch_add(&total_deallocations, 1);
atomic_fetch_add(&total_deallocated_bytes, size);
atomic_fetch_sub(¤t_bytes, size);
original_free(header);
} else {
original_free(ptr);
}
} else {
original_free(ptr);
}
}
void* exp_rs_malloc(size_t size) {
atomic_store(&custom_allocator_used, true);
if (!tracking_initialized) {
init_memory_tracking();
}
if (atomic_load(&tracking_enabled)) {
return tracked_malloc(size);
} else {
return malloc(size);
}
}
void exp_rs_free(void* ptr) {
atomic_store(&custom_allocator_used, true);
if (!tracking_initialized) {
init_memory_tracking();
}
if (atomic_load(&tracking_enabled)) {
tracked_free(ptr);
} else {
free(ptr);
}
}
void mark_rust_allocation_start() {
}
void mark_rust_allocation_end() {
}
memory_stats_t get_memory_stats() {
memory_stats_t stats;
if (using_custom_allocator()) {
stats.total_allocs = atomic_load(&total_allocations);
stats.total_deallocs = atomic_load(&total_deallocations);
stats.current_bytes = atomic_load(¤t_bytes);
stats.peak_bytes = atomic_load(&peak_bytes);
stats.total_allocated_bytes = atomic_load(&total_allocated_bytes);
stats.total_deallocated_bytes = atomic_load(&total_deallocated_bytes);
stats.leaked_allocs = stats.total_allocs - stats.total_deallocs;
} else {
stats.total_allocs = exp_rs_get_allocation_count();
stats.total_deallocs = exp_rs_get_free_count();
stats.current_bytes = exp_rs_get_current_allocated();
stats.peak_bytes = 0; stats.total_allocated_bytes = exp_rs_get_total_allocated();
stats.total_deallocated_bytes = exp_rs_get_total_freed();
stats.leaked_allocs = stats.total_allocs - stats.total_deallocs;
}
return stats;
}
void print_memory_stats(const char* phase) {
memory_stats_t stats = get_memory_stats();
printf("Memory Stats [%s]:\n", phase);
printf(" Allocations: %zu\n", stats.total_allocs);
printf(" Deallocations: %zu\n", stats.total_deallocs);
printf(" Current bytes: %zu\n", stats.current_bytes);
printf(" Peak bytes: %zu (%.1f KB)\n", stats.peak_bytes, stats.peak_bytes / 1024.0);
printf(" Total allocated: %zu (%.1f KB)\n", stats.total_allocated_bytes, stats.total_allocated_bytes / 1024.0);
printf(" Leaked allocations: %zu\n", stats.leaked_allocs);
}
void reset_memory_stats() {
atomic_store(&total_allocations, 0);
atomic_store(&total_deallocations, 0);
atomic_store(¤t_bytes, 0);
atomic_store(&peak_bytes, 0);
atomic_store(&total_allocated_bytes, 0);
atomic_store(&total_deallocated_bytes, 0);
}