commit 4b80c8aea57ecf4f0ec62b9cdce8cd2acacaeac9 Author: Jonas Röger Date: Mon Mar 23 00:26:37 2026 +0100 feat: add dyn-arr diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..0e179fc --- /dev/null +++ b/.envrc @@ -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 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b7f730b --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +build +.direnv diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0d1c996 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/flake.lock b/flake.lock new file mode 100644 index 0000000..c31be65 --- /dev/null +++ b/flake.lock @@ -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 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..e563792 --- /dev/null +++ b/flake.nix @@ -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 + ''; + }; + }; + } + ); +} diff --git a/include/c-libs/dyn-arr.h b/include/c-libs/dyn-arr.h new file mode 100644 index 0000000..9dc31bf --- /dev/null +++ b/include/c-libs/dyn-arr.h @@ -0,0 +1,106 @@ +#ifndef CLIBS_DYN_ARR_H +#define CLIBS_DYN_ARR_H + +#include +#include +#include + +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 diff --git a/nix/c-libs.nix b/nix/c-libs.nix new file mode 100644 index 0000000..1e4582a --- /dev/null +++ b/nix/c-libs.nix @@ -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."; + }; +}) diff --git a/src/dyn-arr.c b/src/dyn-arr.c new file mode 100644 index 0000000..9033bfb --- /dev/null +++ b/src/dyn-arr.c @@ -0,0 +1,2 @@ +#define CLIBS_DYN_ARR_IMPL +#include "c-libs/dyn-arr.h" diff --git a/test/main.c b/test/main.c new file mode 100644 index 0000000..8b0b965 --- /dev/null +++ b/test/main.c @@ -0,0 +1,46 @@ +#include "c-libs/dyn-arr.h" +#include + +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); +}