Logging in C with support for monoid logging
logging jsonl monoid c
0

Configure Feed

Select the types of activity you want to include in your feed.

clog / include / dynarr.h
7.3 kB 206 lines
1#pragma once 2#include <assert.h> 3#include <stddef.h> 4#include <stdlib.h> 5#include <string.h> 6#include "prelude.h" 7 8/// @file dynarr.h 9/// @brief Type-safe dynamic array using a header-behind-pointer layout. 10/// 11/// Each array is a single heap allocation: a DynArrHeader immediately followed 12/// by the element data. The pointer you hold points to the data region, so 13/// plain C array indexing (@c arr[i]) works directly. 14/// 15/// @code 16/// int *nums = da_new(int); 17/// da_append(nums, 42); 18/// printf("%d\n", nums[0]); 19/// da_free(nums); 20/// @endcode 21/// 22/// @note @p arr must always be a typed pointer variable — many macros may 23/// reassign it when a reallocation moves the underlying block. 24 25/// @brief Internal metadata stored immediately before the data pointer. 26typedef struct { 27 size_t count; ///< Number of elements currently stored. 28 size_t capacity; ///< Number of elements the allocation can hold. 29} DynArrHeader; 30 31/// @cond INTERNAL 32void* new_header(void); 33void* copy_header(void *arr, size_t elem_size); 34 35/// @brief Retrieve the DynArrHeader that precedes @p arr in memory. 36#define da_header(arr) ((DynArrHeader*)(arr) - 1) 37/// @endcond 38 39// --------------------------------------------------------------------------- 40// Lifecycle 41// --------------------------------------------------------------------------- 42 43/// @brief Allocate a new empty dynamic array. 44/// 45/// @param type The element type (e.g. @c int, @c float, a struct). Used only 46/// as documentation; the initial allocation contains no elements. 47/// @return A @c void* pointing to the (empty) data region. Assign to a typed 48/// pointer: @code int *arr = da_new(int); @endcode 49/// @note Aborts on allocation failure. 50#define da_new(type) new_header() 51 52/// @brief Free a dynamic array. 53/// 54/// Releases the entire allocation (header + data). @p arr must not be used 55/// after this call. 56/// 57/// @param arr The array to free. 58#define da_free(arr) do { \ 59 free(da_header(arr)); \ 60 } while (0) 61 62/// @brief Return an independent copy of @p arr. 63/// 64/// Allocates a fresh block and copies all elements into it. The copy is 65/// @e tight: its capacity equals its count. Mutations to either array do not 66/// affect the other. 67/// 68/// @param arr The source array. 69/// @return A new @c void* pointing to the copied data. Assign to a typed 70/// pointer of the same type as @p arr. 71/// @note Aborts on allocation failure. 72#define da_copy(arr) copy_header((void*)(arr), sizeof(*(arr))) 73 74// --------------------------------------------------------------------------- 75// Adding elements 76// --------------------------------------------------------------------------- 77 78/// @brief Append a single element to the end of the array. 79/// 80/// Grows the allocation when @c count == @c capacity. Initial capacity is 8; 81/// subsequent growth adds 50% (@c capacity + @c capacity/2). 82/// 83/// @param arr The array. May be reassigned if a reallocation occurs. 84/// @param elem The value to append (evaluated once). 85/// @note Aborts if @p arr is @c NULL or if reallocation fails. 86#define da_append(arr, elem) do { \ 87 exists(arr); \ 88 DynArrHeader* h = da_header(arr); \ 89 if (h->count == h->capacity) { \ 90 size_t new_cap = h->capacity == 0 ? 8 : h->capacity + h->capacity / 2; \ 91 void*tmp = realloc(h, sizeof(DynArrHeader) + new_cap * sizeof(elem)); \ 92 exists(tmp); \ 93 h = tmp; \ 94 h->capacity = new_cap; \ 95 } \ 96 arr = (void*)(h + 1); \ 97 arr[h->count++] = elem; \ 98 } while (0) 99 100/// @brief Append @p n elements from a plain C array. 101/// 102/// Equivalent to calling da_append() in a loop but more efficient: at most 103/// one reallocation is performed. 104/// 105/// @param arr The destination array. May be reassigned if a reallocation occurs. 106/// @param ptr Pointer to the source elements. 107/// @param n Number of elements to copy from @p ptr. 108/// @note Aborts on allocation failure. 109#define da_extend(arr, ptr, n) do { \ 110 size_t _n = (n); \ 111 size_t _count = da_header(arr)->count; \ 112 da_reserve(arr, _count + _n); \ 113 memcpy((arr) + _count, (ptr), _n * sizeof(*(arr))); \ 114 da_header(arr)->count = _count + _n; \ 115 } while (0) 116 117/// @brief Pre-allocate capacity for at least @p n elements. 118/// 119/// Ensures that at least @p n elements can be stored without further 120/// reallocation. A no-op if current capacity already meets or exceeds @p n. 121/// 122/// @param arr The array. May be reassigned if a reallocation occurs. 123/// @param n Minimum desired capacity. 124/// @note Aborts on allocation failure. 125#define da_reserve(arr, n) do { \ 126 DynArrHeader* h = da_header(arr); \ 127 if ((n) > h->capacity) { \ 128 void* tmp = realloc(h, sizeof(DynArrHeader) + (n) * sizeof(*(arr))); \ 129 exists(tmp); \ 130 h = tmp; \ 131 h->capacity = (n); \ 132 arr = (void*)(h + 1); \ 133 } \ 134 } while (0) 135 136// --------------------------------------------------------------------------- 137// Removing elements 138// --------------------------------------------------------------------------- 139 140/// @brief Remove and return the last element. 141/// 142/// Decrements @c count and returns the value that was at @c arr[count]. 143/// Capacity is unchanged. 144/// 145/// @param arr The array. 146/// @return The removed element (lvalue). 147#define da_pop(arr) (arr)[--da_header(arr)->count] 148 149/// @brief O(1) unordered removal at index @p i. 150/// 151/// Overwrites @c arr[i] with the last element then decrements @c count. 152/// Does @e not preserve element order. Use da_last() to read the element 153/// before removal if you need its value. 154/// 155/// @param arr The array. 156/// @param i Index to remove. Bounds-checked with @c assert. 157#define da_swap_remove(arr, i) do { \ 158 DynArrHeader* h = da_header(arr); \ 159 assert((i) < h->count); \ 160 (arr)[(i)] = (arr)[h->count - 1]; \ 161 h->count--; \ 162 } while (0) 163 164/// @brief Reset the element count to zero, keeping the allocation intact. 165/// 166/// After a clear, da_empty() returns true and the buffer can be reused 167/// without another allocation. 168/// 169/// @param arr The array. 170#define da_clear(arr) do { da_header(arr)->count = 0; } while (0) 171 172// --------------------------------------------------------------------------- 173// Accessing elements 174// --------------------------------------------------------------------------- 175 176/// @brief Bounds-checked element access via @c assert. 177/// 178/// @param arr The array. 179/// @param i Index (must be < da_length(@p arr)). 180/// @return The element at index @p i (lvalue). 181#define da_get(arr, i) (assert((i) < da_header(arr)->count), (arr)[(i)]) 182 183/// @brief The last element (no bounds check). 184/// 185/// @param arr The array. Must be non-empty. 186/// @return The element at @c arr[count - 1] (lvalue). 187#define da_last(arr) (arr)[da_header(arr)->count - 1] 188 189// --------------------------------------------------------------------------- 190// Inspecting state 191// --------------------------------------------------------------------------- 192 193/// @brief Number of elements currently stored. 194/// @param arr The array. 195/// @return @c count as @c size_t. 196#define da_length(arr) da_header(arr)->count 197 198/// @brief Current allocated capacity (elements, not bytes). 199/// @param arr The array. 200/// @return @c capacity as @c size_t. 201#define da_capacity(arr) da_header(arr)->capacity 202 203/// @brief Test whether the array has no elements. 204/// @param arr The array. 205/// @return @c 1 if @c count == 0, @c 0 otherwise. 206#define da_empty(arr) (da_header(arr)->count == 0)