···11+#pragma once
22+#include <assert.h>
33+#include <stddef.h>
44+#include <stdlib.h>
55+#include <string.h>
66+#include "prelude.h"
77+88+/// @file dynarr.h
99+/// @brief Type-safe dynamic array using a header-behind-pointer layout.
1010+///
1111+/// Each array is a single heap allocation: a DynArrHeader immediately followed
1212+/// by the element data. The pointer you hold points to the data region, so
1313+/// plain C array indexing (@c arr[i]) works directly.
1414+///
1515+/// @code
1616+/// int *nums = da_new(int);
1717+/// da_append(nums, 42);
1818+/// printf("%d\n", nums[0]);
1919+/// da_free(nums);
2020+/// @endcode
2121+///
2222+/// @note @p arr must always be a typed pointer variable — many macros may
2323+/// reassign it when a reallocation moves the underlying block.
2424+2525+/// @brief Internal metadata stored immediately before the data pointer.
2626+typedef struct {
2727+ size_t count; ///< Number of elements currently stored.
2828+ size_t capacity; ///< Number of elements the allocation can hold.
2929+} DynArrHeader;
3030+3131+/// @cond INTERNAL
3232+void* new_header(void);
3333+void* copy_header(void *arr, size_t elem_size);
3434+3535+/// @brief Retrieve the DynArrHeader that precedes @p arr in memory.
3636+#define da_header(arr) ((DynArrHeader*)(arr) - 1)
3737+/// @endcond
3838+3939+// ---------------------------------------------------------------------------
4040+// Lifecycle
4141+// ---------------------------------------------------------------------------
4242+4343+/// @brief Allocate a new empty dynamic array.
4444+///
4545+/// @param type The element type (e.g. @c int, @c float, a struct). Used only
4646+/// as documentation; the initial allocation contains no elements.
4747+/// @return A @c void* pointing to the (empty) data region. Assign to a typed
4848+/// pointer: @code int *arr = da_new(int); @endcode
4949+/// @note Aborts on allocation failure.
5050+#define da_new(type) new_header()
5151+5252+/// @brief Free a dynamic array.
5353+///
5454+/// Releases the entire allocation (header + data). @p arr must not be used
5555+/// after this call.
5656+///
5757+/// @param arr The array to free.
5858+#define da_free(arr) do { \
5959+ free(da_header(arr)); \
6060+ } while (0)
6161+6262+/// @brief Return an independent copy of @p arr.
6363+///
6464+/// Allocates a fresh block and copies all elements into it. The copy is
6565+/// @e tight: its capacity equals its count. Mutations to either array do not
6666+/// affect the other.
6767+///
6868+/// @param arr The source array.
6969+/// @return A new @c void* pointing to the copied data. Assign to a typed
7070+/// pointer of the same type as @p arr.
7171+/// @note Aborts on allocation failure.
7272+#define da_copy(arr) copy_header((void*)(arr), sizeof(*(arr)))
7373+7474+// ---------------------------------------------------------------------------
7575+// Adding elements
7676+// ---------------------------------------------------------------------------
7777+7878+/// @brief Append a single element to the end of the array.
7979+///
8080+/// Grows the allocation when @c count == @c capacity. Initial capacity is 8;
8181+/// subsequent growth adds 50% (@c capacity + @c capacity/2).
8282+///
8383+/// @param arr The array. May be reassigned if a reallocation occurs.
8484+/// @param elem The value to append (evaluated once).
8585+/// @note Aborts if @p arr is @c NULL or if reallocation fails.
8686+#define da_append(arr, elem) do { \
8787+ exists(arr); \
8888+ DynArrHeader* h = da_header(arr); \
8989+ if (h->count == h->capacity) { \
9090+ size_t new_cap = h->capacity == 0 ? 8 : h->capacity + h->capacity / 2; \
9191+ void*tmp = realloc(h, sizeof(DynArrHeader) + new_cap * sizeof(elem)); \
9292+ exists(tmp); \
9393+ h = tmp; \
9494+ h->capacity = new_cap; \
9595+ } \
9696+ arr = (void*)(h + 1); \
9797+ arr[h->count++] = elem; \
9898+ } while (0)
9999+100100+/// @brief Append @p n elements from a plain C array.
101101+///
102102+/// Equivalent to calling da_append() in a loop but more efficient: at most
103103+/// one reallocation is performed.
104104+///
105105+/// @param arr The destination array. May be reassigned if a reallocation occurs.
106106+/// @param ptr Pointer to the source elements.
107107+/// @param n Number of elements to copy from @p ptr.
108108+/// @note Aborts on allocation failure.
109109+#define da_extend(arr, ptr, n) do { \
110110+ size_t _n = (n); \
111111+ size_t _count = da_header(arr)->count; \
112112+ da_reserve(arr, _count + _n); \
113113+ memcpy((arr) + _count, (ptr), _n * sizeof(*(arr))); \
114114+ da_header(arr)->count = _count + _n; \
115115+ } while (0)
116116+117117+/// @brief Pre-allocate capacity for at least @p n elements.
118118+///
119119+/// Ensures that at least @p n elements can be stored without further
120120+/// reallocation. A no-op if current capacity already meets or exceeds @p n.
121121+///
122122+/// @param arr The array. May be reassigned if a reallocation occurs.
123123+/// @param n Minimum desired capacity.
124124+/// @note Aborts on allocation failure.
125125+#define da_reserve(arr, n) do { \
126126+ DynArrHeader* h = da_header(arr); \
127127+ if ((n) > h->capacity) { \
128128+ void* tmp = realloc(h, sizeof(DynArrHeader) + (n) * sizeof(*(arr))); \
129129+ exists(tmp); \
130130+ h = tmp; \
131131+ h->capacity = (n); \
132132+ arr = (void*)(h + 1); \
133133+ } \
134134+ } while (0)
135135+136136+// ---------------------------------------------------------------------------
137137+// Removing elements
138138+// ---------------------------------------------------------------------------
139139+140140+/// @brief Remove and return the last element.
141141+///
142142+/// Decrements @c count and returns the value that was at @c arr[count].
143143+/// Capacity is unchanged.
144144+///
145145+/// @param arr The array.
146146+/// @return The removed element (lvalue).
147147+#define da_pop(arr) (arr)[--da_header(arr)->count]
148148+149149+/// @brief O(1) unordered removal at index @p i.
150150+///
151151+/// Overwrites @c arr[i] with the last element then decrements @c count.
152152+/// Does @e not preserve element order. Use da_last() to read the element
153153+/// before removal if you need its value.
154154+///
155155+/// @param arr The array.
156156+/// @param i Index to remove. Bounds-checked with @c assert.
157157+#define da_swap_remove(arr, i) do { \
158158+ DynArrHeader* h = da_header(arr); \
159159+ assert((i) < h->count); \
160160+ (arr)[(i)] = (arr)[h->count - 1]; \
161161+ h->count--; \
162162+ } while (0)
163163+164164+/// @brief Reset the element count to zero, keeping the allocation intact.
165165+///
166166+/// After a clear, da_empty() returns true and the buffer can be reused
167167+/// without another allocation.
168168+///
169169+/// @param arr The array.
170170+#define da_clear(arr) do { da_header(arr)->count = 0; } while (0)
171171+172172+// ---------------------------------------------------------------------------
173173+// Accessing elements
174174+// ---------------------------------------------------------------------------
175175+176176+/// @brief Bounds-checked element access via @c assert.
177177+///
178178+/// @param arr The array.
179179+/// @param i Index (must be < da_length(@p arr)).
180180+/// @return The element at index @p i (lvalue).
181181+#define da_get(arr, i) (assert((i) < da_header(arr)->count), (arr)[(i)])
182182+183183+/// @brief The last element (no bounds check).
184184+///
185185+/// @param arr The array. Must be non-empty.
186186+/// @return The element at @c arr[count - 1] (lvalue).
187187+#define da_last(arr) (arr)[da_header(arr)->count - 1]
188188+189189+// ---------------------------------------------------------------------------
190190+// Inspecting state
191191+// ---------------------------------------------------------------------------
192192+193193+/// @brief Number of elements currently stored.
194194+/// @param arr The array.
195195+/// @return @c count as @c size_t.
196196+#define da_length(arr) da_header(arr)->count
197197+198198+/// @brief Current allocated capacity (elements, not bytes).
199199+/// @param arr The array.
200200+/// @return @c capacity as @c size_t.
201201+#define da_capacity(arr) da_header(arr)->capacity
202202+203203+/// @brief Test whether the array has no elements.
204204+/// @param arr The array.
205205+/// @return @c 1 if @c count == 0, @c 0 otherwise.
206206+#define da_empty(arr) (da_header(arr)->count == 0)
···11+#include <stdio.h>
22+#include "test.h"
33+#include "clog.h"
44+55+/* Placeholder — tests for the original stub functions were removed when
66+ clog.c was replaced with the logging implementation. See test_clog.c. */
77+88+int main(void) {
99+ printf("test_log: all tests passed\n");
1010+ return 0;
1111+}