Dynamic array container for embedded systems using dalloc memory allocator.
Version: 1.3.0 License: Apache 2.0 Author: Alexey Vasilenko
- Dynamic resizing with automatic capacity growth (20% reserve)
- Support for POD and non-POD types with proper constructor/destructor calls
- Two modes: multi-heap (explicit heap pointer) and single-heap (global default)
- STL-like interface (
at,front,back,push_back,pop_back, etc.) - Memory defragmentation support via dalloc
- No memory fragmentation issues for long-running embedded applications
uvector requires dalloc v1.5.0+ memory allocator.
| Constructor | Description |
|---|---|
uvector() |
Default constructor (single-heap mode only) |
uvector(heap_t* heap) |
Constructor with heap pointer (multi-heap mode) |
uvector(uint32_t size, heap_t* heap) |
Constructor with initial capacity |
uvector(const uvector& other) |
Copy constructor |
| Method | Description |
|---|---|
T& at(uint32_t i) |
Access element with bounds checking |
T& operator[](uint32_t i) |
Access element without bounds checking |
T& front() |
Access first element |
T& back() |
Access last element |
T* data() |
Direct access to underlying array |
| Method | Description |
|---|---|
bool empty() |
Check if container is empty |
uint32_t size() |
Return number of elements |
uint32_t capacity() |
Return current capacity |
bool reserve(uint32_t n) |
Reserve capacity for n elements |
bool shrink_to_fit() |
Reduce capacity to match size |
| Method | Description |
|---|---|
bool push_back(T value) |
Add element to end |
bool pop_back() |
Remove last element (calls destructor) |
bool pop(uint32_t i) |
Remove element at index (calls destructor) |
void clear() |
Remove all elements (calls destructors) |
bool resize(uint32_t n) |
Change size (calls destructors when shrinking) |
bool resize(uint32_t n, T value) |
Change size with fill value |
| Method | Description |
|---|---|
uvector& operator=(const uvector& other) |
Copy assignment |
uvector properly manages object lifecycles for non-POD types:
- Construction: Objects are constructed using placement new when capacity is reserved
- Destruction: Destructors are called when:
- Elements are removed via
pop_back(),pop(i),clear() - Size is reduced via
resize() - Memory is freed via
shrink_to_fit()or destructor
- Elements are removed via
- Copy: Copy constructors/assignment operators are used for element copying
This makes uvector safe for storing objects that manage resources (file handles, GPIO, peripherals, etc.).
Enable single-heap mode via compiler flags or custom config file:
# Via compiler flags
gcc -DUSE_SINGLE_HEAP_MEMORY ...
# Or via custom config file
gcc -DDALLOC_CUSTOM_CONF_FILE=\"my_dalloc_conf.h\" ...// my_dalloc_conf.h
#define USE_SINGLE_HEAP_MEMORYRegister heap buffer before using uvector:
#include "uvector.h"
// Buffer must be 4-byte aligned!
__attribute__((aligned(4)))
static uint8_t heap_buffer[4096];
int main() {
// Register heap (must be called before any uvector operations)
if (!dalloc_register_heap(heap_buffer, sizeof(heap_buffer))) {
return -1;
}
uvector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
for (uint32_t i = 0; i < vec.size(); i++) {
printf("Element %u: %d\n", i, vec.at(i));
}
return 0;
}When USE_SINGLE_HEAP_MEMORY is not defined, use explicit heap pointers:
#include "uvector.h"
#define HEAP_SIZE 1024
// Buffers must be 4-byte aligned!
__attribute__((aligned(4)))
static uint8_t buffer1[HEAP_SIZE];
__attribute__((aligned(4)))
static uint8_t buffer2[HEAP_SIZE];
heap_t heap1, heap2;
int main() {
heap_init(&heap1, buffer1, sizeof(buffer1));
heap_init(&heap2, buffer2, sizeof(buffer2));
uvector<int> vec1(&heap1);
uvector<int> vec2(&heap2);
vec1.push_back(10);
vec2.push_back(20);
// Cleanup
heap_deinit(&heap1);
heap_deinit(&heap2);
return 0;
}uvector works with classes that have constructors/destructors:
class Sensor {
public:
int gpio_pin;
Sensor() : gpio_pin(-1) {}
Sensor(int pin) : gpio_pin(pin) { init_gpio(pin); }
~Sensor() { if (gpio_pin >= 0) deinit_gpio(gpio_pin); }
};
uvector<Sensor> sensors(&heap);
sensors.push_back(Sensor(GPIO_PIN_1));
sensors.push_back(Sensor(GPIO_PIN_2));
sensors.pop_back(); // Destructor called, GPIO deinitialized
sensors.clear(); // All destructors called// ESP32: Use internal SRAM for fast access
__attribute__((section(".iram1"), aligned(4)))
static uint8_t fast_heap[4096];
// STM32F4/F3 (F405, F407, F303, etc.): Use CCM RAM (64KB, CPU-only, no DMA)
__attribute__((section(".ccmram"), aligned(4)))
static uint8_t ccm_heap[8192];
// STM32: Generic aligned buffer
__ALIGN_BEGIN uint8_t heap_buffer[4096] __ALIGN_END;
// Any platform: ensure 4-byte alignment
alignas(4) static uint8_t heap_buffer[4096];When pushing elements one by one, maximum capacity is approximately (HEAP_SIZE / sizeof(T)) / 2 due to reallocation. Pre-reserve to maximize usage:
uvector<int> vec(&heap);
vec.reserve(100); // Reserve space for 100 elements upfront
// Or at construction:
uvector<int> vec2(100, &heap);Use dalloc debug functions to inspect heap state:
// Multi-heap mode
print_dalloc_info(&heap);
dump_dalloc_ptr_info(&heap);
dump_heap(&heap);
// Single-heap mode
print_def_dalloc_info();
dump_def_dalloc_ptr_info();
dump_def_heap();
// Check heap state
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);
}uvector inherits configuration from dalloc. Key options:
| Option | Default | Description |
|---|---|---|
USE_SINGLE_HEAP_MEMORY |
undefined | Enable single-heap mode |
MAX_NUM_OF_ALLOCATIONS |
100 | Maximum simultaneous allocations |
ALLOCATION_ALIGNMENT_BYTES |
4 | Memory alignment (typically 4 for ARM) |
USE_THREAD_SAFETY |
undefined | Enable thread-safe operations |
Configure via compiler flags:
gcc -DUSE_SINGLE_HEAP_MEMORY -DMAX_NUM_OF_ALLOCATIONS=50 ...Or via custom config file:
gcc -DDALLOC_CUSTOM_CONF_FILE=\"my_dalloc_conf.h\" ...uvector includes comprehensive unit tests using Google Test:
cd libs/uvector/tests
mkdir build && cd build
cmake ..
make -j$(nproc)
./uvector_tests # Multi-heap tests (180 tests)
./uvector_single_heap_tests # Single-heap tests (23 tests)Tests cover:
- Basic operations (construction, push, pop, access)
- Capacity management (reserve, resize, shrink_to_fit)
- Copy semantics (copy constructor, assignment)
- Object lifecycle (constructor/destructor calls)
- Edge cases (empty vector, bounds, heap exhaustion)
- Stress tests (rapid push/pop, multiple vectors)
- Integrity checks (double destruction detection, memory corruption)
All tests run with AddressSanitizer and UndefinedBehaviorSanitizer enabled.
Apache License 2.0. See LICENSE for details.