I wrote this code for a dynamic stack and would like to know what would be better if done differently.
It can increase memory by a percentage and/or fixed value when needed. It can also be set to not increase automatically at all, so slots are only allocated by the user.
The stack is just a structure holding any data type, so it can change what kind of data is stored without deallocating memory.
Header definitions
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define MINIMUM_ELEMENT_SIZE (1)
#define MULTIPLIER (1.00)
#define EXPANSION_AMOUNT (8)
typedef struct {
void *content;
size_t element_size;
size_t count;
size_t slots;
float multiplier;
size_t expansion_amount;
} Stack;
Some inline functions to do very simple things
static inline void *memory_from_position(Stack *stack, size_t position) __attribute__((always_inline));
static inline size_t required_memory_for_slots(Stack *stack, size_t n_slots) __attribute__((always_inline));
static inline size_t how_many_slots_can_fit(size_t memory_size, size_t slot_size) __attribute__((always_inline));
static inline size_t available_slots(Stack *stack) __attribute__((always_inline));
//Return memory address based on position supplied, starts at 0, no error
//checking, caller must be sure the position is in range
static inline void *memory_from_position(Stack *stack, size_t position)
{
return stack->content + stack->element_size * position;
}
//Return how much memory n_slots would occupy
static inline size_t required_memory_for_slots(Stack *stack, size_t n_slots)
{
return stack->element_size * n_slots;
}
//Return how many slots can fit into memory
static inline size_t how_many_slots_can_fit(size_t memory_size, size_t slot_size)
{
return memory_size / slot_size;
}
//Return how many slots there are left
static inline size_t available_slots(Stack *stack)
{
return stack->slots - stack->count;
}
Main functions
//Allocate and initialize stack
Stack *new_stack(size_t element_size)
{
if(element_size < MINIMUM_ELEMENT_SIZE){
return NULL;
}
Stack *temp = malloc(sizeof(Stack));
if(temp == NULL){
return NULL;
}
temp->content = NULL;
temp->element_size = element_size;
temp->count = 0;
temp->slots = 0;
temp->multiplier = MULTIPLIER;
temp->expansion_amount = EXPANSION_AMOUNT;
return temp;
}
void delete_stack(Stack *stack)
{
free(stack->content);
free(stack);
}
//Keep stack structure, free contents
void release_stack_resources(Stack *stack)
{
free(stack->content);
clear_stack(stack);
stack->content = NULL;
stack->slots = 0;
}
void clear_stack(Stack *stack)
{
stack->count = 0;
}
//Use an already allocated stack to hold a new data type, keep old multiplier and
//expansion amount, a few bytes may become unusable until a memory operation
//is performed, always succeed
void repurpose_stack(Stack *stack, size_t new_element_size)
{
//Calculate available memory
size_t available_memory = required_memory_for_slots(stack, stack->slots);
//Update slots count and stack
clear_stack(stack);
stack->slots = how_many_slots_can_fit(available_memory, new_element_size);
stack->element_size = new_element_size;
}
//Return multiplier set, must be positive
float set_stack_multiplier(Stack *stack, float new_multiplier)
{
if(new_multiplier >= 0){
stack->multiplier = new_multiplier;
}
return stack->multiplier;
}
//Can be set to 0
void set_stack_expansion_amount(Stack *stack, size_t expansion_amount)
{
stack->expansion_amount = expansion_amount;
}
//Allocate memory for more slots and return new memory location
void *add_slots(Stack *stack, size_t new_slots)
{
void *temp = realloc(stack->content, required_memory_for_slots(stack, stack->slots + new_slots));
if(temp == NULL){
return NULL;
}
stack->content = temp;
stack->slots += new_slots;
return temp;
}
//Remove slots whether they are empty or not, if the number is greater than
//the amount of slots, it removes all. Return pointer to stack or NULL
Stack *remove_slots(Stack *stack, size_t amount)
{
//Handle special case of removing all slots
if(amount >= stack->slots){
release_stack_resources(stack);
return stack;
}
//Regular usage
size_t slots_to_keep = stack->slots - amount;
void *temp = realloc(stack->content, required_memory_for_slots(stack, slots_to_keep));
if(temp == NULL){
return NULL;
}
//Update stack info
stack->content = temp;
stack->slots = slots_to_keep;
//Set back count if its overflowing
if(stack->count > stack->slots){
stack->count = stack->slots;
}
return stack;
}
//Expand stack memory by a percentage or fixed amount, whichever is greater
static void *expand_stack_memory(Stack *stack)
{
//If both multiplier and expansion amount evaluate to 0 slots, the stack cannot expand
//automatically, then a call to add_slots must be made to enable the stack to work
size_t new_slots = (float)stack->slots * stack->multiplier;
if(new_slots < stack->expansion_amount){
new_slots = stack->expansion_amount;
}
if(new_slots == 0){
return NULL;
}
return add_slots(stack, new_slots);
}
//Element must have size identical to element_size or stack will break
void *push(Stack *stack, void *element)
{
//Check if there's space, try to allocate more if there isn't
if(available_slots(stack) == 0 && expand_stack_memory(stack) == NULL){
return NULL;
}
//Copy element to stack and return its location
return memcpy(memory_from_position(stack, stack->count++), element, stack->element_size);
}
void *pop(Stack *stack)
{
return (stack->count > 0) ? memory_from_position(stack, --stack->count) : NULL;
}
//Look what element is at n_levels deep without removing it, 0 being the top element
void *peek(Stack *stack, size_t n_levels)
{
if(n_levels >= stack->count){
return NULL;
}
return memory_from_position(stack, stack->count - 1 - n_levels);
}
//Shrink allocated memory to fit current elements, return stack or NULL
Stack *shrink_stack_to_fit(Stack *stack)
{
size_t remove_total = stack->slots - stack->count;
return (remove_total > 0) ? remove_slots(stack, remove_total) : stack;
}
//Might be useful for printing all data or collecting it without removing
void stack_iterate(Stack *stack, void(*callback)(void*))
{
for(size_t i = 0; i < stack->count; ++i){
callback(memory_from_position(stack, stack->count - 1 - i));
}
}