feat: add dyn-arr

This commit is contained in:
2026-03-23 00:26:37 +01:00
commit 4b80c8aea5
9 changed files with 338 additions and 0 deletions

17
.envrc Normal file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env bash
# ^ make editor happy
#
# Use https://direnv.net/ to automatically load the dev shell.
#
if ! has nix_direnv_version || ! nix_direnv_version 3.0.4; then
source_url "https://raw.githubusercontent.com/nix-community/nix-direnv/3.0.4/direnvrc" "sha256-DzlYZ33mWF/Gs8DDeyjr8mnVmQGx7ASYqA5WlxwvBG4="
fi
watch_file nix/**
watch_file -- **/*.nix
# Adding files to git includes them in a flake
# But it is also a bit much reloading.
# watch_file .git/index .git/HEAD
use flake . --show-trace

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
build
.direnv

22
CMakeLists.txt Normal file
View File

@@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.15)
project(
c-lib
DESCRIPTION "C Libraries for recreational Programming."
LANGUAGES C)
add_library(c-libs ${PROJECT_SOURCE_DIR}/src/dyn-arr.c)
target_include_directories(c-libs PUBLIC "${PROJECT_SOURCE_DIR}/include")
include(CTest)
if(BUILD_TESTING)
find_package(PkgConfig REQUIRED)
pkg_check_modules(CRITERION REQUIRED IMPORTED_TARGET GLOBAL criterion)
add_executable(tests test/main.c)
target_link_libraries(tests c-libs PkgConfig::CRITERION)
add_test(NAME all_tests COMMAND tests)
endif()
install(TARGETS c-libs)

61
flake.lock generated Normal file
View File

@@ -0,0 +1,61 @@
{
"nodes": {
"flake-parts": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
"locked": {
"lastModified": 1772408722,
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1773821835,
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs-lib": {
"locked": {
"lastModified": 1772328832,
"narHash": "sha256-e+/T/pmEkLP6BHhYjx6GmwP5ivonQQn0bJdH9YrRB+Q=",
"owner": "nix-community",
"repo": "nixpkgs.lib",
"rev": "c185c7a5e5dd8f9add5b2f8ebeff00888b070742",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs.lib",
"type": "github"
}
},
"root": {
"inputs": {
"flake-parts": "flake-parts",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

57
flake.nix Normal file
View File

@@ -0,0 +1,57 @@
{
description = "Some C Libs for recreational programming.";
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
flake-parts.url = "github:hercules-ci/flake-parts";
};
outputs = inputs @ {
self,
flake-parts,
...
}:
flake-parts.lib.mkFlake {inherit inputs;} (
top: {
imports = [];
flake = {
overlays.default = final: prev: {
c-libs = final.callPackage ./nix/c-libs.nix {};
};
};
systems = [
"x86_64-linux"
];
perSystem = {
self',
pkgs,
system,
...
}: {
_module.args.pkgs = import inputs.nixpkgs {
inherit system;
overlays = [self.overlays.default];
};
packages.default = pkgs.c-libs;
devShells.default = pkgs.mkShell {
packages = [
pkgs.cmake-language-server
pkgs.cmake-format
pkgs.clang-tools
pkgs.gdb
];
inputsFrom = [self'.packages.default];
shellHook = ''
export CMAKE_EXPORT_COMPILE_COMMANDS=ON
export BUILD_TESTING=ON
'';
};
};
}
);
}

106
include/c-libs/dyn-arr.h Normal file
View File

@@ -0,0 +1,106 @@
#ifndef CLIBS_DYN_ARR_H
#define CLIBS_DYN_ARR_H
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
size_t cap;
size_t size;
} DArrHeader;
/// Initialize a dynamic array
/// @param darr a pointer, which will be overwritten to point to the dynamic
/// array's data part
/// @param capacity the initial capacity allocated
#define darr_init(darr, capacity) \
do { \
size_t c = (capacity); \
DArrHeader *header = malloc(sizeof(DArrHeader) + c * sizeof(*(darr))); \
header->cap = c; \
header->size = 0; \
(darr) = (typeof(darr))(header + 1); \
} while (0)
/// Grow an danamic array by an amount
/// @param darr a pointer initialized with darr_init, will be changed to point
/// to the new data location
/// @param extra_cap the extra capacity to add
#define darr_grow(darr, extra_cap) \
do { \
DArrHeader *header = ((DArrHeader *)(darr)) - 1; \
header->cap += (extra_cap); \
\
header = \
realloc(header, header->cap * sizeof(*(darr)) + sizeof(DArrHeader)); \
(darr) = (typeof((darr)))(header + 1); \
} while (0)
/// Append an element to a dynamic array.
/// @param darr a pointer initialized with darr_init, may be overwritten if
/// memory changes
/// @param x the element to insert at darr[darr_size(darr)]
#define darr_push(darr, x) \
do { \
DArrHeader *header = ((DArrHeader *)(darr)) - 1; \
\
if (header->size == header->cap) { \
size_t inc = header->cap / 2; \
darr_grow(darr, inc ? inc : 1); \
} \
\
header = ((DArrHeader *)(darr)) - 1; \
darr[header->size++] = (x); \
} while (0)
/// Clone an dynamic array
/// @param darr a pointer initialized with darr_init
/// Returns a fresh pointer to the cloned data
#define darr_clone(darr) (typeof(darr))darr_clone_impl((darr), sizeof(*(darr)))
/// Clone an untyped dynamic array
/// @param arr any pointer initialized with darr_init
/// @param elem_size the byte-size of the contained elements
/// Returns a fresh pointer to the cloned data, must be freed with darr_free
void *darr_clone_impl(void *arr, size_t elem_size);
/// Get the number of elements in the dynamic array
/// @param arr a pointer initialized with darr_init
size_t darr_size(void *arr);
/// Get the capacity of the dynamic array
/// @param arr a pointer initialized with darr_init
size_t darr_cap(void *arr);
/// Free a dynamic array
/// @param arr a pointer initialized with darr_init or returned from darr_clone
void darr_free(void *arr);
#endif
#ifdef CLIBS_DYN_ARR_IMPL
size_t darr_size(void *arr) {
DArrHeader *header = ((DArrHeader *)arr) - 1;
return header->size;
}
size_t darr_cap(void *arr) {
DArrHeader *header = ((DArrHeader *)arr) - 1;
return header->cap;
}
void *darr_clone_impl(void *arr, size_t elem_size) {
DArrHeader *header = ((DArrHeader *)arr) - 1;
size_t size = sizeof(DArrHeader) + elem_size * header->cap;
DArrHeader *tgt = malloc(size);
memcpy(tgt, header, size);
return (void *)(tgt + 1);
}
void darr_free(void *arr) {
DArrHeader *header = ((DArrHeader *)arr) - 1;
free(header);
}
#endif

25
nix/c-libs.nix Normal file
View File

@@ -0,0 +1,25 @@
{
cmake,
criterion,
lib,
pkg-config,
stdenv,
...
}:
stdenv.mkDerivation (finalAttrs: {
pname = "c-libs";
version = "0.1.0";
src = ../.;
nativeBuildInputs = [cmake] ++ lib.optional finalAttrs.doCheck pkg-config;
buildInputs = [] ++ lib.optional finalAttrs.doCheck criterion;
cmakeFlags = [
(lib.cmakeBool "BUILD_TESTING" finalAttrs.doCheck)
];
doCheck = stdenv.buildPlatform.canExecute stdenv.hostPlatform;
meta = {
description = "Some C libs for recreational programming.";
};
})

2
src/dyn-arr.c Normal file
View File

@@ -0,0 +1,2 @@
#define CLIBS_DYN_ARR_IMPL
#include "c-libs/dyn-arr.h"

46
test/main.c Normal file
View File

@@ -0,0 +1,46 @@
#include "c-libs/dyn-arr.h"
#include <criterion/criterion.h>
Test(darr, basic_usage) {
int *darr = NULL;
darr_init(darr, 2);
cr_assert(darr_size(darr) == 0, "Dynamic array size is 0 after init");
cr_assert(darr_cap(darr) == 2, "Dynamic array capacity is 2 after init");
darr_push(darr, 10);
darr_push(darr, 20);
cr_assert(darr[0] == 10);
cr_assert(darr[1] == 20);
cr_assert(darr_size(darr) == 2, "Dynamic array size is 2 after push");
cr_assert(darr_cap(darr) == 2, "Dynamic array capacity is 2 after push");
darr_push(darr, 30);
darr_push(darr, 40);
darr_push(darr, 50);
cr_assert(darr_size(darr) == 5, "Dynamic array size is 5 after grow push");
cr_assert(darr_cap(darr) == 6, "Dynamic array capacity is 6 after grow push");
darr_free(darr);
}
Test(darr, clone_array) {
int *darr = NULL;
darr_init(darr, 5);
darr[0] = 1;
darr[1] = 10;
darr[2] = 100;
darr[3] = 1000;
darr[4] = 10000;
int *darr2 = darr_clone(darr);
cr_assert(darr_size(darr) == darr_size(darr2));
cr_assert(darr_cap(darr) == darr_cap(darr2));
for (int i = 0; i < darr_size(darr); i++) {
cr_assert(darr[i] == darr2[i]);
}
darr_free(darr);
darr_free(darr2);
}