From bd4f06e700e0dc46b7766372e46c0495df1ae40f Mon Sep 17 00:00:00 2001 From: Sona Tau Estrada Rivera Date: Sun, 17 May 2026 13:47:55 -0400 Subject: [PATCH] docs: add README.md --- README.md | 133 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..0864f21 --- /dev/null +++ b/README.md @@ -0,0 +1,133 @@ +# dynarr + +A type-safe dynamic array library for C11, using a header-behind-pointer layout so the array pointer can be indexed and passed to functions just like a plain C array. + +## Setup + +The project uses Nix flakes + direnv: + +```sh +direnv allow . # once, to activate the shell +``` + +This provides `gcc`, `just`, `bear`, `cppcheck`, `clang-format`, and `clangd` (plus `valgrind` on Linux). + +## Building + +```sh +just build # static (.a) and dynamic (.so/.dylib) into .build/ +just static # static only +just dynamic # dynamic only +``` + +Pre-built artifacts are in `lib/static/` and `lib/dynamic/`. To link against them from another project: + +```sh +gcc -Ipath/to/dynarr/include main.c -Lpath/to/dynarr/lib/static -lmylib +``` + +## Testing + +```sh +just test # all tests, with ASan + UBSan +just valgrind # all tests under Valgrind (Linux only) +``` + +To run a single test binary directly: + +```sh +.build/tests/test_dynarr +``` + +## API reference + +All operations are macros. `arr` must be a typed pointer variable (e.g. `int *arr`); it may be reassigned by operations that realloc. + +### Lifecycle + +| Macro | Description | +|---|---| +| `da_new(type)` | Allocate an empty array. Assign to a `type *` variable. | +| `da_free(arr)` | Free the array. | +| `da_copy(arr)` | Return a new, independent copy. The copy is tight: `capacity == count`. | + +### Adding elements + +| Macro | Description | +|---|---| +| `da_append(arr, elem)` | Append one element, growing if needed. | +| `da_extend(arr, ptr, n)` | Append `n` elements from a plain C array `ptr`. | +| `da_reserve(arr, n)` | Ensure capacity for at least `n` elements. No-op if already sufficient. | + +### Removing elements + +| Macro | Description | +|---|---| +| `da_pop(arr)` | Remove and return the last element. | +| `da_swap_remove(arr, i)` | O(1) removal: overwrites index `i` with the last element, then decrements count. Does not preserve order. Bounds-checked with `assert`. | +| `da_clear(arr)` | Reset count to 0, keeping the allocation. | + +### Accessing elements + +| Macro | Description | +|---|---| +| `arr[i]` | Direct unchecked access (standard C indexing). | +| `da_get(arr, i)` | Bounds-checked access via `assert`. | +| `da_last(arr)` | The last element (no bounds check). | + +### Inspecting state + +| Macro | Description | +|---|---| +| `da_length(arr)` | Number of elements (`size_t`). | +| `da_capacity(arr)` | Current allocated capacity (`size_t`). | +| `da_empty(arr)` | 1 if `count == 0`, else 0. | + +## Usage example + +```c +#include "dynarr.h" + +typedef struct { float x, y; } Vec2; + +int main(void) { + // Basic append and iterate + int *nums = da_new(int); + for (int i = 0; i < 10; i++) + da_append(nums, i); + + for (size_t i = 0; i < da_length(nums); i++) + printf("%d\n", nums[i]); + + // Bulk load from a plain C array + int extra[] = {10, 11, 12}; + da_extend(nums, extra, 3); + + // O(1) removal (order not preserved) + da_swap_remove(nums, 0); + + // Copy, then free both independently + int *copy = da_copy(nums); + da_free(nums); + da_free(copy); + + // Works with any type + Vec2 *pts = da_new(Vec2); + da_reserve(pts, 64); + Vec2 p = {1.0f, 2.0f}; + da_append(pts, p); + da_free(pts); + + return 0; +} +``` + +## Other commands + +```sh +just fmt # auto-format all .c/.h files +just fmt-check # verify formatting without modifying +just cppcheck # static analysis +just compile-commands # generate compile_commands.json for clangd +just clean # remove .build/ +``` -- 2.51.2