Maestro is a lightweight and efficient C library that implements a generic dynamic array data structure. It provides an elegant solution for managing collections of elements of any data type, offering flexible and efficient array manipulation in memory.
- Type Agnostic: Works with any data type through void* pointers
- Dynamic Memory Management: Automatic memory allocation and reallocation as needed
- Intuitive API: Clean and consistent interface for collection manipulation
- Comprehensive Operations: Support for fundamental operations like insertion, deletion, and appending
Maestro handles all memory management internally, including:
- Initial allocation during creation
- Dynamic resizing when elements are added
- Proper cleanup when elements are removed
- Complete deallocation during destruction
This automatic memory management helps prevent memory leaks while maintaining optimal performance.
Maestro implements a contiguous dynamic array using a void pointer (void* data
) as its primary storage mechanism. This approach allows for storing any data type while maintaining memory efficiency and direct access capabilities.
The library stores elements in contiguous memory locations, which means all elements are placed next to each other in memory. When you store elements in data
, they are placed sequentially, with each element occupying element_size
bytes. To access an element at position i
, Maestro performs pointer arithmetic: data + (i * element_size)
.
- Cache Efficiency: Contiguous storage means better cache utilization and faster memory access
- Type Flexibility: Using
void*
allows storing any data type - Direct Access: Fast access time to any element using pointer arithmetic
- Reallocation Cost: Adding elements may require reallocating and copying the entire array
- Type Safety: Using
void*
means type checking must be handled by the user - Memory Limitations: Requires a single contiguous block of memory
Maestro can be easily installed by downloading the last version and using the provided installation script:
./install.sh
This script will:
- Copy the header file to the appropriate system include directory
- Install the static library
libmaestro.a
to the system library path - Set up the necessary permissions
To use Maestro in your C project, include the header file:
#include <maestro/maestro.h>
When compiling your project, you need to link against the Maestro library.
If you're using a Makefile, add the library flag:
LDFLAGS += -lmaestro
Or when compiling directly with gcc:
gcc your_program.c -o your_program -lmaestro
This will link your program with the Maestro library, giving you access to all its dynamic array functionality.
First, let's define our structure:
typedef struct {
int id;
double value;
const char* name;
} Item;
Creates a new dynamic array with the specified element size.
// Create an array for our Items
maestro* items = maestro_new(sizeof(Item));
Adds a new element at the end of the array. The array will automatically resize if needed.
Item new_item = {1, 3.14, "First"};
items->push_back(items, &new_item);
Removes the last element from the array. If the array becomes empty, it frees the memory.
items->pop_back(items); // Removes the last Item
Inserts a new element at the specified position, shifting all subsequent elements one position forward.
Item insert_item = {2, 2.71, "Second"};
items->insert(items, 1, &insert_item); // Inserts at position 1
Removes an element at the specified position, shifting all subsequent elements one position backward.
items->erase(items, 0); // Removes Item at position 0
Frees all allocated memory associated with the array and the array itself.
items->destroy(items); // Cleans up the array
#include <maestro/maestro.h>
typedef struct {
int id;
double value;
const char* name;
} Item;
int main()
{
// Create the array
maestro* items = maestro_new(sizeof(Item));
// Create some Items
Item a = {1, 1.1, "First"};
Item b = {2, 2.2, "Second"};
Item c = {3, 3.3, "Third"};
Item d = {4, 4.4, "Fourth"};
// Add elements
items->push_back(items, &a); // [{1, 1.1, "First"}]
items->push_back(items, &b); // [{1, 1.1, "First"}, {2, 2.2, "Second"}]
items->push_back(items, &c); // [{1, 1.1, "First"}, {2, 2.2, "Second"}, {3, 3.3, "Third"}]
// Remove last element
items->pop_back(items); // [{1, 1.1, "First"}, {2, 2.2, "Second"}]
// Insert at position 1
items->insert(items, 1, &c); // [{1, 1.1, "First"}, {3, 3.3, "Third"}, {2, 2.2, "Second"}]
// Add another element
items->push_back(items, &d); // [{1, 1.1, "First"}, {3, 3.3, "Third"}, {2, 2.2, "Second"}, {4, 4.4, "Fourth"}]
// Erase element at position 2
items->erase(items, 2); // [{1, 1.1, "First"}, {3, 3.3, "Third"}, {4, 4.4, "Fourth"}]
// Display array contents
for (size_t i = 0; i < items->length; i++) {
Item current = ((Item*)items->data)[i];
printf("Id: %d, Value: %.1f, Name: %s\n",
current.id, current.value, current.name);
}
// Clean up
items->destroy(items);
return 0;
}