#pragma once #include #include #define VEC(TYPE) \ \ struct vec_##TYPE { \ size_t length; \ size_t capacity; \ TYPE *buffer; /* Backing heap allocation. Different lifetime because realloc might move this */ \ }; \ \ static inline int vec_##TYPE##_init(struct vec_##TYPE *vec, size_t capacity); \ static inline struct vec_##TYPE *vec_##TYPE##_alloc(size_t capacity); \ static inline void vec_##TYPE##_deinit(struct vec_##TYPE *vec); \ static inline void vec_##TYPE##_free(struct vec_##TYPE *vec); \ \ /** \ * Initializes a vec, allocating a backing buffer for the provided capcity \ * @param vec pointer to an uninitialized vec \ * @param capacity \ * @returns 0 on success, -1 on failure \ */ \ static inline int vec_##TYPE##_init(struct vec_##TYPE *vec, size_t capacity) \ { \ if (capacity == 0) { \ vec->buffer = NULL; \ } else { \ vec->buffer = calloc(capacity, sizeof(TYPE)); \ if (vec->buffer == NULL) return -1; \ } \ \ vec->length = 0; \ vec->capacity = capacity; \ \ return 0; \ } \ \ /** \ * Allocate and initialize a vec with a backing buffer \ * @param capacity \ * @returns a pointer to an initialized vec on the heap, ready for use \ */ \ static inline struct vec_##TYPE *vec_##TYPE##_alloc(size_t capacity) \ { \ struct vec_##TYPE *vec = (struct vec_##TYPE *)calloc(1, sizeof(struct vec_##TYPE)); \ if (vec == NULL) return vec; \ \ int rc = vec_##TYPE##_init(vec, capacity); \ if (rc < 0) { \ vec_##TYPE##_free(vec); \ return NULL; \ } \ \ return vec; \ } \ \ /** \ * Deinitialize a vec, clearing out members and releasing the backing buffer \ * @param vec \ */ \ static inline void vec_##TYPE##_deinit(struct vec_##TYPE *vec) \ { \ if (vec->capacity == 0) { \ assert(vec->buffer == NULL); \ assert(vec->length == 0); \ return; \ } \ \ assert(vec->buffer != NULL); \ free(vec->buffer); \ vec->buffer = NULL; \ vec->length = 0; \ vec->capacity = 0; \ } \ \ /** \ * Deinitializes and frees a vec allocated to the heap \ * @param vec \ */ \ static inline void vec_##TYPE##_free(struct vec_##TYPE *vec) \ { \ vec_##TYPE##_deinit(vec); \ free(vec); \ } \ \ static inline int vec_##TYPE##_resize(struct vec_##TYPE *vec, size_t capacity) \ { \ if (vec->capacity != capacity) { \ TYPE *temp = (TYPE *)realloc(vec->buffer, sizeof(TYPE) * capacity); \ if (temp == NULL) return -1; \ vec->buffer = temp; \ vec->capacity = capacity; \ } \ return 0; \ } \ \ \ static inline int vec_##TYPE##_grow(struct vec_##TYPE *vec) \ { \ size_t capacity = vec->capacity == 0 ? 1 : vec->capacity * 2; \ return vec_##TYPE##_resize(vec, capacity); \ } \ \ static inline int vec_##TYPE##_insert(struct vec_##TYPE *vec, uint32_t idx, TYPE value) \ { \ if (vec->length == vec->capacity) { \ int rc = vec_##TYPE##_grow(vec); \ if (rc != 0) return -1; \ } \ \ vec->buffer[idx] = value; \ return 0; \ } \ \ static inline TYPE *vec_##TYPE##_get(struct vec_##TYPE *vec, uint32_t idx) \ { \ if (idx >= vec->capacity) return NULL; \ \ return &vec->buffer[idx]; \ } \ \ static inline int vec_##TYPE##_push(struct vec_##TYPE *vec, TYPE value) \ { \ if (vec->length == vec->capacity) { \ int rc = vec_##TYPE##_grow(vec); \ if (rc != 0) return -1; \ } \ \ vec->buffer[vec->length] = value; \ vec->length++; \ \ return 0; \ }