Here is the stack abstraction that I've written. I've designed it to be safe and without undefined behavior.
I use ugly_cast to discourage casting and to make casts more easily visible.
stack.h:
#ifndef STACK_H
#define STACK_H 1
#include <stddef.h>
#define stack_op(var, op) stack_##op(&(var), sizeof(var), stack_global)
#define stack_op_r(var, op, stack) stack_##op(&(var), sizeof(var), (stack))
#define push(var) stack_op(var, push)
#define pop(var) stack_op(var, pop)
#define peek(var) stack_op(var, peek)
#define push_r(var, stack) stack_op_r(var, push, stack)
#define pop_r(var, stack) stack_op_r(var, pop, stack)
#define peek_r(var, stack) stack_op_r(var, peek, stack)
#define STACK_INIT(size) \
do { \
STACK_FINI(); \
stack_global = stack_create(size); \
} while(0)
#define STACK_INIT_R(size, name) \
do { \
STACK_FINI_R(name); \
name = stack_create(size); \
} while(0)
#define STACK_FINI() stack_destroy(stack_global)
#define STACK_FINI_R(name) stack_destroy(name)
extern struct stack *stack_global;
extern struct stack *stack_create(size_t);
extern int stack_resize(struct stack *, size_t);
extern void stack_destroy(struct stack *);
extern void stack_push(void *, size_t, struct stack *);
extern void stack_pop(void *, size_t, struct stack *);
extern void stack_peek(void *, size_t, struct stack *);
#endif
stack_create.c:
#include <stdio.h>
#include <stack.h>
#include <stdlib.h>
#include <stdlib.h>
#include "stack_internal.h"
struct stack *stack_create(size_t size)
{
struct stack *ret;
if(!size)
{
stack_error("creating stack with size 0");
}
else if(size < 16)
{
stack_warn("creating stack with size less than 16");
}
if((ret = malloc(sizeof(*ret))))
{
ret->beg = malloc(size);
ret->end = ret->cur = ret->beg + size;
}
return ret;
}
stack_destroy.c:
#include <stack.h>
#include <stdlib.h>
#include <string.h>
#include "stack_internal.h"
void stack_destroy(struct stack *stack)
{
if(stack) free(stack->beg);
free(stack);
}
stack_diagnostics.c:
#include <stack.h>
#include <stdio.h>
#include <stdlib.h>
#include "stack_internal.h"
static void stack_diagnose(const char *type, const char *diagnostic)
{
fprintf(stderr, "libstack %s: %s\n", type, diagnostic);
}
void stack_error(const char *str)
{
stack_diagnose("ERROR", str);
abort();
}
void stack_warn(const char *str)
{
stack_diagnose("WARNING", str);
}
stack_global.c:
#include <stack.h>
#include "stack_internal.h"
struct stack *stack_global;
stack_internal.h:
#ifndef STACK_INTERNAL_H
#define STACK_INTERNAL_H 1
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 201112L
# if defined(__cplusplus) && __cplusplus >= 201103L
# define noreturn [[noreturn]]
# else
# ifdef __GNUC__
# define noreturn __attribute__((__noreturn__))
# else
# define noreturn
# endif
# endif
#else
# include <stdnoreturn.h>
#endif
#define ugly_cast(x) (x)
struct stack {
char *beg;
char *cur;
char *end;
};
noreturn void stack_error(const char *);
void stack_warn(const char *);
#endif
stack_peek.c:
#include <stack.h>
#include <string.h>
#include "stack_internal.h"
void stack_peek(void *val, size_t size, struct stack *stack)
{
memcpy(val, stack->cur, size);
}
stack_pop.c:
#include <stdio.h>
#include <stack.h>
#include <stdlib.h>
#include <string.h>
#include "stack_internal.h"
void stack_pop(void *val, size_t size, struct stack *stack)
{
if(ugly_cast(size_t)(stack->end - stack->cur) < size)
{
stack_error("popping past stack boundaries");
}
memcpy(val, stack->cur, size);
stack->cur += size;
}
stack_push.c:
#include <stdio.h>
#include <stack.h>
#include <stdlib.h>
#include <string.h>
#include "stack_internal.h"
void stack_push(void *val, size_t size, struct stack *stack)
{
if(ugly_cast(size_t)(stack->cur - stack->beg) < size)
{
stack_error("pushing past stack boundaries");
}
stack->cur -= size;
memcpy(stack->cur, val, size);
}
stack_resize.c:
#include <stdio.h>
#include <stack.h>
#include <stdlib.h>
#include <string.h>
#include "stack_internal.h"
int stack_resize(struct stack *stack, size_t newsize)
{
char *newptr;
size_t oldsize, off;
oldsize = ugly_cast(size_t)(stack->end - stack->beg);
off = ugly_cast(size_t)(stack->cur - stack->beg);
if(!newsize)
stack_error("resizing stack to size 0");
else if(newsize < 16)
stack_warn("resizing stack to size less than 16");
if(newsize < oldsize)
{
stack_warn("truncating stack");
}
if(off > newsize)
{
stack_warn("truncating offset into stack to new size");
off = newsize;
}
newptr = realloc(stack->beg, newsize);
if(!newptr)
{
free(stack->beg);
return -1;
}
if(newsize > oldsize)
memset(newptr + oldsize, 0, oldsize - newsize);
stack->beg = newptr;
stack->end = stack->beg + newsize;
stack->cur = stack->beg + off;
return 0;
}
Here's an example program that uses the library:
#include <stack.h>
#include <stdlib.h>
int main(void)
{
int err = 0;
STACK_INIT(8);
STACK_FINI();
STACK_INIT(16);
push(err);
push(err);
err = stack_resize(stack_global, 8);
STACK_FINI();
return err != 0;
}
Here's another example program that uses it:
#include <stdio.h>
#include <stack.h>
int main(void)
{
int a = 16, b = 32;
struct stack *stack = 0;
STACK_INIT_R(32, stack);
push_r(a, stack);
push_r(b, stack);
pop_r(a, stack);
pop_r(b, stack);
printf("a=%d, b=%d\n", a, b);
STACK_FINI_R(stack);
return a != 32 || b != 16;
}
Is there anything I can improve?
This project is available under the LGPLv2.1+ on my GitHub site.