603 lines
No EOL
15 KiB
C
603 lines
No EOL
15 KiB
C
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
#include <stddef.h>
|
|
#include <stdbool.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
* Basic usage:
|
|
* #define T int
|
|
* // optional, if not defined all the names will be longer, e.g.
|
|
* // vec_int_t v = vec_intInit();
|
|
* #define VEC_SHORT_NAME veci
|
|
* #include <vec.h> // undefines T
|
|
* [...]
|
|
* veci_t v = veciInit();
|
|
* veciPush(&v, 10);
|
|
* veciEraseAt(&v, 0);
|
|
* veciFree(&v);
|
|
*/
|
|
|
|
typedef int( *vec_cmp_fn_t)(const void *, const void *);
|
|
|
|
#ifndef T
|
|
#error "Must define T before including vec.h"
|
|
#endif
|
|
|
|
#define VEC_CAT(a, b) a ## b
|
|
#define VEC_PASTE(a, b) VEC_CAT(a, b)
|
|
#define TYPE(prefix, type) VEC_PASTE(VEC_PASTE(prefix, _), VEC_PASTE(type, _t))
|
|
#define ITER(prefix, type) VEC_PASTE(VEC_PASTE(prefix, _), VEC_PASTE(type, _it_t))
|
|
|
|
#ifdef VEC_SHORT_NAME
|
|
#define VEC_T VEC_PASTE(VEC_SHORT_NAME, _t)
|
|
#define VEC_IT_T VEC_PASTE(VEC_SHORT_NAME, _it_t)
|
|
#define VEC VEC_SHORT_NAME
|
|
#else
|
|
#define VEC_T TYPE(vec, T)
|
|
#define VEC_IT_T ITER(vec, T)
|
|
#define VEC VEC_PASTE(vec, VEC_PASTE(_, T))
|
|
#endif
|
|
|
|
#define FUN(postfix) VEC_PASTE(VEC, postfix)
|
|
|
|
#ifndef VEC_NO_DECLARATION
|
|
|
|
typedef struct {
|
|
T *buf;
|
|
size_t size;
|
|
size_t allocated;
|
|
} VEC_T;
|
|
|
|
#define vec_foreach(type, name, it, vec) \
|
|
for(type *it = VEC_PASTE(name, Beg)(&vec); it < VEC_PASTE(name, End)(&vec); ++it)
|
|
|
|
VEC_T FUN(Init)(void);
|
|
VEC_T FUN(InitArr)(T *arr, size_t len);
|
|
void FUN(Free)(VEC_T *ctx);
|
|
|
|
VEC_T FUN(Move)(VEC_T *ctx);
|
|
VEC_T FUN(Copy)(VEC_T *ctx);
|
|
|
|
T *FUN(Beg)(VEC_T *ctx);
|
|
T *FUN(End)(VEC_T *ctx);
|
|
T *FUN(Back)(VEC_T *ctx);
|
|
bool FUN(Empty)(VEC_T *ctx);
|
|
|
|
void FUN(Realloc)(VEC_T *ctx, size_t needed);
|
|
void FUN(Reserve)(VEC_T *ctx, size_t newsize);
|
|
void FUN(ShrinkToFit)(VEC_T *ctx);
|
|
|
|
void FUN(Clear)(VEC_T *ctx);
|
|
void FUN(ClearZero)(VEC_T *ctx);
|
|
|
|
void FUN(InsertAt)(VEC_T *ctx, size_t index, T val);
|
|
void FUN(InsertAfter)(VEC_T *ctx, T *it, T val);
|
|
void FUN(InsertBefore)(VEC_T *ctx, T *it, T val);
|
|
|
|
void FUN(InsertAtSw)(VEC_T *ctx, size_t index, T val);
|
|
void FUN(InsertAfterSw)(VEC_T *ctx, T *it, T val);
|
|
void FUN(InsertBeforeSw)(VEC_T *ctx, T *it, T val);
|
|
|
|
// swaps with last
|
|
void FUN(Erase)(VEC_T *ctx, T *it);
|
|
void FUN(EraseAt)(VEC_T *ctx, size_t index);
|
|
// moves whole array back one
|
|
void FUN(EraseMv)(VEC_T *ctx, T *it);
|
|
void FUN(EraseAtMv)(VEC_T *ctx, size_t index);
|
|
|
|
void FUN(Push)(VEC_T *ctx, T val);
|
|
void FUN(PushRef)(VEC_T *ctx, T *val);
|
|
T FUN(Pop)(VEC_T *ctx);
|
|
|
|
void FUN(Resize)(VEC_T *ctx, size_t newcount, T val);
|
|
void FUN(ResizeRef)(VEC_T *ctx, size_t newcount, T *val);
|
|
|
|
void FUN(EraseWhen)(VEC_T *ctx, T val);
|
|
|
|
typedef bool (*TYPE(vec_erase_fn, T))(const T *val, void *udata);
|
|
void FUN(EraseIf)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata);
|
|
void FUN(EraseIfMv)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata);
|
|
|
|
// typedef int (*TYPE(vec_sort_fn, T))(const T *a, const T *b);
|
|
void FUN(Sort)(VEC_T *ctx, vec_cmp_fn_t cmp);
|
|
|
|
#endif // VEC_NO_DECLARATION
|
|
|
|
#ifndef VEC_NO_IMPLEMENTATION
|
|
|
|
inline
|
|
VEC_T FUN(Init)(void) {
|
|
return (VEC_T){
|
|
.buf = NULL,
|
|
.size = 0,
|
|
.allocated = 0
|
|
};
|
|
}
|
|
|
|
inline
|
|
VEC_T FUN(InitArr)(T *arr, size_t len) {
|
|
VEC_T v = FUN(Init)();
|
|
FUN(Realloc)(&v, len);
|
|
memcpy(v.buf, arr, len * sizeof(T));
|
|
v.size = len;
|
|
return v;
|
|
}
|
|
|
|
inline
|
|
void FUN(Free)(VEC_T *ctx) {
|
|
free(ctx->buf);
|
|
ctx->buf = NULL;
|
|
ctx->size = 0;
|
|
ctx->allocated = 0;
|
|
}
|
|
|
|
inline
|
|
VEC_T FUN(Move)(VEC_T *ctx) {
|
|
VEC_T mv = *ctx;
|
|
ctx->buf = NULL;
|
|
FUN(Free)(ctx);
|
|
return mv;
|
|
}
|
|
|
|
inline
|
|
VEC_T FUN(Copy)(VEC_T *ctx) {
|
|
VEC_T cp = FUN(Init)();
|
|
if(ctx->buf) {
|
|
FUN(Reserve)(&cp, ctx->size);
|
|
memcpy(cp.buf, ctx->buf, ctx->size * sizeof(T));
|
|
cp.size = ctx->size;
|
|
}
|
|
return cp;
|
|
}
|
|
|
|
inline
|
|
T *FUN(Beg)(VEC_T *ctx) {
|
|
return ctx->buf;
|
|
}
|
|
|
|
inline
|
|
T *FUN(End)(VEC_T *ctx) {
|
|
return ctx->buf + ctx->size;
|
|
}
|
|
|
|
inline
|
|
T *FUN(Back)(VEC_T *ctx) {
|
|
return ctx->buf ? &ctx->buf[ctx->size - 1] : NULL;
|
|
}
|
|
|
|
inline
|
|
bool FUN(Empty)(VEC_T *ctx) {
|
|
return ctx->buf ? ctx->size == 0 : true;
|
|
}
|
|
|
|
inline
|
|
void FUN(Realloc)(VEC_T *ctx, size_t needed) {
|
|
if((ctx->size + needed) >= ctx->allocated) {
|
|
ctx->allocated = (ctx->allocated * 2) + needed;
|
|
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
|
|
}
|
|
}
|
|
|
|
inline
|
|
void FUN(Reserve)(VEC_T *ctx, size_t newsize) {
|
|
if(ctx->allocated < newsize) {
|
|
ctx->allocated = newsize;
|
|
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
|
|
}
|
|
}
|
|
|
|
inline
|
|
void FUN(ShrinkToFit)(VEC_T *ctx) {
|
|
ctx->allocated = ctx->size;
|
|
ctx->buf = (T *)realloc(ctx->buf, ctx->allocated * sizeof(T));
|
|
}
|
|
|
|
inline
|
|
void FUN(Clear)(VEC_T *ctx) {
|
|
ctx->size = 0;
|
|
}
|
|
|
|
inline
|
|
void FUN(ClearZero)(VEC_T *ctx) {
|
|
ctx->size = 0;
|
|
memset(ctx->buf, 0, ctx->allocated * sizeof(T));
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertAt)(VEC_T *ctx, size_t index, T val) {
|
|
FUN(Realloc)(ctx, 1);
|
|
for(size_t i = ctx->size; i > index; --i) {
|
|
ctx->buf[i] = ctx->buf[i - 1];
|
|
}
|
|
ctx->buf[index] = val;
|
|
ctx->size++;
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertAfter)(VEC_T *ctx, T *it, T val) {
|
|
size_t index = it - ctx->buf;
|
|
// insertAt acts as insertBefore, so we just add 1
|
|
FUN(InsertAt)(ctx, index + 1, val);
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertBefore)(VEC_T *ctx, T *it, T val) {
|
|
size_t index = it - ctx->buf;
|
|
FUN(InsertAt)(ctx, index, val);
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertAtSw)(VEC_T *ctx, size_t index, T val) {
|
|
FUN(Realloc)(ctx, 1);
|
|
ctx->buf[ctx->size] = ctx->buf[index];
|
|
ctx->buf[index] = val;
|
|
ctx->size++;
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertAfterSw)(VEC_T *ctx, T *it, T val) {
|
|
size_t index = it - ctx->buf;
|
|
// insertAt acts as insertBefore, so we just add 1
|
|
FUN(InsertAtSw)(ctx, index + 1, val);
|
|
}
|
|
|
|
inline
|
|
void FUN(InsertBeforeSw)(VEC_T *ctx, T *it, T val) {
|
|
size_t index = it - ctx->buf;
|
|
FUN(InsertAtSw)(ctx, index, val);
|
|
}
|
|
|
|
inline
|
|
void FUN(Erase)(VEC_T *ctx, T *it) {
|
|
size_t index = it - ctx->buf;
|
|
FUN(EraseAt)(ctx, index);
|
|
}
|
|
|
|
inline
|
|
void FUN(EraseAt)(VEC_T *ctx, size_t index) {
|
|
ctx->size--;
|
|
ctx->buf[index] = ctx->buf[ctx->size];
|
|
}
|
|
|
|
inline
|
|
void FUN(EraseMv)(VEC_T *ctx, T *it) {
|
|
size_t index = it - ctx->buf;
|
|
FUN(EraseAtMv)(ctx, index);
|
|
}
|
|
|
|
inline
|
|
void FUN(EraseAtMv)(VEC_T *ctx, size_t index) {
|
|
ctx->size--;
|
|
for(size_t i = index; i < ctx->size; ++i) {
|
|
ctx->buf[i] = ctx->buf[i + 1];
|
|
}
|
|
}
|
|
|
|
inline
|
|
void FUN(Push)(VEC_T *ctx, T val) {
|
|
FUN(Realloc)(ctx, 1);
|
|
ctx->buf[ctx->size] = val;
|
|
ctx->size++;
|
|
}
|
|
|
|
inline
|
|
void FUN(PushRef)(VEC_T *ctx, T *val) {
|
|
FUN(Realloc)(ctx, 1);
|
|
ctx->buf[ctx->size] = *val;
|
|
ctx->size++;
|
|
}
|
|
|
|
inline
|
|
T FUN(Pop)(VEC_T *ctx) {
|
|
ctx->size--;
|
|
return ctx->buf[ctx->size];
|
|
}
|
|
|
|
inline
|
|
void FUN(Resize)(VEC_T *ctx, size_t newcount, T val) {
|
|
if(newcount <= ctx->size) {
|
|
ctx->size = newcount;
|
|
return;
|
|
}
|
|
FUN(Realloc)(ctx, newcount - ctx->size);
|
|
for(size_t i = ctx->size; i < newcount; ++i) {
|
|
ctx->buf[i] = val;
|
|
}
|
|
ctx->size = newcount;
|
|
}
|
|
|
|
inline
|
|
void FUN(ResizeRef)(VEC_T *ctx, size_t newcount, T *val) {
|
|
if(newcount <= ctx->size) {
|
|
ctx->size = newcount;
|
|
}
|
|
FUN(Realloc)(ctx, newcount - ctx->size);
|
|
if(val) {
|
|
for(size_t i = ctx->size; i < newcount; ++i) {
|
|
ctx->buf[i] = *val;
|
|
}
|
|
}
|
|
ctx->size = newcount;
|
|
}
|
|
|
|
#ifndef VEC_DISABLE_ERASE_WHEN
|
|
|
|
inline
|
|
void FUN(EraseWhen)(VEC_T *ctx, T val) {
|
|
for(size_t i = 0; i < ctx->size; ++i) {
|
|
if(ctx->buf[i] == val) {
|
|
FUN(EraseAt)(ctx, i);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
inline
|
|
void FUN(EraseIf)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata) {
|
|
for(size_t i = 0; i < ctx->size; ++i) {
|
|
if(cmp(&ctx->buf[i], udata)) {
|
|
FUN(EraseAt)(ctx, i);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline
|
|
void FUN(EraseIfMv)(VEC_T *ctx, TYPE(vec_erase_fn, T) cmp, void *udata) {
|
|
for(size_t i = 0; i < ctx->size; ++i) {
|
|
if(cmp(&ctx->buf[i], udata)) {
|
|
FUN(EraseAtMv)(ctx, i);
|
|
--i;
|
|
}
|
|
}
|
|
}
|
|
|
|
inline
|
|
void FUN(Sort)(VEC_T *ctx, vec_cmp_fn_t cmp) {
|
|
qsort(ctx->buf, ctx->size, sizeof(T), cmp);
|
|
}
|
|
|
|
#endif // VEC_NO_IMPLEMENTATION
|
|
|
|
#undef FUN
|
|
#undef VEC
|
|
#undef VEC_T
|
|
#undef TYPE
|
|
#undef VEC_SHORT_NAME
|
|
#undef T
|
|
#undef VEC_DISABLE_ERASE_WHEN
|
|
#undef VEC_NO_DECLARATION
|
|
#undef VEC_NO_IMPLEMENTATION
|
|
|
|
#if 0
|
|
// vec.h testing:
|
|
|
|
#define T int
|
|
#define VEC_SHORT_NAME veci
|
|
#include <vec.h>
|
|
#define foreach(it, vec) vec_foreach(int, veci, it, vec)
|
|
|
|
#define T char
|
|
#define VEC_SHORT_NAME vecc
|
|
#include <vec.h>
|
|
#include <tracelog.h>
|
|
|
|
|
|
#define PRINTVALS(v, s) \
|
|
printf("{ "); \
|
|
for(size_t i = 0; i < v.size; ++i) \
|
|
printf(s " ", v.buf[i]); \
|
|
printf("}\n");
|
|
|
|
#define PRINTVEC(v, s) \
|
|
printf(#v ": {\n"); \
|
|
printf("\tsize: %zu\n", v.size); \
|
|
printf("\tallocated: %zu\n", v.allocated); \
|
|
printf("\tvalues:"); \
|
|
PRINTVALS(v, s); \
|
|
printf("}\n"); \
|
|
|
|
#define PRINTVECI(v) PRINTVEC(v, "%d")
|
|
#define PRINTVALSI(v) PRINTVALS(v, "%d")
|
|
|
|
bool veciEraseEven(const int *val, void *udata) {
|
|
return *val % 2 == 0;
|
|
}
|
|
|
|
bool veciEraseHigher(const int *val, void *udata) {
|
|
return *val > 8;
|
|
}
|
|
|
|
int main() {
|
|
debug("== TESTING INIT ===========================");
|
|
{
|
|
veci_t v = veciInit();
|
|
PRINTVECI(v);
|
|
veciFree(&v);
|
|
|
|
v = veciInitArr((int[]){1, 2, 3, 4}, 4);
|
|
veciPush(&v, 25);
|
|
veciPush(&v, 13);
|
|
PRINTVECI(v);
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING MOVE/COPY ======================");
|
|
{
|
|
veci_t a = veciInitArr((int[]){1, 2, 3, 4}, 4);
|
|
info("before move");
|
|
PRINTVECI(a);
|
|
info("after move");
|
|
veci_t b = veciMove(&a);
|
|
PRINTVECI(a);
|
|
PRINTVECI(b);
|
|
veciFree(&a);
|
|
veciFree(&b);
|
|
|
|
a = veciInitArr((int[]){1, 2, 3, 4}, 4);
|
|
b = veciCopy(&a);
|
|
info("copied");
|
|
PRINTVECI(a);
|
|
PRINTVECI(b);
|
|
info("modified b");
|
|
b.buf[2] = 9;
|
|
PRINTVECI(a);
|
|
PRINTVECI(b);
|
|
veciFree(&a);
|
|
veciFree(&b);
|
|
}
|
|
debug("== TESTING BACK ===========================");
|
|
{
|
|
vecc_t v = veccInitArr((char[]){'a', 'b', 'c', 'd', 'e', 'f'}, 6);
|
|
|
|
PRINTVEC(v, "%c");
|
|
info("The last character is '%c'.", *veccBack(&v));
|
|
|
|
veccFree(&v);
|
|
}
|
|
debug("== TESTING EMPTY ==========================");
|
|
{
|
|
veci_t v = veciInit();
|
|
info("Initially, vecEmpty(): %s", veciEmpty(&v) ? "true":"false");
|
|
veciPush(&v, 42);
|
|
info("After adding elements, vecEmpty(): %s", veciEmpty(&v) ? "true":"false");
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING RESERVE/SHRINK_TO_FIT/CLEAR ====");
|
|
{
|
|
veci_t v = veciInit();
|
|
info("Default capacity: %zu", v.allocated);
|
|
veciResize(&v, 100, 0);
|
|
info("100 elements: %zu", v.allocated);
|
|
veciResize(&v, 50, 0);
|
|
info("after resize(50): %zu", v.allocated);
|
|
veciShrinkToFit(&v);
|
|
info("after shrinkToFit(): %zu", v.allocated);
|
|
veciClear(&v);
|
|
info("after clear(): %zu", v.allocated);
|
|
veciShrinkToFit(&v);
|
|
info("after shrinkToFit(): %zu", v.allocated);
|
|
for(int i = 1000; i < 1300; ++i) {
|
|
veciPush(&v, i);
|
|
}
|
|
info("after adding 300 elements: %zu", v.allocated);
|
|
veciShrinkToFit(&v);
|
|
info("after shrinkToFit(): %zu", v.allocated);
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING ITERATORS ======================");
|
|
{
|
|
veci_t v = veciInitArr((int[]){1, 2, 3, 4, 5}, 5);
|
|
PRINTVECI(v);
|
|
info("foreach:");
|
|
for(int *it = veciBeg(&v); it != veciEnd(&v); ++it) {
|
|
printf("\t*it: %d\n", *it);
|
|
}
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING INSERT =========================");
|
|
{
|
|
veci_t v = veciInit();
|
|
info("init with 3 100");
|
|
veciResize(&v, 3, 100);
|
|
PRINTVALSI(v);
|
|
info("insert 200 at 0");
|
|
veciInsertAt(&v, 0, 200);
|
|
PRINTVALSI(v);
|
|
info("insert 300 before beginning");
|
|
veciInsertBefore(&v, veciBeg(&v), 300);
|
|
PRINTVALSI(v);
|
|
info("insert 400 after beg + 1");
|
|
veciInsertAfter(&v, veciBeg(&v) + 1, 400);
|
|
PRINTVALSI(v);
|
|
info("insert swap 500 at 3");
|
|
veciInsertAtSw(&v, 3, 500);
|
|
PRINTVALSI(v);
|
|
info("insert swap 600 before beg");
|
|
veciInsertBeforeSw(&v, veciBeg(&v), 600);
|
|
PRINTVALSI(v);
|
|
info("insert swap 700 after end - 4");
|
|
veciInsertAfterSw(&v, veciEnd(&v) - 4, 700);
|
|
PRINTVALSI(v);
|
|
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING ERASE ==========================");
|
|
{
|
|
veci_t v = veciInitArr((int[]){0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 10);
|
|
info("initializing with number from 0 to 9");
|
|
PRINTVALSI(v);
|
|
info("erasing beginning");
|
|
veciErase(&v, veciBeg(&v));
|
|
PRINTVALSI(v);
|
|
info("erasing index 5");
|
|
veciEraseAt(&v, 5);
|
|
PRINTVALSI(v);
|
|
info("erasing mv end - 3");
|
|
veciEraseMv(&v, veciEnd(&v) - 3);
|
|
PRINTVALSI(v);
|
|
info("erasing mv index 1");
|
|
veciEraseAtMv(&v, 1);
|
|
PRINTVALSI(v);
|
|
info("erasing mv all even numbers");
|
|
veciEraseIfMv(&v, veciEraseEven, NULL);
|
|
PRINTVALSI(v);
|
|
info("erasing numbers higher than 8");
|
|
veciEraseIf(&v, veciEraseHigher, NULL);
|
|
PRINTVALSI(v);
|
|
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING CLEAR_ZERO =====================");
|
|
{
|
|
veci_t v = veciInitArr((int[]){0, 1, 2, 3, 4, 5}, 6);
|
|
info("initialized from 0 to 6");
|
|
PRINTVECI(v);
|
|
info("clearZero");
|
|
size_t oldsize = v.size;
|
|
veciClearZero(&v);
|
|
for(int i = 0; i < oldsize; ++i) {
|
|
printf("\t%d > %d\n", i, v.buf[i]);
|
|
}
|
|
}
|
|
debug("== TESTING PUSH/PUSH_REF ==================");
|
|
{
|
|
veci_t v = veciInit();
|
|
|
|
info("pushing 10");
|
|
veciPush(&v, 10);
|
|
int value = 50;
|
|
info("pushing reference to value: %d", value);
|
|
veciPushRef(&v, &value);
|
|
|
|
info("vector holds: ");
|
|
printf("> ");
|
|
foreach(it, v) {
|
|
printf("%d ", *it);
|
|
}
|
|
printf("\n");
|
|
|
|
veciFree(&v);
|
|
}
|
|
debug("== TESTING POP ============================");
|
|
{
|
|
vecc_t v = veccInitArr("hello world!", 12);
|
|
info("initialized with %.*s", (int)v.size, v.buf);
|
|
while(!veccEmpty(&v)) {
|
|
printf("pooped '%c'\n", veccPop(&v));
|
|
printf("> [%.*s]\n", (int)v.size, v.buf);
|
|
}
|
|
veccFree(&v);
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
#ifdef __cplusplus
|
|
} // extern "C"
|
|
#endif |