Last active
November 1, 2025 08:23
-
-
Save haseeb-heaven/fe7c9ac61f0852d0f6ee38b7c28bbf26 to your computer and use it in GitHub Desktop.
A simple and safe dynamic array in C that grows automatically, checks index bounds, prevents crashes from invalid access, and makes handling lists of data easier and safer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * 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) | |
| * ════════════════════════════════════════════════════════════════════════════ */ | |
| /* Forward declarations */ | |
| typedef struct STB_Vector_int STB_Vector_int; | |
| typedef struct STB_Vector_float STB_Vector_float; | |
| typedef struct STB_Vector_double STB_Vector_double; | |
| typedef struct STB_Vector_char STB_Vector_char; | |
| typedef struct STB_Vector_long STB_Vector_long; | |
| /* Thread-local contexts */ | |
| extern __thread void *_stb_vec_ctx_current; | |
| extern __thread STB_Vector_int *_stb_vec_ctx_int; | |
| extern __thread STB_Vector_float *_stb_vec_ctx_float; | |
| extern __thread STB_Vector_double *_stb_vec_ctx_double; | |
| extern __thread STB_Vector_char *_stb_vec_ctx_char; | |
| extern __thread STB_Vector_long *_stb_vec_ctx_long; | |
| /* 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; | |
| __thread STB_Vector_int *_stb_vec_ctx_int = NULL; | |
| __thread STB_Vector_float *_stb_vec_ctx_float = NULL; | |
| __thread STB_Vector_double *_stb_vec_ctx_double = NULL; | |
| __thread STB_Vector_char *_stb_vec_ctx_char = NULL; | |
| __thread STB_Vector_long *_stb_vec_ctx_long = NULL; | |
| /* ════════════════════════════════════════════════════════════════════════════ | |
| * UNIVERSAL MACRO FOR TYPE GENERATION | |
| * ════════════════════════════════════════════════════════════════════════════ */ | |
| #define DECLARE_VECTOR(T) \ | |
| typedef struct STB_Vector_##T STB_Vector_##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 |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example of Vector Int.