Skip to content

Instantly share code, notes, and snippets.

@cartercanedy
Last active May 16, 2026 05:37
Show Gist options
  • Select an option

  • Save cartercanedy/deb8cbd9c6e1c46b96a4d83976791514 to your computer and use it in GitHub Desktop.

Select an option

Save cartercanedy/deb8cbd9c6e1c46b96a4d83976791514 to your computer and use it in GitHub Desktop.
A lightweight, header-only dynamically-sized array implmentation with first-class support for arbitrary types
#ifndef INLINE_VEC_H
#define INLINE_VEC_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#if defined(__cplusplus) && __cplusplus >= 201703L
#define VEC_DECL [[maybe_unused]] static inline
#elif defined(__GNUC__) || defined(__clang__)
#define VEC_DECL __attribute__((unused)) static inline
#else
#define VEC_DECL static inline
#endif
const static size_t VEC_INIT_CAPACITY = 8;
const static size_t VEC_MAGIC_NUMBER = 0xDEADBEEF;
#ifndef VEC_UNCHECKED
# define VEC_ASSERT(vec, item_size) \
if ((vec)->item_size != (item_size) || item_size == 0) { \
return VEC_RESULT_ERR_UNINITIALIZED; \
} else if ((vec)->magic_number != VEC_MAGIC_NUMBER) { \
return VEC_RESULT_ERR_SIZEMISMATCH; \
} else if ((vec)->size > (vec)->capacity) { \
return VEC_RESULT_ERR_INTERNALERROR; \
}
#else
# define VEC_ASSERT(vec, item_size)
#endif
#define VEC_PUSH(type, vec, value) _vec_push_checked((vec), (value), sizeof(type))
#define VEC_POP(type, vec, out_value) _vec_pop_checked((vec), (out_value), sizeof(type))
#define VEC_SET_ITEM(type, vec, at, value) _vec_set_item_checked((vec), (at), (value), sizeof(type))
#define VEC_GET_ITEM(type, vec, at, out_value) _vec_get_item_checked((vec), (at), (out_value), sizeof(type))
#define VEC_INSERT(type, vec, at, value) _vec_insert_checked((vec), (at), (value), sizeof(type))
#define VEC_REMOVE(type, vec, at, out_value) _vec_remove_checked((vec), (at), (out_value), sizeof(type))
#define VEC_SWAP_REMOVE(type, vec, at, out_value) _vec_swap_remove_checked((vec), (at), (out_value), sizeof(type))
typedef struct {
size_t size;
size_t capacity;
void *data;
size_t item_size;
#ifndef VEC_UNCHECKED
size_t magic_number;
#endif
} vec_t;
typedef enum {
VEC_RESULT_OK = 0,
VEC_RESULT_ERR_UNINITIALIZED,
VEC_RESULT_ERR_INTERNALERROR,
VEC_RESULT_ERR_INVALIDARGUMENT,
VEC_RESULT_ERR_OUTOFBOUNDS,
VEC_RESULT_ERR_EMPTY,
VEC_RESULT_ERR_ALLOC,
VEC_RESULT_ERR_SIZEMISMATCH,
VEC_RESULT_ERR_TOOBIG,
VEC_RESULT_ERR_TRUNCATION,
} vec_result_t;
VEC_DECL
void vec__zero(vec_t *vec) {
vec->data = NULL;
vec->size = vec->capacity = vec->item_size = 0;
#ifndef VEC_UNCHECKED
vec->magic_number = 0;
#endif
}
VEC_DECL
vec_result_t vec_init_with_capacity(vec_t *vec, size_t capacity, size_t item_size) {
if (item_size < 1) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
}
vec__zero(vec);
if ((vec->capacity = capacity)) {
if (!(vec->data = calloc(capacity, item_size))) {
return VEC_RESULT_ERR_ALLOC;
}
}
vec->item_size = item_size;
#ifndef VEC_UNCHECKED
vec->magic_number = VEC_MAGIC_NUMBER;
#endif
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_init(vec_t *vec, size_t item_size) {
return vec_init_with_capacity(vec, VEC_INIT_CAPACITY, item_size);
}
VEC_DECL
void vec_free(vec_t *vec) {
if (vec->data != NULL) {
free(vec->data);
}
vec__zero(vec);
}
VEC_DECL
size_t vec_capacity(vec_t *vec) {
return vec->capacity;
}
VEC_DECL
size_t vec_len(vec_t *vec) {
return vec->size;
}
VEC_DECL
vec_result_t vec_resize(vec_t *vec, size_t capacity) {
if (vec->capacity == capacity) {
return VEC_RESULT_OK;
} else if (capacity > SIZE_MAX / vec->item_size) {
return VEC_RESULT_ERR_TOOBIG;
} else if (capacity == 0 && vec->size == 0) {
if (vec->data) {
free(vec->data);
}
vec->data = NULL;
vec->capacity = 0;
return VEC_RESULT_OK;
}
size_t curr_len = vec->item_size * vec->size;
size_t new_cap = capacity * vec->item_size;
if (curr_len > new_cap) {
return VEC_RESULT_ERR_TRUNCATION;
}
void *new_data = realloc(vec->data, new_cap);
if (!new_data) {
return VEC_RESULT_ERR_ALLOC;
}
vec->data = new_data;
vec->capacity = capacity;
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_push(vec_t *vec, void *value) {
if (value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
}
vec_result_t result = VEC_RESULT_OK;
if (!vec->capacity) {
result = vec_resize(vec, VEC_INIT_CAPACITY);
} else if (vec->size == vec->capacity) {
result = vec_resize(vec, vec->size * 2);
}
if (result) {
return result;
}
size_t offset = vec->size++ * vec->item_size;
memmove(((char *)vec->data) + offset, value, vec->item_size);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_get_item(vec_t *vec, size_t at, void **out_value) {
if (out_value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at >= vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
}
size_t offset = at * vec->item_size;
*out_value = ((char *)vec->data) + offset;
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_set_item(vec_t *vec, size_t at, void *value) {
if (value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at >=vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
}
void *item = ((char *)vec->data) + at * vec->item_size;
memmove(item, value, vec->item_size);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_pop(vec_t *vec, void *out_value) {
if (out_value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (vec->size == 0) {
return VEC_RESULT_ERR_EMPTY;
}
vec->size -= 1;
void *item = ((char *)vec->data) + vec->size * vec->item_size;
memmove(out_value, item, vec->item_size);
memset(item, 0, vec->item_size);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_remove(vec_t *vec, size_t at, void *out_value) {
if (out_value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at >= vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
} else if (at == vec->size - 1) {
return vec_pop(vec, out_value);
}
void *item = ((char *)vec->data) + at * vec->item_size;
memmove(out_value, item, vec->item_size);
void *next_item = (char *)item + vec->item_size;
memmove(item, next_item, (--vec->size - at) * vec->item_size);
memset((char *)vec->data + vec->size * vec->item_size, 0, vec->item_size);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_swap_remove(vec_t *vec, size_t at, void *out_value) {
if (out_value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at >= vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
} else if (at == vec->size - 1) {
return vec_pop(vec, out_value);
}
void *item = ((char *)vec->data) + at * vec->item_size;
void *last_item = ((char *)vec->data) + --vec->size * vec->item_size;
memmove(out_value, item, vec->item_size);
memmove(item, last_item, vec->item_size);
memset(last_item, 0, vec->item_size);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_insert(vec_t *vec, size_t at, void *value) {
if (value == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at > vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
} else if (at == vec->size) {
return vec_push(vec, value);
}
size_t new_size = vec->size + 1;
if (new_size > vec->capacity) {
vec_result_t result = vec_resize(vec, vec->size * 2);
if (result) {
return result;
}
}
void *item = ((char *)vec->data) + at * vec->item_size;
void *next_item = (char *)item + vec->item_size;
memmove(next_item, item, (vec->size - at) * vec->item_size);
memmove(item, value, vec->item_size);
vec->size += 1;
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_insert_range(vec_t *vec, size_t at, void *values, size_t values_len) {
if (values == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at > vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
} else if (values_len == 1) {
return vec_insert(vec, at, values);
}
size_t new_len = vec->size + values_len;
if (new_len > vec->capacity) {
vec_result_t result = vec_resize(vec, new_len);
if (result) {
return result;
}
}
void *src = ((char *)vec->data) + at * vec->item_size;
size_t insert_raw_len = values_len * vec->item_size;
size_t tail_raw_len = (vec->size - at) * vec->item_size;
void *dst = (char *)src + insert_raw_len;
memmove(dst, src, tail_raw_len);
memmove(src, values, insert_raw_len);
vec->size += values_len;
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_insert_range_sparse(vec_t *vec, size_t at, void **values, size_t values_len) {
if (values == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at > vec->size) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
} else if (values_len == 1) {
return vec_insert(vec, at, *values);
}
for (size_t i = 0; i < values_len; i++) {
if (values[i] == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
}
}
size_t new_len = vec->size + values_len;
if (new_len > vec->capacity) {
vec_result_t result = vec_resize(vec, new_len);
if (result) {
return result;
}
}
void *src = ((char *)vec->data) + at * vec->item_size;
size_t insert_raw_len = values_len * vec->item_size;
size_t tail_raw_len = (vec->size - at) * vec->item_size;
void *dst = (char *)src + insert_raw_len;
memmove(dst, src, tail_raw_len);
for (size_t i = 0; i < values_len; i++) {
memmove((char *)src + i * vec->item_size, values[i], vec->item_size);
}
vec->size += values_len;
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t vec_remove_range(vec_t *vec, size_t at, size_t n, void *out_buf) {
if (n < 1 || out_buf == NULL) {
return VEC_RESULT_ERR_INVALIDARGUMENT;
} else if (at >= vec->size || n > vec->size - at) {
return VEC_RESULT_ERR_OUTOFBOUNDS;
}
void *src = ((char *)vec->data) + at * vec->item_size;
size_t raw_len = n * vec->item_size;
memmove(out_buf, src, raw_len);
void *rem_start = (char *)src + raw_len;
size_t rem_len = (vec->size - at - n) * vec->item_size;
if (rem_len > 0) {
memmove(src, rem_start, rem_len);
}
vec->size -= n;
void *trim_start = ((char *)vec->data) + vec->size * vec->item_size;
memset(trim_start, 0, raw_len);
return VEC_RESULT_OK;
}
VEC_DECL
vec_result_t _vec_push_checked(vec_t *vec, void *value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_push(vec, value);
}
VEC_DECL
vec_result_t _vec_get_item_checked(vec_t *vec, size_t at, void **value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_get_item(vec, at, value);
}
VEC_DECL
vec_result_t _vec_set_item_checked(vec_t *vec, size_t at, void *value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_set_item(vec, at, value);
}
VEC_DECL
vec_result_t _vec_pop_checked(vec_t *vec, void *out_value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_pop(vec, out_value);
}
VEC_DECL
vec_result_t _vec_remove_checked(vec_t *vec, size_t at, void *out_value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_remove(vec, at, out_value);
}
VEC_DECL
vec_result_t _vec_swap_remove_checked(vec_t *vec, size_t at, void *out_value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_swap_remove(vec, at, out_value);
}
VEC_DECL
vec_result_t _vec_insert_checked(vec_t *vec, size_t at, void *value, size_t item_size) {
VEC_ASSERT(vec, item_size);
return vec_insert(vec, at, value);
}
#ifdef __cplusplus
}
#endif
#endif // #ifndef INLINE_VEC_H
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment