|
/** |
|
* stb_vector.h - v1.0 - public domain type-safe generic dynamic arrays |
|
* |
|
* This is a single-header-file library that provides easy-to-use |
|
* dynamic arrays for C (also works in C++). |
|
* |
|
* DO THIS ONCE, AT THE START OF ONE SOURCE FILE: |
|
* #define STB_VECTOR_IMPLEMENTATION |
|
* #include "stb_vector.h" |
|
* |
|
* OTHER FILES: |
|
* #include "stb_vector.h" |
|
* |
|
* QUICK EXAMPLE: |
|
* ββββββββββββββ |
|
* |
|
* #define STB_VECTOR_IMPLEMENTATION |
|
* #include "stb_vector.h" |
|
* #include <stdio.h> |
|
* |
|
* int main(void) { |
|
* Vector(int) *vec = vector_new(int); |
|
* |
|
* $(vec)->resize(2); // Allocate space for 2 elements |
|
* $(vec)->set(0, 10); |
|
* $(vec)->set(1, 20); |
|
* $(vec)->push_back(30); |
|
* |
|
* printf("Initial: "); |
|
* for (size_t index = 0; index < $(vec)->size(); ++index) |
|
* printf("%d ", $(vec)->get(index)); |
|
* printf("\n"); |
|
* |
|
* $(vec)->erase(1); // Remove element at index 1 |
|
* |
|
* printf("Size: %zu, Capacity: %zu\n", $(vec)->size(), $(vec)->capacity()); |
|
* |
|
* printf("Remaining: "); |
|
* for (size_t index = 0; index < $(vec)->size(); ++index) |
|
* printf("%d ", $(vec)->get(index)); |
|
* printf("\n"); |
|
* |
|
* $(vec)->destroy(); |
|
* return 0; |
|
* } |
|
* |
|
* API REFERENCE: |
|
* ββββββββββββββ |
|
* |
|
* CREATION: |
|
* Vector(T) *vec = vector_new(T); // empty |
|
* Vector(T) *vec = vector_new(T, 10); // sized |
|
* Vector(T) *vec = vector_new(T, 10, value); // filled |
|
* |
|
* MODIFICATION: |
|
* $(vec)->push_back(value); // O(1) amortized |
|
* $(vec)->pop_back(); // O(1) |
|
* $(vec)->insert(idx, value); // O(n) |
|
* $(vec)->erase(idx); // O(n) |
|
* $(vec)->set(idx, value); // O(1) |
|
* $(vec)->resize(new_size); // O(n) if growing |
|
* $(vec)->reserve(capacity); // O(n) if growing |
|
* $(vec)->clear(); // O(1) |
|
* |
|
* ACCESS (WITH BOUNDS CHECKING): |
|
* T value = $(vec)->get(idx); // read element |
|
* T value = $(vec)->front(); // first element |
|
* T value = $(vec)->back(); // last element |
|
* T *ptr = $(vec)->data_ptr(); // raw pointer |
|
* |
|
* INFORMATION: |
|
* size_t len = $(vec)->size(); // current size |
|
* size_t cap = $(vec)->capacity(); // allocated capacity |
|
* int empty = $(vec)->empty(); // check if empty |
|
* |
|
* OPTIMIZATION: |
|
* $(vec)->shrink_to_fit(); // release unused memory |
|
* $(vec)->swap(other_vec); // O(1) swap |
|
* |
|
* CLEANUP: |
|
* $(vec)->destroy(); // free internal data |
|
* free(vec); // free struct |
|
* |
|
* EXCEPTION HANDLING: |
|
* stb_try(vec) { |
|
* $(vec)->push_back(10); |
|
* $(vec)->get(999); // Throws exception! |
|
* } stb_catch { |
|
* printf("Error: %s\n", vec->error.message); |
|
* } |
|
* |
|
* SUPPORTED TYPES: |
|
* Built-in: int, float, double, char, long |
|
* Custom: DECLARE_VECTOR(your_type) |
|
* |
|
* THREAD SAFETY: |
|
* β Thread-local error contexts |
|
* β Safe for different instances in different threads |
|
* β NOT safe to share same vector across threads |
|
* |
|
* MEMORY SAFETY: |
|
* β Bounds checking on all operations |
|
* β Magic number validation (use-after-free detection) |
|
* β Allocation failure handling |
|
* β Detailed error messages |
|
* |
|
* TIME COMPLEXITY: |
|
* push_back: O(1) amortized pop_back: O(1) |
|
* get/set: O(1) insert: O(n) |
|
* erase: O(n) resize: O(n) |
|
* clear: O(1) |
|
* |
|
* PERFORMANCE: |
|
* β’ Exponential growth (2x doubling) for amortized O(1) push |
|
* β’ Minimal overhead: ~40 bytes per vector struct |
|
* β’ Zero external dependencies |
|
* β’ Optimized for cache locality |
|
* |
|
* LIMITATIONS: |
|
* β’ Not suitable for strict real-time (setjmp/longjmp overhead) |
|
* β’ Element access requires context binding macro: $(vec) |
|
* β’ Maximum element size: SIZE_MAX / sizeof(T) |
|
* |
|
* PORTABILITY: |
|
* β C89/C90 and later |
|
* β MSVC, GCC, Clang |
|
* β Windows, Linux, macOS, Unix |
|
* β 32-bit and 64-bit systems |
|
* β C++ compatible all the way to C++17 |
|
* |
|
* LICENSE |
|
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* |
|
* This software is available under 2 licenses -- choose whichever you prefer. |
|
* |
|
* ALTERNATIVE A - MIT License |
|
* Copyright (c) 2025 Haseeb Mir |
|
* |
|
* Permission is hereby granted, free of charge, to any person obtaining a copy |
|
* of this software and associated documentation files (the "Software"), to deal |
|
* in the Software without restriction, including without limitation the rights |
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|
* copies of the Software, and to permit persons to whom the Software is |
|
* furnished to do so, subject to the following conditions: |
|
* |
|
* The above copyright notice and this permission notice shall be included in |
|
* all copies or substantial portions of the Software. |
|
* |
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|
* SOFTWARE. |
|
* |
|
* ALTERNATIVE B - Public Domain |
|
* This is free and unencumbered software released into the public domain. |
|
* Anyone is free to copy, modify, publish, use, compile, sell, or distribute |
|
* this software, either in source code form or as a compiled binary, for any |
|
* purpose, commercial or non-commercial, and by any means. |
|
* |
|
* βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
*/ |
|
|
|
#ifndef STB_VECTOR_H |
|
#define STB_VECTOR_H |
|
|
|
#include <stddef.h> |
|
#include <setjmp.h> |
|
#include <limits.h> |
|
|
|
#ifdef __cplusplus |
|
extern "C" { |
|
#endif |
|
|
|
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* CONFIGURATION |
|
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
|
|
|
#ifndef STB_VECTOR_MALLOC |
|
#define STB_VECTOR_MALLOC(sz, ctx) malloc(sz) |
|
#endif |
|
|
|
#ifndef STB_VECTOR_REALLOC |
|
#define STB_VECTOR_REALLOC(ptr, sz, ctx) realloc(ptr, sz) |
|
#endif |
|
|
|
#ifndef STB_VECTOR_FREE |
|
#define STB_VECTOR_FREE(ptr, ctx) free(ptr) |
|
#endif |
|
|
|
#define STB_VECTOR_MAGIC 0xDEADBEEF |
|
#define STB_VECTOR_MAX_SIZE(T) (SIZE_MAX / sizeof(T)) |
|
#define STB_VECTOR_INITIAL_CAPACITY 1 |
|
#define STB_VECTOR_GROWTH_FACTOR 2 |
|
|
|
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* ERROR CODES & STRUCTURES |
|
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
|
|
|
typedef enum { |
|
STB_VEC_ERR_NONE = 0, |
|
STB_VEC_ERR_OUT_OF_BOUNDS = 1, |
|
STB_VEC_ERR_ALLOCATION_FAILED = 2, |
|
STB_VEC_ERR_LENGTH_ERROR = 3, |
|
STB_VEC_ERR_INVALID_VECTOR = 4 |
|
} STB_VectorErrorCode; |
|
|
|
typedef struct { |
|
jmp_buf env; |
|
STB_VectorErrorCode code; |
|
char message[256]; |
|
int active; |
|
} STB_VectorErrorHandler; |
|
|
|
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* PUBLIC DECLARATIONS (always included) |
|
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
|
|
|
/* Thread-local context (global) */ |
|
extern __thread void *_stb_vec_ctx_current; |
|
|
|
/* Public API Macros */ |
|
#define Vector(T) STB_Vector_##T |
|
|
|
#define _STB_VEC_SELECT(_1, _2, _3, NAME, ...) NAME |
|
#define vector_new(...) _STB_VEC_SELECT(__VA_ARGS__, _stb_vec_create_fill, _stb_vec_create_size, _stb_vec_create)(__VA_ARGS__) |
|
|
|
#define _stb_vec_create(T) _stb_vec_create_##T(0, NULL) |
|
#define _stb_vec_create_size(T, n) _stb_vec_create_size_##T(n, NULL) |
|
#define _stb_vec_create_fill(T, n, fill) _stb_vec_create_fill_##T(n, fill, NULL) |
|
|
|
#define $(vec) (_stb_vec_ctx_current = (vec), (vec)) |
|
|
|
#define stb_try(vec) if (setjmp((vec)->error.env) == STB_VEC_ERR_NONE) |
|
#define stb_catch else |
|
|
|
#define RAISE(vec, msg) do { \ |
|
if (!(vec)) break; \ |
|
strncpy((vec)->error.message, (msg), sizeof((vec)->error.message) - 1); \ |
|
(vec)->error.message[sizeof((vec)->error.message) - 1] = '\0'; \ |
|
longjmp((vec)->error.env, STB_VEC_ERR_INVALID_VECTOR); \ |
|
} while (0) |
|
|
|
#endif /* STB_VECTOR_H */ |
|
|
|
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* IMPLEMENTATION (only if STB_VECTOR_IMPLEMENTATION is defined) |
|
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
|
|
|
#ifdef STB_VECTOR_IMPLEMENTATION |
|
|
|
#undef STB_VECTOR_IMPLEMENTATION |
|
|
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <stdio.h> |
|
|
|
/* Thread-local storage (implementation) */ |
|
__thread void *_stb_vec_ctx_current = NULL; |
|
|
|
/* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
|
* UNIVERSAL MACRO FOR TYPE GENERATION |
|
* ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ */ |
|
|
|
#define DECLARE_VECTOR(T) \ |
|
typedef struct STB_Vector_##T STB_Vector_##T; \ |
|
__thread STB_Vector_##T *_stb_vec_ctx_##T; \ |
|
struct STB_Vector_##T { \ |
|
T *data; \ |
|
size_t _size; \ |
|
size_t _capacity; \ |
|
unsigned int magic; \ |
|
STB_VectorErrorHandler error; \ |
|
void (*push_back)(T value); \ |
|
T (*get)(int index); \ |
|
T (*at)(int index); \ |
|
void (*set)(int index, T value); \ |
|
void (*resize)(size_t new_size); \ |
|
void (*reserve)(size_t capacity); \ |
|
size_t (*size)(void); \ |
|
size_t (*capacity)(void); \ |
|
void (*clear)(void); \ |
|
int (*empty)(void); \ |
|
void (*destroy)(void); \ |
|
void (*pop_back)(void); \ |
|
T (*front)(void); \ |
|
T (*back)(void); \ |
|
T *(*data_ptr)(void); \ |
|
void (*insert)(int index, T value); \ |
|
void (*erase)(int index); \ |
|
void (*swap)(STB_Vector_##T *other); \ |
|
void (*shrink_to_fit)(void); \ |
|
size_t (*max_size)(void); \ |
|
}; \ |
|
\ |
|
static void _stb_vec_throw_##T(STB_VectorErrorCode code, const char *msg) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
if (!vec || !vec->error.active) { \ |
|
fprintf(stderr, "FATAL: %s\n", msg); \ |
|
exit(EXIT_FAILURE); \ |
|
} \ |
|
vec->error.code = code; \ |
|
strncpy(vec->error.message, msg, 255); \ |
|
vec->error.message[255] = '\0'; \ |
|
longjmp(vec->error.env, code); \ |
|
} \ |
|
\ |
|
static void _stb_vec_validate_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
if (!vec || vec->magic != STB_VECTOR_MAGIC) { \ |
|
fprintf(stderr, "FATAL: Invalid or corrupted vector\n"); \ |
|
exit(EXIT_FAILURE); \ |
|
} \ |
|
} \ |
|
\ |
|
static void _stb_vec_range_check_##T(int index) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
if (index < 0 || (size_t)index >= vec->_size) { \ |
|
char buf[256]; \ |
|
snprintf(buf, 256, "vector::range_check: index %d out of bounds (size %zu)", index, vec->_size); \ |
|
_stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, buf); \ |
|
} \ |
|
} \ |
|
\ |
|
static size_t _stb_vec_max_size_##T(void) { \ |
|
size_t alloc_max = SIZE_MAX / sizeof(T); \ |
|
size_t diff_max = (size_t)PTRDIFF_MAX; \ |
|
return (alloc_max < diff_max) ? alloc_max : diff_max; \ |
|
} \ |
|
\ |
|
static void _stb_vec_check_length_##T(size_t n) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
size_t max_sz = _stb_vec_max_size_##T(); \ |
|
if (n > max_sz) { \ |
|
char buf[256]; \ |
|
snprintf(buf, 256, "vector::check_length: requested size %zu exceeds maximum %zu", n, max_sz); \ |
|
_stb_vec_throw_##T(STB_VEC_ERR_LENGTH_ERROR, buf); \ |
|
} \ |
|
} \ |
|
\ |
|
static void _stb_vec_push_back_##T(T value) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (vec->_size >= vec->_capacity) { \ |
|
size_t new_cap = vec->_capacity ? vec->_capacity * STB_VECTOR_GROWTH_FACTOR : STB_VECTOR_INITIAL_CAPACITY; \ |
|
_stb_vec_check_length_##T(new_cap); \ |
|
T *temp = (T *)STB_VECTOR_REALLOC(vec->data, new_cap * sizeof(T), NULL); \ |
|
if (!temp) _stb_vec_throw_##T(STB_VEC_ERR_ALLOCATION_FAILED, "push_back: memory allocation failed"); \ |
|
vec->data = temp; \ |
|
vec->_capacity = new_cap; \ |
|
} \ |
|
vec->data[vec->_size++] = value; \ |
|
} \ |
|
\ |
|
static T _stb_vec_get_##T(int index) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
_stb_vec_range_check_##T(index); \ |
|
return vec->data[index]; \ |
|
} \ |
|
\ |
|
static void _stb_vec_set_##T(int index, T value) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
_stb_vec_range_check_##T(index); \ |
|
vec->data[index] = value; \ |
|
} \ |
|
\ |
|
static void _stb_vec_resize_##T(size_t new_size) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
_stb_vec_check_length_##T(new_size); \ |
|
if (new_size > vec->_capacity) { \ |
|
T *temp = (T *)STB_VECTOR_REALLOC(vec->data, new_size * sizeof(T), NULL); \ |
|
if (!temp) _stb_vec_throw_##T(STB_VEC_ERR_ALLOCATION_FAILED, "resize: memory allocation failed"); \ |
|
vec->data = temp; \ |
|
vec->_capacity = new_size; \ |
|
} \ |
|
if (new_size > vec->_size) { \ |
|
memset(vec->data + vec->_size, 0, (new_size - vec->_size) * sizeof(T)); \ |
|
} \ |
|
vec->_size = new_size; \ |
|
} \ |
|
\ |
|
static void _stb_vec_reserve_##T(size_t new_cap) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
_stb_vec_check_length_##T(new_cap); \ |
|
if (new_cap > vec->_capacity) { \ |
|
T *temp = (T *)STB_VECTOR_REALLOC(vec->data, new_cap * sizeof(T), NULL); \ |
|
if (!temp) _stb_vec_throw_##T(STB_VEC_ERR_ALLOCATION_FAILED, "reserve: memory allocation failed"); \ |
|
vec->data = temp; \ |
|
vec->_capacity = new_cap; \ |
|
} \ |
|
} \ |
|
\ |
|
static size_t _stb_vec_size_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
return vec->_size; \ |
|
} \ |
|
\ |
|
static size_t _stb_vec_capacity_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
return vec->_capacity; \ |
|
} \ |
|
\ |
|
static void _stb_vec_clear_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
vec->_size = 0; \ |
|
} \ |
|
\ |
|
static int _stb_vec_empty_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
return vec->_size == 0; \ |
|
} \ |
|
\ |
|
static void _stb_vec_destroy_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
if (!vec) return; \ |
|
if (vec->data) { STB_VECTOR_FREE(vec->data, NULL); vec->data = NULL; } \ |
|
vec->_size = 0; \ |
|
vec->_capacity = 0; \ |
|
vec->magic = 0; \ |
|
} \ |
|
\ |
|
static void _stb_vec_pop_back_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (vec->_size == 0) _stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, "pop_back: vector is empty"); \ |
|
vec->_size--; \ |
|
memset(&vec->data[vec->_size], 0, sizeof(T)); \ |
|
} \ |
|
\ |
|
static T _stb_vec_front_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (vec->_size == 0) _stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, "front: vector is empty"); \ |
|
return vec->data[0]; \ |
|
} \ |
|
\ |
|
static T _stb_vec_back_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (vec->_size == 0) _stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, "back: vector is empty"); \ |
|
return vec->data[vec->_size - 1]; \ |
|
} \ |
|
\ |
|
static T *_stb_vec_data_ptr_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
return vec->data; \ |
|
} \ |
|
\ |
|
static void _stb_vec_insert_##T(int index, T value) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (index < 0 || (size_t)index > vec->_size) { \ |
|
char buf[256]; \ |
|
snprintf(buf, 256, "vector::insert: index %d out of range (size %zu)", index, vec->_size); \ |
|
_stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, buf); \ |
|
} \ |
|
size_t uindex = (size_t)index; \ |
|
if (vec->_size >= vec->_capacity) { \ |
|
size_t new_cap = vec->_capacity ? vec->_capacity * STB_VECTOR_GROWTH_FACTOR : STB_VECTOR_INITIAL_CAPACITY; \ |
|
_stb_vec_check_length_##T(new_cap); \ |
|
T *temp = (T *)STB_VECTOR_REALLOC(vec->data, new_cap * sizeof(T), NULL); \ |
|
if (!temp) _stb_vec_throw_##T(STB_VEC_ERR_ALLOCATION_FAILED, "insert: memory allocation failed"); \ |
|
vec->data = temp; \ |
|
vec->_capacity = new_cap; \ |
|
} \ |
|
if (uindex < vec->_size) { \ |
|
memmove(&vec->data[uindex + 1], &vec->data[uindex], (vec->_size - uindex) * sizeof(T)); \ |
|
} \ |
|
vec->data[uindex] = value; \ |
|
vec->_size++; \ |
|
} \ |
|
\ |
|
static void _stb_vec_erase_##T(int index) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (index < 0 || (size_t)index >= vec->_size) { \ |
|
char buf[256]; \ |
|
snprintf(buf, 256, "vector::erase: index %d out of range (size %zu)", index, vec->_size); \ |
|
_stb_vec_throw_##T(STB_VEC_ERR_OUT_OF_BOUNDS, buf); \ |
|
} \ |
|
size_t uindex = (size_t)index; \ |
|
if (uindex + 1 < vec->_size) { \ |
|
memmove(&vec->data[uindex], &vec->data[uindex + 1], (vec->_size - uindex - 1) * sizeof(T)); \ |
|
} \ |
|
vec->_size--; \ |
|
memset(&vec->data[vec->_size], 0, sizeof(T)); \ |
|
} \ |
|
\ |
|
static void _stb_vec_swap_##T(STB_Vector_##T *other) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
if (!other) _stb_vec_throw_##T(STB_VEC_ERR_INVALID_VECTOR, "swap: other vector is NULL"); \ |
|
_stb_vec_validate_##T(); \ |
|
if (other->magic != STB_VECTOR_MAGIC) _stb_vec_throw_##T(STB_VEC_ERR_INVALID_VECTOR, "swap: other vector is invalid"); \ |
|
T *tmp_data = vec->data; \ |
|
size_t tmp_size = vec->_size; \ |
|
size_t tmp_cap = vec->_capacity; \ |
|
vec->data = other->data; \ |
|
vec->_size = other->_size; \ |
|
vec->_capacity = other->_capacity; \ |
|
other->data = tmp_data; \ |
|
other->_size = tmp_size; \ |
|
other->_capacity = tmp_cap; \ |
|
} \ |
|
\ |
|
static void _stb_vec_shrink_to_fit_##T(void) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)_stb_vec_ctx_current; \ |
|
_stb_vec_validate_##T(); \ |
|
if (vec->_capacity <= vec->_size) return; \ |
|
if (vec->_size == 0) { \ |
|
STB_VECTOR_FREE(vec->data, NULL); \ |
|
vec->data = NULL; \ |
|
vec->_capacity = 0; \ |
|
return; \ |
|
} \ |
|
T *temp = (T *)STB_VECTOR_REALLOC(vec->data, vec->_size * sizeof(T), NULL); \ |
|
if (!temp) _stb_vec_throw_##T(STB_VEC_ERR_ALLOCATION_FAILED, "shrink_to_fit: memory allocation failed"); \ |
|
vec->data = temp; \ |
|
vec->_capacity = vec->_size; \ |
|
} \ |
|
\ |
|
static STB_Vector_##T *_stb_vec_create_##T(size_t capacity, void *ctx) { \ |
|
STB_Vector_##T *vec = (STB_Vector_##T *)STB_VECTOR_MALLOC(sizeof(STB_Vector_##T), ctx); \ |
|
if (!vec || capacity > STB_VECTOR_MAX_SIZE(T)) { \ |
|
fprintf(stderr, "FATAL: Vector allocation failed (type: %s, capacity: %zu)\n", #T, capacity); \ |
|
exit(EXIT_FAILURE); \ |
|
} \ |
|
vec->_size = 0; \ |
|
vec->_capacity = 0; \ |
|
vec->magic = STB_VECTOR_MAGIC; \ |
|
vec->data = NULL; \ |
|
vec->error.active = 1; \ |
|
vec->error.code = STB_VEC_ERR_NONE; \ |
|
memset(vec->error.message, 0, 256); \ |
|
vec->push_back = _stb_vec_push_back_##T; \ |
|
vec->get = _stb_vec_get_##T; \ |
|
vec->at = _stb_vec_get_##T; \ |
|
vec->set = _stb_vec_set_##T; \ |
|
vec->resize = _stb_vec_resize_##T; \ |
|
vec->reserve = _stb_vec_reserve_##T; \ |
|
vec->size = _stb_vec_size_##T; \ |
|
vec->capacity = _stb_vec_capacity_##T; \ |
|
vec->clear = _stb_vec_clear_##T; \ |
|
vec->empty = _stb_vec_empty_##T; \ |
|
vec->destroy = _stb_vec_destroy_##T; \ |
|
vec->pop_back = _stb_vec_pop_back_##T; \ |
|
vec->front = _stb_vec_front_##T; \ |
|
vec->back = _stb_vec_back_##T; \ |
|
vec->data_ptr = _stb_vec_data_ptr_##T; \ |
|
vec->insert = _stb_vec_insert_##T; \ |
|
vec->erase = _stb_vec_erase_##T; \ |
|
vec->swap = _stb_vec_swap_##T; \ |
|
vec->shrink_to_fit = _stb_vec_shrink_to_fit_##T; \ |
|
vec->max_size = _stb_vec_max_size_##T; \ |
|
_stb_vec_ctx_current = vec; \ |
|
_stb_vec_ctx_##T = vec; \ |
|
if (capacity > 0) { \ |
|
_stb_vec_check_length_##T(capacity); \ |
|
vec->data = (T *)STB_VECTOR_MALLOC(capacity * sizeof(T), ctx); \ |
|
if (!vec->data) { \ |
|
STB_VECTOR_FREE(vec, ctx); \ |
|
fprintf(stderr, "FATAL: Vector data allocation failed (type: %s)\n", #T); \ |
|
exit(EXIT_FAILURE); \ |
|
} \ |
|
memset(vec->data, 0, capacity * sizeof(T)); \ |
|
vec->_capacity = capacity; \ |
|
} \ |
|
return vec; \ |
|
} \ |
|
\ |
|
static STB_Vector_##T *_stb_vec_create_size_##T(size_t size, void *ctx) { \ |
|
STB_Vector_##T *vec = _stb_vec_create_##T(size, ctx); \ |
|
vec->_size = size; \ |
|
return vec; \ |
|
} \ |
|
\ |
|
static STB_Vector_##T *_stb_vec_create_fill_##T(size_t size, T fill_value, void *ctx) { \ |
|
STB_Vector_##T *vec = _stb_vec_create_##T(size, ctx); \ |
|
vec->_size = size; \ |
|
if (size > 0) { \ |
|
for (size_t i = 0; i < size; i++) { \ |
|
vec->data[i] = fill_value; \ |
|
} \ |
|
} \ |
|
return vec; \ |
|
} |
|
|
|
/* Instantiate all types */ |
|
DECLARE_VECTOR(int) |
|
DECLARE_VECTOR(float) |
|
DECLARE_VECTOR(double) |
|
DECLARE_VECTOR(char) |
|
DECLARE_VECTOR(long) |
|
|
|
#endif /* STB_VECTOR_IMPLEMENTATION */ |
|
|
|
#ifdef __cplusplus |
|
} |
|
#endif |