SkillAgentSearch skills...

Dalloc

This is the custom implementation of function malloc for embedded systems, that defragmentate memory after using it. Good solution when you need to allocate memory dynamically, but memory fragmentation is the problem.

Install / Use

/learn @SkyEng1neering/Dalloc

README

dalloc - Defragmenting Memory Allocator

Version: 1.5.0

A custom memory allocator for embedded systems with automatic defragmentation. Unlike standard malloc, dalloc tracks pointer addresses and can move allocations during defragmentation, updating all affected pointers automatically.

Libraries built on dalloc: uvector, ustring, usmartpointer.

The Problem dalloc Solves

Standard malloc - fragmentation issue:

malloc fragmentation

On the last step there are 7 kB free but not contiguous, so allocating 4 kB fails.

dalloc - automatic defragmentation:

dalloc defragmentation

How It Works

dalloc saves the address of the pointer (not just its value). When memory is freed, defragmentation runs automatically and updates all affected pointers to their new locations.

Important: This means the pointer variable must remain valid until dfree() is called.

Quick Start

Option 1: Single Global Heap (Recommended for simple projects)

#include "dalloc.h"

// Place buffer in specific memory section if needed
__attribute__((aligned(4)))  // Ensure 4-byte alignment!
static uint8_t heap_buffer[4096];

int main() {
    // Register the heap buffer (must be called before any allocation)
    if (!dalloc_register_heap(heap_buffer, sizeof(heap_buffer))) {
        return -1;  // Registration failed
    }

    // Allocate memory
    uint8_t *data = NULL;
    def_dalloc(64, (void**)&data);

    if (data == NULL) {
        return -1;  // Allocation failed
    }

    // Use allocated memory
    for (int i = 0; i < 64; i++) {
        data[i] = i;
    }

    // Free memory (data becomes NULL)
    def_dfree((void**)&data);

    return 0;
}

Option 2: Multiple Heaps (Explicit API)

#include "dalloc.h"

__attribute__((aligned(4)))
static uint8_t buffer1[2048];
static uint8_t buffer2[2048];

heap_t heap_fast;  // For frequently accessed data
heap_t heap_slow;  // For less critical data

int main() {
    heap_init(&heap_fast, buffer1, sizeof(buffer1));
    heap_init(&heap_slow, buffer2, sizeof(buffer2));

    // Allocate from specific heap
    uint8_t *critical_data = NULL;
    dalloc(&heap_fast, 256, (void**)&critical_data);

    uint8_t *temp_data = NULL;
    dalloc(&heap_slow, 512, (void**)&temp_data);

    // Use memory...

    // Free when done
    dfree(&heap_fast, (void**)&critical_data, USING_PTR_ADDRESS);
    dfree(&heap_slow, (void**)&temp_data, USING_PTR_ADDRESS);

    // Cleanup heaps
    heap_deinit(&heap_fast);
    heap_deinit(&heap_slow);

    return 0;
}

Examples

Reallocation

uint8_t *buffer = NULL;
def_dalloc(32, (void**)&buffer);

// Fill with data
for (int i = 0; i < 32; i++) {
    buffer[i] = i;
}

// Grow the buffer - data is preserved
if (def_drealloc(64, (void**)&buffer)) {
    // buffer now has 64 bytes, first 32 contain original data
    buffer[32] = 0xFF;  // Use new space
}

def_dfree((void**)&buffer);

Transferring Pointer Ownership

When you need to move an allocation to a different pointer variable:

uint8_t *temp_ptr = NULL;
def_dalloc(64, (void**)&temp_ptr);

// Work with temp_ptr...
temp_ptr[0] = 0x42;

// Transfer to permanent storage
uint8_t *permanent_ptr = NULL;
def_replace_pointers((void**)&temp_ptr, (void**)&permanent_ptr);
// Now: temp_ptr == NULL, permanent_ptr points to the memory

// Later...
def_dfree((void**)&permanent_ptr);

Bulk Reset (Clear All Allocations)

// Array of pointers - must persist until free/reset
uint8_t *objects[100];

// Allocate many objects
for (int i = 0; i < 100; i++) {
    objects[i] = NULL;
    def_dalloc(16, (void**)&objects[i]);
    if (objects[i]) {
        objects[i][0] = i;  // Use allocated memory
    }
}

// Instead of freeing each one, reset the entire heap
dalloc_reset_heap();
// All allocations are cleared, heap is empty
// WARNING: All pointers in objects[] are now invalid!

Checking Heap State

if (!dalloc_is_initialized()) {
    dalloc_register_heap(buffer, sizeof(buffer));
}

heap_t *heap = dalloc_get_default_heap();
if (heap != NULL) {
    printf("Used: %lu / %lu bytes\n",
           (unsigned long)heap->offset,
           (unsigned long)heap->total_size);
    printf("Allocations: %lu\n",
           (unsigned long)heap->alloc_info.allocations_num);
}

ESP32/STM32: Using Specific Memory Regions

// ESP32: Use IRAM for fast access
__attribute__((section(".iram1")))
static uint8_t fast_heap[4096];

// STM32: Use CCM RAM
__attribute__((section(".ccmram")))
static uint8_t ccm_heap[8192];

// External SRAM (if available)
__attribute__((section(".sdram")))
static uint8_t large_heap[65536];

void init_heaps(void) {
    dalloc_register_heap(fast_heap, sizeof(fast_heap));
}

API Reference

Single Heap API (USE_SINGLE_HEAP_MEMORY)

| Function | Description | |----------|-------------| | dalloc_register_heap(buffer, size) | Register user buffer as default heap | | dalloc_unregister_heap(force) | Unregister heap. If force=false, fails when allocations exist | | dalloc_reset_heap() | Clear all allocations, reinitialize heap | | dalloc_is_initialized() | Check if heap is registered | | dalloc_get_default_heap() | Get pointer to heap_t structure | | def_dalloc(size, &ptr) | Allocate memory | | def_dfree(&ptr) | Free memory | | def_drealloc(size, &ptr) | Reallocate memory | | def_replace_pointers(&old, &new) | Transfer allocation to new pointer |

Multi-Heap API

| Function | Description | |----------|-------------| | heap_init(heap, buffer, size) | Initialize heap with buffer | | heap_deinit(heap) | Deinitialize heap, release resources | | dalloc(heap, size, &ptr) | Allocate from specific heap | | dfree(heap, &ptr, condition) | Free from specific heap | | drealloc(heap, size, &ptr) | Reallocate in specific heap | | replace_pointers(heap, &old, &new) | Transfer allocation | | validate_ptr(heap, &ptr, condition, &index) | Check if pointer is valid |

Debug Functions

| Function | Description | |----------|-------------| | print_dalloc_info(heap) / print_def_dalloc_info() | Print heap statistics | | dump_heap(heap) / dump_def_heap() | Hex dump of heap memory | | dump_dalloc_ptr_info(heap) / dump_def_dalloc_ptr_info() | Print allocation details |

Configuration

You can override default settings without modifying dalloc_conf.h:

Method 1: Compiler flags

gcc -DMAX_NUM_OF_ALLOCATIONS=200 -DUSE_SINGLE_HEAP_MEMORY ...

Method 2: Custom config file

gcc -DDALLOC_CUSTOM_CONF_FILE=\"my_dalloc_conf.h\" ...
// my_dalloc_conf.h - only override what you need
#define MAX_NUM_OF_ALLOCATIONS      50
#define USE_SINGLE_HEAP_MEMORY
#define dalloc_debug(...)           // disable debug output

Configuration Options

| Option | Default | Description | |--------|---------|-------------| | MAX_NUM_OF_ALLOCATIONS | 100 | Maximum simultaneous allocations | | USE_ALIGNMENT | defined | Enable pointer alignment | | ALLOCATION_ALIGNMENT_BYTES | 4 | Alignment boundary (typically 4 for ARM) | | FILL_FREED_MEMORY_BY_NULLS | true | Zero memory on free (security) | | USE_SINGLE_HEAP_MEMORY | undefined | Enable single heap mode | | USE_THREAD_SAFETY | undefined | Enable thread-safe operations | | USE_FREE_RTOS | undefined | Use FreeRTOS mutex primitives | | dalloc_debug | printf | Debug output function |

Thread Safety

dalloc supports optional thread safety via USE_THREAD_SAFETY. Each heap has its own mutex for protection.

FreeRTOS (Easiest)

// my_dalloc_conf.h
#define USE_THREAD_SAFETY
#define USE_FREE_RTOS

dalloc will automatically use xSemaphoreCreateMutex(), xSemaphoreTake(), xSemaphoreGive().

POSIX Threads

// my_dalloc_conf.h
#define USE_THREAD_SAFETY

#include <pthread.h>
#include <stdlib.h>

#define DALLOC_MUTEX_TYPE           pthread_mutex_t*

#define DALLOC_MUTEX_CREATE()       ({ \
    pthread_mutex_t *m = malloc(sizeof(pthread_mutex_t)); \
    if (m) pthread_mutex_init(m, NULL); \
    m; \
})

#define DALLOC_MUTEX_DELETE(mutex)  do { \
    if (mutex) { pthread_mutex_destroy(mutex); free(mutex); } \
} while(0)

#define DALLOC_MUTEX_LOCK(mutex)    do { if (mutex) pthread_mutex_lock(mutex); } while(0)
#define DALLOC_MUTEX_UNLOCK(mutex)  do { if (mutex) pthread_mutex_unlock(mutex); } while(0)

Required Mutex Macros

| Macro | Description | |-------|-------------| | DALLOC_MUTEX_TYPE | The mutex handle type | | DALLOC_MUTEX_CREATE() | Create and return a mutex handle | | DALLOC_MUTEX_DELETE(mutex) | Delete/destroy the mutex | | DALLOC_MUTEX_LOCK(mutex) | Acquire the mutex (blocking) | | DALLOC_MUTEX_UNLOCK(mutex) | Release the mutex |

Important Limitations

Buffer Alignment

The heap buffer must be aligned to ALLOCATION_ALIGNMENT_BYTES (default: 4 bytes):

// CORRECT - explicitly aligned
__attribute__((aligned(4)))
static uint8_t heap_buffer[4096];

// ALSO OK - naturally aligned on most systems
static uint32_t heap_buffer_u32[1024];  // 4096 bytes, 4-byte aligned
static uint8_t *heap_buffer = (uint8_t*)heap_buffer_u32;

Pointer Lifetime

The pointer variable passed to dalloc() must remain in scope until dfree() is called (dalloc stores the address of the variable, not just its value):

// WRONG - data_ptr is destroyed when function returns
uint8_t* bad_function(heap_t *heap) {
    uint8_t* data_ptr = NULL;
    dalloc(heap, 64, (void**)&data_ptr);
    return data_ptr;  // BUG: &data_ptr becomes invalid!
}

// CORRECT - use pointer from caller's scope
bool good_function(heap_t *heap, uint8_t **data_ptr) {
    dalloc(heap, 64, (void**)data_ptr);
    return (*data_
View on GitHub
GitHub Stars19
CategoryDevelopment
Updated4d ago
Forks2

Languages

C++

Security Score

95/100

Audited on Mar 28, 2026

No findings