diff options
94 files changed, 1744 insertions, 1123 deletions
diff --git a/.clang-format b/.clang-format index 6245b22..808c8b5 100644 --- a/.clang-format +++ b/.clang-format @@ -1,10 +1,21 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC --- AlignConsecutiveAssignments: true AlignConsecutiveDeclarations: true -AlignEscapedNewlinesLeft: true +AlignEscapedNewlines: Left +AttributeMacros: + - ZIX_ALWAYS_INLINE_FUNC + - ZIX_API + - ZIX_CONST_API + - ZIX_CONST_FUNC + - ZIX_MALLOC_API + - ZIX_MALLOC_FUNC + - ZIX_NODISCARD + - ZIX_PURE_API + - ZIX_PURE_FUNC + - ZIX_PURE_WIN_API BasedOnStyle: Mozilla BraceWrapping: AfterEnum: false @@ -20,17 +31,7 @@ KeepEmptyLinesAtTheStartOfBlocks: false SpacesInContainerLiterals: false StatementMacros: - FALLTHROUGH - - ZIX_ALWAYS_INLINE_FUNC - - ZIX_API - ZIX_BEGIN_DECLS - - ZIX_CONST_API - - ZIX_CONST_FUNC - ZIX_END_DECLS - - ZIX_MALLOC_API - - ZIX_MALLOC_FUNC - - ZIX_NODISCARD - - ZIX_PURE_API - - ZIX_PURE_FUNC - - ZIX_PURE_WIN_API - _Pragma ... diff --git a/.clang-tidy b/.clang-tidy index 1c5a762..aefcd13 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,4 +1,4 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > @@ -9,6 +9,7 @@ Checks: > -hicpp-multiway-paths-covered, -llvmlibc-*, -misc-include-cleaner, + -readability-avoid-nested-conditional-operator, -readability-identifier-length, WarningsAsErrors: '*' HeaderFilterRegex: '.*' diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..7b4b16b --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,5 @@ +# Copyright 2025 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +# Format all code with clang-format +741c3349b09c8774fcd013e3bdd7d9e7f6b470ce @@ -1,10 +1,11 @@ -# Copyright 2021-2022 David Robillard <d@drobilla.net> +# Copyright 2021-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC +/.meson-subproject-wrap-hash.txt +/build/ +/subprojects/packagecache/ +/subprojects/sphinxygen-1.0.10/ +/subprojects/sphinxygen/ + *.pyc -.meson-subproject-wrap-hash.txt -__pycache__ -build/** -subprojects/packagecache/ -subprojects/sphinxygen-1.0.4/ -subprojects/sphinxygen/ +__pycache__/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ebfdd1a..1589a8a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -# Copyright 2019-2022 David Robillard <d@drobilla.net> +# Copyright 2019-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC stages: [build, deploy] @@ -95,6 +95,14 @@ mingw32: - ninja -C build test - meson configure -Dbuildtype=release build - ninja -C build test + - meson configure -Dwin_ver=vista build + - ninja -C build test + - meson configure -Dwin_ver=winxp build + - ninja -C build test + - meson configure -Dwin_ver=nt4 build + - ninja -C build test + - meson configure -Dwin_wchar=disabled build + - ninja -C build test variables: WINEPATH: "Z:\\usr\\lib\\gcc\\i686-w64-mingw32\\10-win32" @@ -106,6 +114,12 @@ mingw64: - ninja -C build test - meson configure -Dbuildtype=release build - ninja -C build test + - meson configure -Dwin_ver=vista build + - ninja -C build test + - meson configure -Dwin_ver=winxp build + - ninja -C build test + - meson configure -Dwin_wchar=disabled build + - ninja -C build test variables: WINEPATH: "Z:\\usr\\lib\\gcc\\x86_64-w64-mingw32\\10-win32" @@ -146,6 +160,16 @@ win: - ninja -C build test - meson configure -Dbuildtype=release build - ninja -C build test + - meson configure -Dwin_ver=vista build + - ninja -C build test + - meson configure -Dwin_ver=winxp build + - ninja -C build test + - meson configure -Dwin_ver=nt4 build + - ninja -C build test + - meson configure -Dwin_wchar=disabled build + - ninja -C build test + - meson configure -Dwin_wchar=enabled -Dwin_ver=win8 -Dc_args="/DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP" -Dc_link_args="/APPCONTAINER" build + - ninja -C build test # Documentation diff --git a/.reuse/dep5 b/.reuse/dep5 index ab2d669..a13171a 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -4,5 +4,5 @@ Upstream-Contact: David Robillard <d@drobilla.net> Source: https://gitlab.com/drobilla/zix Files: *.md NEWS -Copyright: 2021 David Robillard <d@drobilla.net> +Copyright: 2021-2025 David Robillard <d@drobilla.net> License: 0BSD OR ISC diff --git a/LICENSES/0BSD.txt b/LICENSES/0BSD.txt index b3e8cfd..f33ca7b 100644 --- a/LICENSES/0BSD.txt +++ b/LICENSES/0BSD.txt @@ -1,4 +1,4 @@ -Copyright 2011-2022 David Robillard <d@drobilla.net> +Copyright 2011-2025 David Robillard <d@drobilla.net> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. diff --git a/LICENSES/ISC.txt b/LICENSES/ISC.txt index 012065c..5533913 120000..100644 --- a/LICENSES/ISC.txt +++ b/LICENSES/ISC.txt @@ -1 +1,13 @@ -../COPYING
\ No newline at end of file +Copyright 2011-2025 David Robillard <d@drobilla.net> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH +REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, +INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM +LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR +OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR +PERFORMANCE OF THIS SOFTWARE. @@ -1,14 +1,32 @@ -zix (0.5.0) unstable; urgency=medium +zix (0.6.3) unstable; urgency=medium + + * Reduce empty BTree memory requirements + * Use getenv() instead of environ to avoid issues on FreeBSD + + -- David Robillard <d@drobilla.net> Thu, 29 May 2025 15:22:01 +0000 + +zix (0.6.2) stable; urgency=medium + + * Fix documentation build with sphinxygen fallback wrap + + -- David Robillard <d@drobilla.net> Sat, 18 Jan 2025 23:53:31 +0000 + +zix (0.6.0) stable; urgency=medium * Add ZIX_NODISCARD attribute + * Add option to build for older Windows versions + * Add zix_expand_environment_strings() * Add zix_string_view_equals() * Avoid fdatasync() on Darwin * Fix build on POSIX systems without PATH_MAX defined * Fix library current_version on MacOS * Fix nullability annotations for zix_canonical_path() and friends * Fix ring unit test when mlock() is unavailable + * Improve documentation + * Support building for UWP + * Support building for Windows with or without UNICODE - -- David Robillard <d@drobilla.net> Wed, 26 Jun 2024 22:21:42 +0000 + -- David Robillard <d@drobilla.net> Sat, 18 Jan 2025 21:49:37 +0000 zix (0.4.2) stable; urgency=medium @@ -32,17 +32,21 @@ Components * `zix/filesystem.h`: Functions for working with filesystems. * `zix/path.h`: Functions for working with filesystem paths lexically. +* Environment + + * `zix/environment.h`: Function to expand shell-style variables in a string. + Platforms --------- Zix is continually tested on: - * Debian GNU/Linux 11 (x86, x64, arm32, and arm64) + * Debian 11 (x86, x64, arm32, and arm64) * Fedora 36 (x64) - * FreeBSD 13 (x64) + * FreeBSD 14 (x64) * MacOS 14 (M2) * Node 12 (as wasm via emscripten 2.0.12) - * Windows 10 (x86) + * Windows 10 (x64) Dependencies ------------ diff --git a/benchmark/.clang-tidy b/benchmark/.clang-tidy index b89626f..6b582c0 100644 --- a/benchmark/.clang-tidy +++ b/benchmark/.clang-tidy @@ -1,4 +1,4 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > @@ -6,6 +6,7 @@ Checks: > -android-cloexec-fopen, -bugprone-easily-swappable-parameters, -cert-err33-c, + -clang-analyzer-core.NonNullParamChecker, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -llvm-header-guard, -performance-no-int-to-ptr, diff --git a/benchmark/bench.h b/benchmark/bench.h index bcfe077..2a25b11 100644 --- a/benchmark/bench.h +++ b/benchmark/bench.h @@ -1,33 +1,29 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #ifndef BENCH_H #define BENCH_H -#include <time.h> +#include <glib.h> -typedef struct timespec BenchmarkTime; +typedef gint64 BenchmarkTime; static inline double bench_elapsed_s(const BenchmarkTime* start, const BenchmarkTime* end) { - return ((double)(end->tv_sec - start->tv_sec) + - ((double)(end->tv_nsec - start->tv_nsec) * 0.000000001)); + return (double)(*end - *start) * 0.000001; } static inline BenchmarkTime bench_start(void) { - BenchmarkTime start_t; - clock_gettime(CLOCK_REALTIME, &start_t); - return start_t; + return g_get_monotonic_time(); } static inline double bench_end(const BenchmarkTime* start_t) { - BenchmarkTime end_t; - clock_gettime(CLOCK_REALTIME, &end_t); + const BenchmarkTime end_t = g_get_monotonic_time(); return bench_elapsed_s(start_t, &end_t); } diff --git a/benchmark/dict_bench.c b/benchmark/dict_bench.c index 279e6ad..9184fb4 100644 --- a/benchmark/dict_bench.c +++ b/benchmark/dict_bench.c @@ -12,10 +12,10 @@ typedef struct { #define ZIX_HASH_KEY_TYPE ZixChunk #define ZIX_HASH_RECORD_TYPE ZixChunk -#include "zix/attributes.h" -#include "zix/digest.h" -#include "zix/hash.h" -#include "zix/status.h" +#include <zix/attributes.h> +#include <zix/digest.h> +#include <zix/hash.h> +#include <zix/status.h> ZIX_DISABLE_GLIB_WARNINGS #include <glib.h> @@ -27,7 +27,6 @@ ZIX_RESTORE_WARNINGS #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> typedef struct { ZixChunk* chunks; @@ -45,8 +44,7 @@ lcg64(const uint64_t i) return (a * i) + c; } -ZIX_PURE_FUNC -static const ZixChunk* +ZIX_PURE_FUNC static const ZixChunk* identity(const ZixChunk* record) { return record; @@ -93,6 +91,7 @@ read_inputs(FILE* const fd) inputs.chunks = new_chunks; inputs.chunks[inputs.n_chunks].buf = (char*)malloc(buf_len); inputs.chunks[inputs.n_chunks].len = this_str_len; + assert(inputs.chunks[inputs.n_chunks].buf); memcpy(inputs.chunks[inputs.n_chunks].buf, inputs.buf, buf_len); this_str_len = 0; if (++inputs.n_chunks == max_n_strings) { @@ -129,6 +128,8 @@ run(FILE* const fd) FILE* insert_dat = fopen("dict_insert.txt", "w"); FILE* search_dat = fopen("dict_search.txt", "w"); + assert(insert_dat); + assert(search_dat); fprintf(insert_dat, "# n\tGHashTable\tZixHash\n"); fprintf(search_dat, "# n\tGHashTable\tZixHash\n"); @@ -147,7 +148,7 @@ run(FILE* const fd) // Benchmark insertion // GHashTable - struct timespec insert_start = bench_start(); + BenchmarkTime insert_start = bench_start(); for (size_t i = 0; i < n; ++i) { g_hash_table_insert(hash, inputs.chunks[i].buf, inputs.chunks[i].buf); } @@ -165,7 +166,7 @@ run(FILE* const fd) // Benchmark search // GHashTable - struct timespec search_start = bench_start(); + BenchmarkTime search_start = bench_start(); for (size_t i = 0; i < n; ++i) { const size_t index = (size_t)(lcg64(seed + i) % n); char* volatile match = diff --git a/benchmark/meson.build b/benchmark/meson.build index d3b04ea..54cac3d 100644 --- a/benchmark/meson.build +++ b/benchmark/meson.build @@ -15,15 +15,19 @@ glib_dep = dependency( if glib_dep.found() build_benchmarks = true - benchmark_c_args = platform_c_args + benchmark_c_args = extra_c_args + benchmark_c_suppressions = [] if cc.get_id() in ['clang', 'emscripten'] benchmark_c_suppressions = [ + '-Wno-bad-function-cast', + '-Wno-c11-extensions', # Glib '-Wno-reserved-identifier', ] - - benchmark_c_args += cc.get_supported_arguments(benchmark_c_suppressions) + elif cc.get_id() == 'gcc' + benchmark_c_suppressions = ['-Wno-bad-function-cast'] endif + benchmark_c_args += cc.get_supported_arguments(benchmark_c_suppressions) foreach benchmark : benchmarks benchmark( @@ -33,6 +37,7 @@ if glib_dep.found() files('@0@.c'.format(benchmark)), c_args: c_suppressions + benchmark_c_args, dependencies: [zix_dep, glib_dep], + implicit_include_directories: false, include_directories: include_dirs, ), ) diff --git a/benchmark/tree_bench.c b/benchmark/tree_bench.c index b9e230e..849216b 100644 --- a/benchmark/tree_bench.c +++ b/benchmark/tree_bench.c @@ -1,26 +1,24 @@ // Copyright 2011-2020 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC +#include "../test/test_data.h" #include "bench.h" #include "warnings.h" -#include "../test/test_data.h" - -#include "zix/attributes.h" -#include "zix/btree.h" -#include "zix/status.h" -#include "zix/tree.h" +#include <zix/attributes.h> +#include <zix/btree.h> +#include <zix/status.h> +#include <zix/tree.h> ZIX_DISABLE_GLIB_WARNINGS #include <glib.h> ZIX_RESTORE_WARNINGS +#include <assert.h> #include <inttypes.h> -#include <stdarg.h> #include <stdbool.h> #include <stdio.h> #include <stdlib.h> -#include <time.h> #ifndef MIN # define MIN(a, b) (((a) < (b)) ? (a) : (b)) @@ -55,15 +53,11 @@ g_int_cmp(const void* a, const void* b, void* user_data) return int_cmp(a, b, user_data); } -ZIX_LOG_FUNC(1, 2) static int -test_fail(const char* fmt, ...) +test_fail(const char* const prefix, const uintptr_t value) { - va_list args; - va_start(args, fmt); fprintf(stderr, "error: "); - vfprintf(stderr, fmt, args); - va_end(args); + fprintf(stderr, "%s %" PRIuPTR "\n", prefix, value); return EXIT_FAILURE; } @@ -87,32 +81,32 @@ bench_zix_tree(size_t n_elems, ZixTree* t = zix_tree_new(NULL, false, int_cmp, NULL, NULL, NULL); // Insert n_elems elements - struct timespec insert_start = bench_start(); + BenchmarkTime insert_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); ZixStatus status = zix_tree_insert(t, (void*)r, &ti); if (status) { - return test_fail("Failed to insert %" PRIuPTR "\n", r); + return test_fail("Failed to insert", r); } } fprintf(insert_dat, "\t%lf", bench_end(&insert_start)); // Search for all elements - struct timespec search_start = bench_start(); + BenchmarkTime search_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); if (zix_tree_find(t, (void*)r, &ti)) { - return test_fail("Failed to find %" PRIuPTR "\n", r); + return test_fail("Failed to find", r); } if ((uintptr_t)zix_tree_get(ti) != r) { - return test_fail("Failed to get %" PRIuPTR "\n", r); + return test_fail("Failed to get", r); } } fprintf(search_dat, "\t%lf", bench_end(&search_start)); // Iterate over all elements - struct timespec iter_start = bench_start(); + BenchmarkTime iter_start = bench_start(); for (ZixTreeIter* iter = zix_tree_begin(t); !zix_tree_iter_is_end(iter); iter = zix_tree_iter_next(iter)) { volatile void* const value = zix_tree_get(iter); @@ -121,16 +115,16 @@ bench_zix_tree(size_t n_elems, fprintf(iter_dat, "\t%lf", bench_end(&iter_start)); // Delete all elements - struct timespec del_start = bench_start(); + BenchmarkTime del_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); ZixTreeIter* item = NULL; if (zix_tree_find(t, (void*)r, &item)) { - return test_fail("Failed to find %" PRIuPTR " to delete\n", r); + return test_fail("Failed on delete to find", r); } if (zix_tree_remove(t, item)) { - return test_fail("Failed to remove %" PRIuPTR "\n", r); + return test_fail("Failed to remove", r); } } fprintf(del_dat, "\t%lf", bench_end(&del_start)); @@ -154,33 +148,33 @@ bench_zix_btree(size_t n_elems, ZixBTree* t = zix_btree_new(NULL, int_cmp, NULL); // Insert n_elems elements - struct timespec insert_start = bench_start(); + BenchmarkTime insert_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); ZixStatus status = zix_btree_insert(t, (void*)r); if (status) { - return test_fail("Failed to insert %" PRIuPTR "\n", r); + return test_fail("Failed to insert", r); } } fprintf(insert_dat, "\t%lf", bench_end(&insert_start)); // Search for all elements - struct timespec search_start = bench_start(); + BenchmarkTime search_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); if (zix_btree_find(t, (void*)r, &ti)) { - return test_fail("Failed to find %" PRIuPTR "\n", r); + return test_fail("Failed to find", r); } if ((uintptr_t)zix_btree_get(ti) != r) { - return test_fail("Failed to get %" PRIuPTR "\n", r); + return test_fail("Failed to get", r); } } fprintf(search_dat, "\t%lf", bench_end(&search_start)); // Iterate over all elements - struct timespec iter_start = bench_start(); - ZixBTreeIter iter = zix_btree_begin(t); + BenchmarkTime iter_start = bench_start(); + ZixBTreeIter iter = zix_btree_begin(t); for (; !zix_btree_iter_is_end(iter); zix_btree_iter_increment(&iter)) { volatile void* const value = zix_btree_get(iter); (void)value; @@ -188,14 +182,14 @@ bench_zix_btree(size_t n_elems, fprintf(iter_dat, "\t%lf", bench_end(&iter_start)); // Delete all elements - struct timespec del_start = bench_start(); + BenchmarkTime del_start = bench_start(); for (size_t i = 0; i < n_elems; i++) { r = unique_rand(i); void* removed = NULL; ZixBTreeIter next = zix_btree_end(t); if (zix_btree_remove(t, (void*)r, &removed, &next)) { - return test_fail("Failed to remove %" PRIuPTR "\n", r); + return test_fail("Failed to remove", r); } } fprintf(del_dat, "\t%lf", bench_end(&del_start)); @@ -218,31 +212,31 @@ bench_glib(size_t n_elems, GSequence* t = g_sequence_new(NULL); // Insert n_elems elements - struct timespec insert_start = bench_start(); + BenchmarkTime insert_start = bench_start(); for (size_t i = 0; i < n_elems; ++i) { r = unique_rand(i); GSequenceIter* iter = g_sequence_insert_sorted(t, (void*)r, g_int_cmp, NULL); if (!iter || g_sequence_iter_is_end(iter)) { - return test_fail("Failed to insert %" PRIuPTR "\n", r); + return test_fail("Failed to insert", r); } } fprintf(insert_dat, "\t%lf", bench_end(&insert_start)); // Search for all elements - struct timespec search_start = bench_start(); + BenchmarkTime search_start = bench_start(); for (size_t i = 0; i < n_elems; ++i) { r = unique_rand(i); GSequenceIter* iter = g_sequence_lookup(t, (void*)r, g_int_cmp, NULL); if (!iter || g_sequence_iter_is_end(iter)) { - return test_fail("Failed to find %" PRIuPTR "\n", r); + return test_fail("Failed to find", r); } } fprintf(search_dat, "\t%lf", bench_end(&search_start)); // Iterate over all elements - struct timespec iter_start = bench_start(); + BenchmarkTime iter_start = bench_start(); for (GSequenceIter* iter = g_sequence_get_begin_iter(t); !g_sequence_iter_is_end(iter); iter = g_sequence_iter_next(iter)) { @@ -251,12 +245,12 @@ bench_glib(size_t n_elems, fprintf(iter_dat, "\t%lf", bench_end(&iter_start)); // Delete all elements - struct timespec del_start = bench_start(); + BenchmarkTime del_start = bench_start(); for (size_t i = 0; i < n_elems; ++i) { r = unique_rand(i); GSequenceIter* iter = g_sequence_lookup(t, (void*)r, g_int_cmp, NULL); if (!iter || g_sequence_iter_is_end(iter)) { - return test_fail("Failed to remove %" PRIuPTR "\n", r); + return test_fail("Failed to remove", r); } g_sequence_remove(iter); } @@ -286,6 +280,11 @@ main(int argc, char** argv) FILE* search_dat = fopen("tree_search.txt", "w"); FILE* iter_dat = fopen("tree_iterate.txt", "w"); FILE* del_dat = fopen("tree_delete.txt", "w"); + assert(insert_dat); + assert(search_dat); + assert(iter_dat); + assert(del_dat); + fprintf(insert_dat, HEADER); fprintf(search_dat, HEADER); fprintf(iter_dat, HEADER); diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 741fe1a..0c60cab 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -68,5 +68,7 @@ INPUT = @ZIX_SRCDIR@/include/zix/zix.h \ \ @ZIX_SRCDIR@/include/zix/filesystem.h \ @ZIX_SRCDIR@/include/zix/path.h \ + \ + @ZIX_SRCDIR@/include/zix/environment.h \ OUTPUT_DIRECTORY = @DOX_OUTPUT@ diff --git a/doc/conf.py.in b/doc/conf.py.in index 6b3c415..a7c254d 100644 --- a/doc/conf.py.in +++ b/doc/conf.py.in @@ -1,10 +1,10 @@ -# Copyright 2021-2023 David Robillard <d@drobilla.net> +# Copyright 2021-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC # Project information project = "Zix" -copyright = "2011-2023, David Robillard" +copyright = "2011-2025, David Robillard" author = "David Robillard" release = "@ZIX_VERSION@" version = "@ZIX_VERSION@" diff --git a/doc/html/meson.build b/doc/html/meson.build index 9c0da44..45374d3 100644 --- a/doc/html/meson.build +++ b/doc/html/meson.build @@ -6,12 +6,15 @@ html_dir = docdir / versioned_name / 'html' custom_target( 'html', build_by_default: true, - command: sphinx_build_command + [ - '-b', 'html', - '-t', 'html', - sphinx_in_dir, - '@OUTDIR@', - ], + command: ( + sphinx_build_command + + [ + '-b', 'html', + '-t', 'html', + sphinx_in_dir, + '@OUTDIR@', + ] + ), input: [api_zix_rst, conf_py, sphinx_input], install: true, install_dir: html_dir, diff --git a/doc/meson.build b/doc/meson.build index 6d60bbb..d48f120 100644 --- a/doc/meson.build +++ b/doc/meson.build @@ -37,8 +37,9 @@ if build_docs executable( 'overview_code', files('overview_code.c'), - dependencies: [zix_dep], c_args: c_suppressions, + dependencies: [zix_dep], + implicit_include_directories: false, ), suite: 'doc', ) diff --git a/doc/overview_code.c b/doc/overview_code.c index 2f6a099..063f829 100644 --- a/doc/overview_code.c +++ b/doc/overview_code.c @@ -1,4 +1,4 @@ -// Copyright 2021-2023 David Robillard <d@drobilla.net> +// Copyright 2021-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC /* @@ -8,13 +8,8 @@ written such that it at least compiles and will run without crashing. */ -#include "zix/attributes.h" -#include "zix/string_view.h" - -#if defined(__GNUC__) -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -#endif +#include <zix/attributes.h> +#include <zix/string_view.h> static void string_views(void) @@ -24,6 +19,7 @@ string_views(void) // begin make-empty-string ZixStringView empty = zix_empty_string(); // end make-empty-string + (void)empty; // begin make-static-string static const ZixStringView hello = ZIX_STATIC_STRING("hello"); @@ -33,20 +29,17 @@ string_views(void) // begin measure-string ZixStringView view = zix_string(string_pointer); // end measure-string + (void)view; // begin make-string-view ZixStringView slice = zix_substring(string_pointer, 4); // end make-string-view + (void)slice; } -ZIX_CONST_FUNC -int +ZIX_CONST_FUNC int main(void) { string_views(); return 0; } - -#if defined(__GNUC__) -# pragma GCC diagnostic pop -#endif diff --git a/doc/singlehtml/meson.build b/doc/singlehtml/meson.build index 33bff61..32a8201 100644 --- a/doc/singlehtml/meson.build +++ b/doc/singlehtml/meson.build @@ -6,12 +6,15 @@ singlehtml_dir = docdir / versioned_name / 'singlehtml' custom_target( 'singlehtml', build_by_default: true, - command: sphinx_build_command + [ - '-b', 'singlehtml', - '-t', 'singlehtml', - sphinx_in_dir, - '@OUTDIR@', - ], + command: ( + sphinx_build_command + + [ + '-b', 'singlehtml', + '-t', 'singlehtml', + sphinx_in_dir, + '@OUTDIR@', + ] + ), input: [api_zix_rst, conf_py, sphinx_input], install: true, install_dir: singlehtml_dir, diff --git a/doc/xml/meson.build b/doc/xml/meson.build index b8863f3..f5a6b1d 100644 --- a/doc/xml/meson.build +++ b/doc/xml/meson.build @@ -23,6 +23,7 @@ doxygen_xml = custom_target( 'btree_8h.xml', 'bump__allocator_8h.xml', 'digest_8h.xml', + 'environment_8h.xml', 'filesystem_8h.xml', 'group__bump__allocator.xml', 'group__zix.xml', @@ -35,8 +36,11 @@ doxygen_xml = custom_target( 'group__zix__btree__modification.xml', 'group__zix__btree__searching.xml', 'group__zix__btree__setup.xml', + 'group__zix__btree__types.xml', 'group__zix__data__structures.xml', 'group__zix__digest.xml', + 'group__zix__environment.xml', + 'group__zix__expand.xml', 'group__zix__file__system.xml', 'group__zix__fs__access.xml', 'group__zix__fs__creation.xml', @@ -46,11 +50,11 @@ doxygen_xml = custom_target( 'group__zix__fs__queries.xml', 'group__zix__fs__resolution.xml', 'group__zix__hash.xml', - 'group__zix__hash__datatypes.xml', 'group__zix__hash__iteration.xml', 'group__zix__hash__modification.xml', 'group__zix__hash__searching.xml', 'group__zix__hash__setup.xml', + 'group__zix__hash__types.xml', 'group__zix__path.xml', 'group__zix__path__concatenation.xml', 'group__zix__path__decomposition.xml', @@ -59,6 +63,7 @@ doxygen_xml = custom_target( 'group__zix__ring.xml', 'group__zix__ring__read.xml', 'group__zix__ring__setup.xml', + 'group__zix__ring__types.xml', 'group__zix__ring__write.xml', 'group__zix__sem.xml', 'group__zix__status.xml', @@ -70,6 +75,7 @@ doxygen_xml = custom_target( 'group__zix__tree__modification.xml', 'group__zix__tree__searching.xml', 'group__zix__tree__setup.xml', + 'group__zix__tree__types.xml', 'group__zix__utilities.xml', 'hash_8h.xml', 'path_8h.xml', diff --git a/include/zix/allocator.h b/include/zix/allocator.h index bace039..5e796ff 100644 --- a/include/zix/allocator.h +++ b/include/zix/allocator.h @@ -4,7 +4,7 @@ #ifndef ZIX_ALLOCATOR_H #define ZIX_ALLOCATOR_H -#include "zix/attributes.h" +#include <zix/attributes.h> #include <stddef.h> @@ -103,13 +103,11 @@ struct ZixAllocatorImpl { }; /// Return the default allocator which simply uses the system allocator -ZIX_CONST_API -ZixAllocator* ZIX_NONNULL +ZIX_CONST_API ZixAllocator* ZIX_NONNULL zix_default_allocator(void); /// Convenience wrapper that defers to malloc() if allocator is null -ZIX_MALLOC_FUNC -static inline void* ZIX_ALLOCATED +ZIX_MALLOC_FUNC static inline void* ZIX_ALLOCATED zix_malloc(ZixAllocator* const ZIX_NULLABLE allocator, const size_t size) { ZixAllocator* const actual = allocator ? allocator : zix_default_allocator(); @@ -118,8 +116,7 @@ zix_malloc(ZixAllocator* const ZIX_NULLABLE allocator, const size_t size) } /// Convenience wrapper that defers to calloc() if allocator is null -ZIX_MALLOC_FUNC -static inline void* ZIX_ALLOCATED +ZIX_MALLOC_FUNC static inline void* ZIX_ALLOCATED zix_calloc(ZixAllocator* const ZIX_NULLABLE allocator, const size_t nmemb, const size_t size) @@ -130,8 +127,7 @@ zix_calloc(ZixAllocator* const ZIX_NULLABLE allocator, } /// Convenience wrapper that defers to realloc() if allocator is null -ZIX_NODISCARD -static inline void* ZIX_ALLOCATED +ZIX_NODISCARD static inline void* ZIX_ALLOCATED zix_realloc(ZixAllocator* const ZIX_NULLABLE allocator, void* const ZIX_NULLABLE ptr, const size_t size) @@ -152,8 +148,7 @@ zix_free(ZixAllocator* const ZIX_NULLABLE allocator, } /// Convenience wrapper that defers to the system allocator if allocator is null -ZIX_MALLOC_FUNC -static inline void* ZIX_ALLOCATED +ZIX_MALLOC_FUNC static inline void* ZIX_ALLOCATED zix_aligned_alloc(ZixAllocator* const ZIX_NULLABLE allocator, const size_t alignment, const size_t size) diff --git a/include/zix/attributes.h b/include/zix/attributes.h index 249e6a6..f92f2b0 100644 --- a/include/zix/attributes.h +++ b/include/zix/attributes.h @@ -48,22 +48,13 @@ #endif /// A pure function in the public API that only reads memory -#define ZIX_PURE_API \ - ZIX_API \ - ZIX_PURE_FUNC \ - ZIX_NODISCARD +#define ZIX_PURE_API ZIX_API ZIX_PURE_FUNC ZIX_NODISCARD /// A const function in the public API that is pure and only reads parameters -#define ZIX_CONST_API \ - ZIX_API \ - ZIX_CONST_FUNC \ - ZIX_NODISCARD +#define ZIX_CONST_API ZIX_API ZIX_CONST_FUNC ZIX_NODISCARD /// A malloc function in the public API that returns allocated memory -#define ZIX_MALLOC_API \ - ZIX_API \ - ZIX_MALLOC_FUNC \ - ZIX_NODISCARD +#define ZIX_MALLOC_API ZIX_API ZIX_MALLOC_FUNC ZIX_NODISCARD // Printf-like format functions #ifdef __GNUC__ diff --git a/include/zix/btree.h b/include/zix/btree.h index ddade81..859e092 100644 --- a/include/zix/btree.h +++ b/include/zix/btree.h @@ -4,9 +4,9 @@ #ifndef ZIX_BTREE_H #define ZIX_BTREE_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <stdbool.h> #include <stddef.h> @@ -21,7 +21,7 @@ ZIX_BEGIN_DECLS */ /** - @defgroup zix_btree_setup Setup + @defgroup zix_btree_types Types @{ */ @@ -51,6 +51,12 @@ typedef void (*ZixBTreeDestroyFunc)(void* ZIX_UNSPECIFIED ptr, const void* ZIX_UNSPECIFIED user_data); /** + @} + @defgroup zix_btree_setup Setup + @{ +*/ + +/** Create a new (empty) B-Tree. The given comparator must be a total ordering and is used to internally @@ -59,9 +65,7 @@ typedef void (*ZixBTreeDestroyFunc)(void* ZIX_UNSPECIFIED ptr, Searching can be done with a custom comparator that supports wildcards, see zix_btree_lower_bound() for details. */ -ZIX_API -ZIX_NODISCARD -ZixBTree* ZIX_ALLOCATED +ZIX_API ZIX_NODISCARD ZixBTree* ZIX_ALLOCATED zix_btree_new(ZixAllocator* ZIX_NULLABLE allocator, ZixBTreeCompareFunc ZIX_NONNULL cmp, const void* ZIX_UNSPECIFIED cmp_data); @@ -76,8 +80,7 @@ zix_btree_new(ZixAllocator* ZIX_NULLABLE allocator, @param destroy_data Opaque user data pointer to pass to `destroy`. */ -ZIX_API -void +ZIX_API void zix_btree_free(ZixBTree* ZIX_NULLABLE t, ZixBTreeDestroyFunc ZIX_NULLABLE destroy, const void* ZIX_NULLABLE destroy_data); @@ -92,15 +95,13 @@ zix_btree_free(ZixBTree* ZIX_NULLABLE t, @param destroy_data Opaque user data pointer to pass to `destroy`. */ -ZIX_API -void +ZIX_API void zix_btree_clear(ZixBTree* ZIX_NONNULL t, ZixBTreeDestroyFunc ZIX_NULLABLE destroy, const void* ZIX_NULLABLE destroy_data); /// Return the number of elements in `t` -ZIX_PURE_API -size_t +ZIX_PURE_API size_t zix_btree_size(const ZixBTree* ZIX_NONNULL t); /** @@ -135,42 +136,34 @@ static const ZixBTreeIter zix_btree_end_iter = { }; /// Return the data at the given position in the tree -ZIX_PURE_API -void* ZIX_UNSPECIFIED +ZIX_PURE_API void* ZIX_UNSPECIFIED zix_btree_get(ZixBTreeIter ti); /// Return an iterator to the first (smallest) element in `t` -ZIX_PURE_API -ZixBTreeIter +ZIX_PURE_API ZixBTreeIter zix_btree_begin(const ZixBTree* ZIX_NONNULL t); /// Return an iterator to the end of `t` (one past the last element) -ZIX_CONST_API -ZixBTreeIter +ZIX_CONST_API ZixBTreeIter zix_btree_end(const ZixBTree* ZIX_NULLABLE t); /// Return true iff `lhs` is equal to `rhs` -ZIX_CONST_API -bool +ZIX_CONST_API bool zix_btree_iter_equals(ZixBTreeIter lhs, ZixBTreeIter rhs); /// Return true iff `i` is an iterator at the end of a tree -ZIX_NODISCARD -static inline bool +ZIX_NODISCARD static inline bool zix_btree_iter_is_end(const ZixBTreeIter i) { return i.level == 0 && !i.nodes[0]; } /// Increment `i` to point to the next element in the tree -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_btree_iter_increment(ZixBTreeIter* ZIX_NONNULL i); /// Return an iterator one past `iter` -ZIX_API -ZIX_NODISCARD -ZixBTreeIter +ZIX_API ZIX_NODISCARD ZixBTreeIter zix_btree_iter_next(ZixBTreeIter iter); /** @@ -185,8 +178,7 @@ zix_btree_iter_next(ZixBTreeIter iter); @return #ZIX_STATUS_SUCCESS on success, #ZIX_STATUS_EXISTS, or #ZIX_STATUS_NO_MEM. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_btree_insert(ZixBTree* ZIX_NONNULL t, void* ZIX_UNSPECIFIED e); /** @@ -203,8 +195,7 @@ zix_btree_insert(ZixBTree* ZIX_NONNULL t, void* ZIX_UNSPECIFIED e); @return #ZIX_STATUS_SUCCESS on success, or #ZIX_STATUS_NOT_FOUND. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_btree_remove(ZixBTree* ZIX_NONNULL t, const void* ZIX_UNSPECIFIED e, void* ZIX_UNSPECIFIED* ZIX_NONNULL out, @@ -223,8 +214,7 @@ zix_btree_remove(ZixBTree* ZIX_NONNULL t, @return #ZIX_STATUS_SUCCESS on success, or #ZIX_STATUS_NOT_FOUND. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_btree_find(const ZixBTree* ZIX_NONNULL t, const void* ZIX_UNSPECIFIED e, ZixBTreeIter* ZIX_NONNULL ti); @@ -245,8 +235,7 @@ zix_btree_find(const ZixBTree* ZIX_NONNULL t, @return #ZIX_STATUS_SUCCESS. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_btree_lower_bound(const ZixBTree* ZIX_NONNULL t, ZixBTreeCompareFunc ZIX_NULLABLE compare_key, const void* ZIX_NULLABLE compare_key_data, diff --git a/include/zix/bump_allocator.h b/include/zix/bump_allocator.h index c69de92..b5fdd7e 100644 --- a/include/zix/bump_allocator.h +++ b/include/zix/bump_allocator.h @@ -4,8 +4,8 @@ #ifndef ZIX_BUMP_ALLOCATOR_H #define ZIX_BUMP_ALLOCATOR_H -#include "zix/allocator.h" -#include "zix/attributes.h" +#include <zix/allocator.h> +#include <zix/attributes.h> #include <stddef.h> @@ -52,8 +52,7 @@ typedef struct { } ZixBumpAllocator; /// Return a bump allocator that works within a provided buffer -ZIX_API -ZixBumpAllocator +ZIX_API ZixBumpAllocator zix_bump_allocator(size_t capacity, void* ZIX_NONNULL buffer); /** diff --git a/include/zix/digest.h b/include/zix/digest.h index deffaf0..82f5b28 100644 --- a/include/zix/digest.h +++ b/include/zix/digest.h @@ -4,7 +4,7 @@ #ifndef ZIX_DIGEST_H #define ZIX_DIGEST_H -#include "zix/attributes.h" +#include <zix/attributes.h> #include <stddef.h> #include <stdint.h> @@ -29,8 +29,7 @@ ZIX_BEGIN_DECLS This can be used for any size or alignment. */ -ZIX_PURE_API -uint32_t +ZIX_PURE_API uint32_t zix_digest32(uint32_t seed, const void* ZIX_NONNULL buf, size_t len); /** @@ -39,8 +38,7 @@ zix_digest32(uint32_t seed, const void* ZIX_NONNULL buf, size_t len); Both the buffer and size must be aligned to 32 bits. For data that fits these requirements, this is equivalent to, but faster than, zix_digest32(). */ -ZIX_PURE_API -uint32_t +ZIX_PURE_API uint32_t zix_digest32_aligned(uint32_t seed, const void* ZIX_NONNULL buf, size_t len); /** @@ -48,8 +46,7 @@ zix_digest32_aligned(uint32_t seed, const void* ZIX_NONNULL buf, size_t len); This can be used for any size or alignment. */ -ZIX_PURE_API -uint64_t +ZIX_PURE_API uint64_t zix_digest64(uint64_t seed, const void* ZIX_NONNULL buf, size_t len); /** @@ -58,8 +55,7 @@ zix_digest64(uint64_t seed, const void* ZIX_NONNULL buf, size_t len); Both the buffer and size must be aligned to 64 bits. For data that fits these requirements, this is equivalent to, but faster than, zix_digest64(). */ -ZIX_PURE_API -uint64_t +ZIX_PURE_API uint64_t zix_digest64_aligned(uint64_t seed, const void* ZIX_NONNULL buf, size_t len); /** @@ -70,8 +66,7 @@ zix_digest64_aligned(uint64_t seed, const void* ZIX_NONNULL buf, size_t len); Internally, this simply dispatches to zix_digest32() or zix_digest64() as appropriate. */ -ZIX_PURE_API -size_t +ZIX_PURE_API size_t zix_digest(size_t seed, const void* ZIX_NONNULL buf, size_t len); /** @@ -84,8 +79,7 @@ zix_digest(size_t seed, const void* ZIX_NONNULL buf, size_t len); Internally, this simply dispatches to zix_digest32_aligned() or zix_digest64_aligned() as appropriate. */ -ZIX_PURE_API -size_t +ZIX_PURE_API size_t zix_digest_aligned(size_t seed, const void* ZIX_NONNULL buf, size_t len); /** diff --git a/include/zix/environment.h b/include/zix/environment.h new file mode 100644 index 0000000..095d421 --- /dev/null +++ b/include/zix/environment.h @@ -0,0 +1,39 @@ +// Copyright 2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef ZIX_ENVIRONMENT_H +#define ZIX_ENVIRONMENT_H + +#include <zix/allocator.h> +#include <zix/attributes.h> + +ZIX_BEGIN_DECLS + +/** + @defgroup zix_expand Variable Expansion + @ingroup zix_environment + @{ +*/ + +/** + Expand shell-style variables in a string. + + On Windows, this expands environment variable references like + "%USERPROFILE%". On POSIX systems, it expands environment variable + references like "$HOME", and the special path component "~". + + @param allocator Allocator used for returned string. + @param string Input string to expand. + @return A newly allocated copy of `string` with variables expanded, or null. +*/ +ZIX_MALLOC_API char* ZIX_ALLOCATED +zix_expand_environment_strings(ZixAllocator* ZIX_NULLABLE allocator, + const char* ZIX_NONNULL string); + +/** + @} +*/ + +ZIX_END_DECLS + +#endif /* ZIX_ENVIRONMENT_H */ diff --git a/include/zix/filesystem.h b/include/zix/filesystem.h index 221e165..dff4145 100644 --- a/include/zix/filesystem.h +++ b/include/zix/filesystem.h @@ -4,9 +4,9 @@ #ifndef ZIX_FILESYSTEM_H #define ZIX_FILESYSTEM_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #if !(defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64) # include <stddef.h> @@ -51,8 +51,7 @@ typedef uint32_t ZixCopyOptions; @param options Options to control the kind of copy and error conditions. @return #ZIX_STATUS_SUCCESS if `dst` was successfully created, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_copy_file(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL src, const char* ZIX_NONNULL dst, @@ -64,8 +63,7 @@ zix_copy_file(ZixAllocator* ZIX_NULLABLE allocator, @return #ZIX_STATUS_SUCCESS if `dir_path` was successfully created, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_directory(const char* ZIX_NONNULL dir_path); /** @@ -77,8 +75,7 @@ zix_create_directory(const char* ZIX_NONNULL dir_path); @return #ZIX_STATUS_SUCCESS if `dir_path` was successfully created, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_directory_like(const char* ZIX_NONNULL dir_path, const char* ZIX_NONNULL existing_path); @@ -92,8 +89,7 @@ zix_create_directory_like(const char* ZIX_NONNULL dir_path, @return #ZIX_STATUS_SUCCESS if all directories in `dir_path` were successfully created (or already existed), or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_directories(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL dir_path); @@ -102,8 +98,7 @@ zix_create_directories(ZixAllocator* ZIX_NULLABLE allocator, @return #ZIX_STATUS_SUCCESS, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_hard_link(const char* ZIX_NONNULL target_path, const char* ZIX_NONNULL link_path); @@ -116,8 +111,7 @@ zix_create_hard_link(const char* ZIX_NONNULL target_path, @return #ZIX_STATUS_SUCCESS, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_symlink(const char* ZIX_NONNULL target_path, const char* ZIX_NONNULL link_path); @@ -129,8 +123,7 @@ zix_create_symlink(const char* ZIX_NONNULL target_path, @return #ZIX_STATUS_SUCCESS, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_create_directory_symlink(const char* ZIX_NONNULL target_path, const char* ZIX_NONNULL link_path); @@ -146,14 +139,12 @@ zix_create_directory_symlink(const char* ZIX_NONNULL target_path, @return The path of the created directory, or null. */ -ZIX_MALLOC_API -char* ZIX_NULLABLE +ZIX_MALLOC_API char* ZIX_NULLABLE zix_create_temporary_directory(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL path_pattern); /// Remove the file or empty directory at `path` -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_remove(const char* ZIX_NONNULL path); /** @@ -163,6 +154,17 @@ zix_remove(const char* ZIX_NONNULL path); */ /** + Function for reading input bytes from a stream. + + @param path Path to the directory being visited. + @param name Name of the directory entry. + @param data Opaque user data. +*/ +typedef void (*ZixDirEntryVisitFunc)(const char* ZIX_NONNULL path, + const char* ZIX_NONNULL name, + void* ZIX_NONNULL data); + +/** Visit every file in the directory at `path`. @param path A path to a directory. @@ -173,13 +175,10 @@ zix_remove(const char* ZIX_NONNULL path); parameter is always the directory path passed to this function, the `name` parameter is the name of the directory entry (not its full path). */ -ZIX_API -void -zix_dir_for_each(const char* ZIX_NONNULL path, - void* ZIX_NULLABLE data, - void (*ZIX_NONNULL f)(const char* ZIX_NONNULL path, - const char* ZIX_NONNULL name, - void* ZIX_NONNULL data)); +ZIX_API void +zix_dir_for_each(const char* ZIX_NONNULL path, + void* ZIX_NULLABLE data, + ZixDirEntryVisitFunc ZIX_NONNULL f); /** Return whether the given paths point to files with identical contents. @@ -193,9 +192,7 @@ zix_dir_for_each(const char* ZIX_NONNULL path, @return True if the two files have byte-for-byte identical contents. */ -ZIX_API -ZIX_NODISCARD -bool +ZIX_API ZIX_NODISCARD bool zix_file_equals(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL a_path, const char* ZIX_NONNULL b_path); @@ -228,8 +225,7 @@ zix_file_equals(ZixAllocator* ZIX_NULLABLE allocator, @return A new canonical version of `path`, or null if it doesn't exist. */ -ZIX_MALLOC_API -char* ZIX_NULLABLE +ZIX_MALLOC_API char* ZIX_NULLABLE zix_canonical_path(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NULLABLE path); @@ -256,8 +252,7 @@ typedef enum { @param mode Lock mode. @return #ZIX_STATUS_SUCCESS if the file was locked, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_file_lock(FILE* ZIX_NONNULL file, ZixFileLockMode mode); /** @@ -267,8 +262,7 @@ zix_file_lock(FILE* ZIX_NONNULL file, ZixFileLockMode mode); @param mode Lock mode. @return #ZIX_STATUS_SUCCESS if the file was unlocked, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_file_unlock(FILE* ZIX_NONNULL file, ZixFileLockMode mode); /** @@ -309,8 +303,7 @@ typedef enum { /** Return the type of a file or directory, resolving symlinks. */ -ZIX_API -ZixFileType +ZIX_API ZixFileType zix_file_type(const char* ZIX_NONNULL path); /** @@ -319,8 +312,7 @@ zix_file_type(const char* ZIX_NONNULL path); On Windows, a directory symlink (actually a "reparse point") always appears as a directory. */ -ZIX_API -ZixFileType +ZIX_API ZixFileType zix_symlink_type(const char* ZIX_NONNULL path); /** @@ -332,8 +324,7 @@ zix_symlink_type(const char* ZIX_NONNULL path); @return A non-negative size in bytes, or -1 on error. */ -ZIX_API -ZixFileOffset +ZIX_API ZixFileOffset zix_file_size(const char* ZIX_NONNULL path); /** @@ -347,8 +338,7 @@ zix_file_size(const char* ZIX_NONNULL path); @param allocator Allocator used for the returned path. */ -ZIX_MALLOC_API -char* ZIX_ALLOCATED +ZIX_MALLOC_API char* ZIX_ALLOCATED zix_current_path(ZixAllocator* ZIX_NULLABLE allocator); /** @@ -358,8 +348,7 @@ zix_current_path(ZixAllocator* ZIX_NULLABLE allocator); @return A new path to a temporary directory, or null on error. */ -ZIX_MALLOC_API -char* ZIX_ALLOCATED +ZIX_MALLOC_API char* ZIX_ALLOCATED zix_temp_directory_path(ZixAllocator* ZIX_NULLABLE allocator); /** diff --git a/include/zix/hash.h b/include/zix/hash.h index 6cd2271..3222e58 100644 --- a/include/zix/hash.h +++ b/include/zix/hash.h @@ -4,9 +4,9 @@ #ifndef ZIX_HASH_H #define ZIX_HASH_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <stdbool.h> #include <stddef.h> @@ -20,7 +20,7 @@ ZIX_BEGIN_DECLS */ /** - @defgroup zix_hash_datatypes Datatypes + @defgroup zix_hash_types Types @{ */ @@ -77,12 +77,6 @@ typedef struct ZixHashImpl ZixHash; /// A full hash code for a key which is not folded down to the table size typedef size_t ZixHashCode; -/** - @} - @defgroup zix_hash_setup Setup - @{ -*/ - /// User function for getting the key of a record typedef const ZixHashKey* ZIX_NONNULL (*ZixKeyFunc)( const ZixHashRecord* ZIX_NONNULL record); @@ -95,6 +89,12 @@ typedef bool (*ZixKeyEqualFunc)(const ZixHashKey* ZIX_NONNULL a, const ZixHashKey* ZIX_NONNULL b); /** + @} + @defgroup zix_hash_setup Setup + @{ +*/ + +/** Create a new hash table. @param allocator Allocator used for the internal array. @@ -102,22 +102,18 @@ typedef bool (*ZixKeyEqualFunc)(const ZixHashKey* ZIX_NONNULL a, @param hash_func The key hashing function. @param equal_func A function to test keys for equality. */ -ZIX_API -ZIX_NODISCARD -ZixHash* ZIX_ALLOCATED +ZIX_API ZIX_NODISCARD ZixHash* ZIX_ALLOCATED zix_hash_new(ZixAllocator* ZIX_NULLABLE allocator, ZixKeyFunc ZIX_NONNULL key_func, ZixHashFunc ZIX_NONNULL hash_func, ZixKeyEqualFunc ZIX_NONNULL equal_func); /// Free `hash` -ZIX_API -void +ZIX_API void zix_hash_free(ZixHash* ZIX_NULLABLE hash); /// Return the number of elements in the hash -ZIX_PURE_API -size_t +ZIX_PURE_API size_t zix_hash_size(const ZixHash* ZIX_NONNULL hash); /** @@ -135,23 +131,19 @@ zix_hash_size(const ZixHash* ZIX_NONNULL hash); typedef size_t ZixHashIter; /// Return an iterator to the first record in a hash, or the end if it is empty -ZIX_PURE_API -ZixHashIter +ZIX_PURE_API ZixHashIter zix_hash_begin(const ZixHash* ZIX_NONNULL hash); /// Return an iterator one past the last possible record in a hash -ZIX_PURE_API -ZixHashIter +ZIX_PURE_API ZixHashIter zix_hash_end(const ZixHash* ZIX_NONNULL hash); /// Return the record pointed to by an iterator -ZIX_PURE_API -ZixHashRecord* ZIX_NULLABLE +ZIX_PURE_API ZixHashRecord* ZIX_NULLABLE zix_hash_get(const ZixHash* ZIX_NONNULL hash, ZixHashIter i); /// Return an iterator that has been advanced to the next record in a hash -ZIX_PURE_API -ZixHashIter +ZIX_PURE_API ZixHashIter zix_hash_next(const ZixHash* ZIX_NONNULL hash, ZixHashIter i); /** @@ -189,8 +181,7 @@ typedef struct { record with this key using zix_hash_insert_at() until the hash table is modified (which invalidates the position). */ -ZIX_API -ZixHashInsertPlan +ZIX_API ZixHashInsertPlan zix_hash_plan_insert(const ZixHash* ZIX_NONNULL hash, const ZixHashKey* ZIX_NONNULL key); @@ -214,8 +205,7 @@ zix_hash_plan_insert(const ZixHash* ZIX_NONNULL hash, be inserted, and the predicate must return true only if the key it is called with (the first argument) matches the key to be inserted. */ -ZIX_API -ZixHashInsertPlan +ZIX_API ZixHashInsertPlan zix_hash_plan_insert_prehashed(const ZixHash* ZIX_NONNULL hash, ZixHashCode code, ZixKeyMatchFunc ZIX_NONNULL predicate, @@ -228,8 +218,7 @@ zix_hash_plan_insert_prehashed(const ZixHash* ZIX_NONNULL hash, can be used to insert a new record, or to access the existing matching record. */ -ZIX_PURE_API -ZixHashRecord* ZIX_NULLABLE +ZIX_PURE_API ZixHashRecord* ZIX_NULLABLE zix_hash_record_at(const ZixHash* ZIX_NONNULL hash, ZixHashInsertPlan position); /** @@ -249,8 +238,7 @@ zix_hash_record_at(const ZixHash* ZIX_NONNULL hash, ZixHashInsertPlan position); @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS if a record already exists at this position, or ZIX_STATUS_NO_MEM if growing the hash table failed. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_hash_insert_at(ZixHash* ZIX_NONNULL hash, ZixHashInsertPlan position, ZixHashRecord* ZIX_NONNULL record); @@ -269,8 +257,7 @@ zix_hash_insert_at(ZixHash* ZIX_NONNULL hash, @return ZIX_STATUS_SUCCESS, ZIX_STATUS_EXISTS, or ZIX_STATUS_NO_MEM. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_hash_insert(ZixHash* ZIX_NONNULL hash, ZixHashRecord* ZIX_NONNULL record); /** @@ -287,8 +274,7 @@ zix_hash_insert(ZixHash* ZIX_NONNULL hash, ZixHashRecord* ZIX_NONNULL record); @return ZIX_STATUS_SUCCES or ZIX_STATUS_BAD_ARG if `i` does not point at a removable record. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_hash_erase(ZixHash* ZIX_NONNULL hash, ZixHashIter i, ZixHashRecord* ZIX_NULLABLE* ZIX_NONNULL removed); @@ -301,8 +287,7 @@ zix_hash_erase(ZixHash* ZIX_NONNULL hash, @param removed Set to the removed record, or null. @return ZIX_STATUS_SUCCES or ZIX_STATUS_NOT_FOUND. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_hash_remove(ZixHash* ZIX_NONNULL hash, const ZixHashKey* ZIX_NONNULL key, ZixHashRecord* ZIX_NULLABLE* ZIX_NONNULL removed); @@ -323,8 +308,7 @@ zix_hash_remove(ZixHash* ZIX_NONNULL hash, @return An iterator to the matching record, or the end iterator if no such record exists. */ -ZIX_API -ZixHashIter +ZIX_API ZixHashIter zix_hash_find(const ZixHash* ZIX_NONNULL hash, const ZixHashKey* ZIX_NONNULL key); @@ -340,8 +324,7 @@ zix_hash_find(const ZixHash* ZIX_NONNULL hash, @return A pointer to the matching record, of null if no such record exists. */ -ZIX_API -ZixHashRecord* ZIX_NULLABLE +ZIX_API ZixHashRecord* ZIX_NULLABLE zix_hash_find_record(const ZixHash* ZIX_NONNULL hash, const ZixHashKey* ZIX_NONNULL key); diff --git a/include/zix/path.h b/include/zix/path.h index 22c8202..ba23b10 100644 --- a/include/zix/path.h +++ b/include/zix/path.h @@ -4,9 +4,9 @@ #ifndef ZIX_PATH_H #define ZIX_PATH_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/string_view.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/string_view.h> #include <stdbool.h> @@ -35,9 +35,7 @@ ZIX_BEGIN_DECLS */ /// Join path `a` and path `b` with a single directory separator between them -ZIX_API -ZIX_NODISCARD -char* ZIX_ALLOCATED +ZIX_MALLOC_API ZIX_NODISCARD char* ZIX_ALLOCATED zix_path_join(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NULLABLE a, const char* ZIX_NULLABLE b); @@ -55,9 +53,7 @@ zix_path_join(ZixAllocator* ZIX_NULLABLE allocator, converted to the preferred separator (backslash on Windows, slash everywhere else). */ -ZIX_API -ZIX_NODISCARD -char* ZIX_ALLOCATED +ZIX_MALLOC_API ZIX_NODISCARD char* ZIX_ALLOCATED zix_path_preferred(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL path); @@ -66,15 +62,13 @@ zix_path_preferred(ZixAllocator* ZIX_NULLABLE allocator, Paths in normal form have all dot segments removed and use only a single preferred separator for all separators (that is, any number of separators is - replaced with a single "\" on Windows, and a single "/" everwhere else). + replaced with a single "\" on Windows, and a single "/" everywhere else). Note that this function doesn't access the filesystem, so won't do anything like case normalization or symbolic link dereferencing. For that, use zix_canonical_path(). */ -ZIX_API -ZIX_NODISCARD -char* ZIX_ALLOCATED +ZIX_MALLOC_API ZIX_NODISCARD char* ZIX_ALLOCATED zix_path_lexically_normal(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL path); @@ -85,9 +79,7 @@ zix_path_lexically_normal(ZixAllocator* ZIX_NULLABLE allocator, equivalent path relative to `base` is returned (which may contain up-references). */ -ZIX_API -ZIX_NODISCARD -char* ZIX_ALLOCATED +ZIX_MALLOC_API ZIX_NODISCARD char* ZIX_ALLOCATED zix_path_lexically_relative(ZixAllocator* ZIX_NULLABLE allocator, const char* ZIX_NONNULL path, const char* ZIX_NONNULL base); @@ -99,13 +91,11 @@ zix_path_lexically_relative(ZixAllocator* ZIX_NULLABLE allocator, */ /// Return the root name of `path` like "C:", or null -ZIX_PURE_WIN_API -ZixStringView +ZIX_PURE_WIN_API ZixStringView zix_path_root_name(const char* ZIX_NONNULL path); /// Return the root directory of `path` like "/" or "\", or null -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_root_directory(const char* ZIX_NONNULL path); /** @@ -121,8 +111,7 @@ zix_path_root_directory(const char* ZIX_NONNULL path); @return The newly allocated root path of `path`, or null if it has no root or allocation failed. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_root_path(const char* ZIX_NONNULL path); /** @@ -131,8 +120,7 @@ zix_path_root_path(const char* ZIX_NONNULL path); If the path has no relative path (because it is empty or a root path), this returns null. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_relative_path(const char* ZIX_NONNULL path); /** @@ -151,8 +139,7 @@ zix_path_relative_path(const char* ZIX_NONNULL path); @return The newly allocated path to the parent of `path`, or null if it has no parent or allocation failed. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_parent_path(const char* ZIX_NONNULL path); /** @@ -161,8 +148,7 @@ zix_path_parent_path(const char* ZIX_NONNULL path); The filename is the name after the last directory separator. If the path has no filename, this returns null. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_filename(const char* ZIX_NONNULL path); /** @@ -171,8 +157,7 @@ zix_path_filename(const char* ZIX_NONNULL path); The "stem" is the filename without the extension, that is, everything up to the last "." if "." is not the first character. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_stem(const char* ZIX_NONNULL path); /** @@ -181,8 +166,7 @@ zix_path_stem(const char* ZIX_NONNULL path); The "extension" is everything past the last "." in the filename, if "." is not the first character. */ -ZIX_PURE_API -ZixStringView +ZIX_PURE_API ZixStringView zix_path_extension(const char* ZIX_NONNULL path); /** @@ -192,53 +176,43 @@ zix_path_extension(const char* ZIX_NONNULL path); */ /// Return true if `path` has a root path like "/" or "C:\" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_root_path(const char* ZIX_NULLABLE path); /// Return true if `path` has a root name like "C:" -ZIX_PURE_WIN_API -bool +ZIX_PURE_WIN_API bool zix_path_has_root_name(const char* ZIX_NULLABLE path); /// Return true if `path` has a root directory like "/" or "\" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_root_directory(const char* ZIX_NULLABLE path); /// Return true if `path` has a relative path "dir/file.txt" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_relative_path(const char* ZIX_NULLABLE path); /// Return true if `path` has a parent path like "dir/" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_parent_path(const char* ZIX_NULLABLE path); /// Return true if `path` has a filename like "file.txt" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_filename(const char* ZIX_NULLABLE path); /// Return true if `path` has a stem like "file" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_stem(const char* ZIX_NULLABLE path); /// Return true if `path` has an extension like ".txt" -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_has_extension(const char* ZIX_NULLABLE path); /// Return true if `path` is an absolute path -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_is_absolute(const char* ZIX_NULLABLE path); /// Return true if `path` is a relative path -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_path_is_relative(const char* ZIX_NULLABLE path); /** diff --git a/include/zix/ring.h b/include/zix/ring.h index db72f41..4431658 100644 --- a/include/zix/ring.h +++ b/include/zix/ring.h @@ -4,9 +4,9 @@ #ifndef ZIX_RING_H #define ZIX_RING_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <stdint.h> @@ -19,7 +19,7 @@ ZIX_BEGIN_DECLS */ /** - @defgroup zix_ring_setup Setup + @defgroup zix_ring_types Types @{ */ @@ -32,22 +32,30 @@ ZIX_BEGIN_DECLS typedef struct ZixRingImpl ZixRing; /** + @} + @defgroup zix_ring_setup Setup + @{ +*/ + +/** Create a new ring. @param allocator Allocator for the ring object and its array. - @param size Size of the ring in bytes (note this may be rounded up). + @param size Minimum size of the ring in bytes (rounded up to a power of 2). - At most `size` - 1 bytes may be stored in the ring at once. + Note that one byte of the ring is reserved, so in order to be able to write + `n` bytes to the ring at once, `size` must be `n + 1`. */ -ZIX_API -ZIX_NODISCARD -ZixRing* ZIX_ALLOCATED +ZIX_API ZIX_NODISCARD ZixRing* ZIX_ALLOCATED zix_ring_new(ZixAllocator* ZIX_NULLABLE allocator, uint32_t size); -/// Destroy a ring -ZIX_API -void +/** + Destroy a ring. + + This frees the ring structure and its buffer, discarding its contents. +*/ +ZIX_API void zix_ring_free(ZixRing* ZIX_NULLABLE ring); /** @@ -57,8 +65,7 @@ zix_ring_free(ZixRing* ZIX_NULLABLE ring); after zix_ring_new() to lock all ring memory to avoid page faults while using the ring. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_ring_mlock(ZixRing* ZIX_NONNULL ring); /** @@ -67,8 +74,7 @@ zix_ring_mlock(ZixRing* ZIX_NONNULL ring); This function is NOT thread-safe, it may only be called when there is no reader or writer. */ -ZIX_API -void +ZIX_API void zix_ring_reset(ZixRing* ZIX_NONNULL ring); /** @@ -77,8 +83,7 @@ zix_ring_reset(ZixRing* ZIX_NONNULL ring); This function returns a constant for any given ring, and may (but usually shouldn't) be called anywhere. */ -ZIX_PURE_API -uint32_t +ZIX_PURE_API uint32_t zix_ring_capacity(const ZixRing* ZIX_NONNULL ring); /** @@ -88,24 +93,47 @@ zix_ring_capacity(const ZixRing* ZIX_NONNULL ring); @{ */ -/// Return the number of bytes available for reading -ZIX_PURE_API -uint32_t +/** + Return the number of bytes available for reading. + + This function returns at most one less than the ring's buffer size. +*/ +ZIX_PURE_API uint32_t zix_ring_read_space(const ZixRing* ZIX_NONNULL ring); -/// Read from the ring without advancing the read head -ZIX_API -uint32_t +/** + Read from the ring without advancing the read head. + + @param ring The ring to read data from. + @param dst The buffer to write data to. + @param size The number of bytes to read from `ring` and write to `dst`. + + @return The number of bytes read, which is either `size` on success, or zero + on failure. +*/ +ZIX_API uint32_t zix_ring_peek(ZixRing* ZIX_NONNULL ring, void* ZIX_NONNULL dst, uint32_t size); -/// Read from the ring and advance the read head -ZIX_API -uint32_t +/** + Read from the ring and advance the read head. + + @param ring The ring to read data from. + @param dst The buffer to write data to. + @param size The number of bytes to read from `ring` and write to `dst`. + + @return The number of bytes read, which is either `size` on success, or zero + on failure. +*/ +ZIX_API uint32_t zix_ring_read(ZixRing* ZIX_NONNULL ring, void* ZIX_NONNULL dst, uint32_t size); -/// Advance the read head, ignoring any data -ZIX_API -uint32_t +/** + Advance the read head, ignoring any data. + + @return Either `size` on success, or zero if there aren't enough bytes to + skip. +*/ +ZIX_API uint32_t zix_ring_skip(ZixRing* ZIX_NONNULL ring, uint32_t size); /** @@ -123,20 +151,36 @@ zix_ring_skip(ZixRing* ZIX_NONNULL ring, uint32_t size); written in several chunks before being "committed" and becoming readable. This can be useful for things like prefixing messages with a header without needing an allocated buffer to construct the "packet". + + The contents of this structure are an implementation detail and must not be + manipulated by the user. */ typedef struct { uint32_t read_head; ///< Read head at the start of the transaction uint32_t write_head; ///< Write head if the transaction were committed } ZixRingTransaction; -/// Return the number of bytes available for writing -ZIX_PURE_API -uint32_t +/** + Return the number of bytes available for writing. + + This function returns at most one less than the ring's buffer size. +*/ +ZIX_PURE_API uint32_t zix_ring_write_space(const ZixRing* ZIX_NONNULL ring); -/// Write data to the ring -ZIX_API -uint32_t +/** + Write data to the ring. + + This writes a contiguous input buffer of bytes to the ring. + + @param ring The ring to write data to. + @param src The buffer to read data from. + @param size The number of bytes to read from `src` and write to `ring`. + + @return The number of bytes written, which is either `size` on success, or + zero on failure. +*/ +ZIX_API uint32_t zix_ring_write(ZixRing* ZIX_NONNULL ring, const void* ZIX_NONNULL src, uint32_t size); @@ -155,9 +199,7 @@ zix_ring_write(ZixRing* ZIX_NONNULL ring, @param ring The ring to write data to. @return A new empty transaction. */ -ZIX_API -ZIX_NODISCARD -ZixRingTransaction +ZIX_API ZIX_NODISCARD ZixRingTransaction zix_ring_begin_write(ZixRing* ZIX_NONNULL ring); /** @@ -178,8 +220,7 @@ zix_ring_begin_write(ZixRing* ZIX_NONNULL ring); @param size Length of data to write in bytes. @return #ZIX_STATUS_NO_MEM or #ZIX_STATUS_SUCCESS. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_ring_amend_write(ZixRing* ZIX_NONNULL ring, ZixRingTransaction* ZIX_NONNULL tx, const void* ZIX_NONNULL src, @@ -198,8 +239,7 @@ zix_ring_amend_write(ZixRing* ZIX_NONNULL ring, @param tx The active transaction, from zix_ring_begin_write(). @return #ZIX_STATUS_SUCCESS. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_ring_commit_write(ZixRing* ZIX_NONNULL ring, const ZixRingTransaction* ZIX_NONNULL tx); diff --git a/include/zix/sem.h b/include/zix/sem.h index 42dd6c3..49ab5f8 100644 --- a/include/zix/sem.h +++ b/include/zix/sem.h @@ -4,8 +4,8 @@ #ifndef ZIX_SEM_H #define ZIX_SEM_H -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/attributes.h> +#include <zix/status.h> #ifdef __APPLE__ # include <mach/mach.h> @@ -15,10 +15,10 @@ # include <semaphore.h> #endif -ZIX_BEGIN_DECLS - #include <stdint.h> +ZIX_BEGIN_DECLS + /** @defgroup zix_sem Semaphore @ingroup zix_threading @@ -37,7 +37,7 @@ ZIX_BEGIN_DECLS be 0 so the semaphore can be used as a simple signal where each post corresponds to one wait. - Semaphores are very efficient (much moreso than a mutex/cond pair). In + Semaphores are very efficient (compared to a mutex/cond pair). In particular, at least on Linux, post is async-signal-safe, which means it does not block and will not be interrupted. If you need to signal from a realtime thread, this is the most appropriate primitive to use. @@ -49,8 +49,7 @@ typedef struct ZixSemImpl ZixSem; @return #ZIX_STATUS_SUCCESS, or an unlikely error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial); /** @@ -58,8 +57,7 @@ zix_sem_init(ZixSem* ZIX_NONNULL sem, unsigned initial); @return #ZIX_STATUS_SUCCESS, or an error. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_destroy(ZixSem* ZIX_NONNULL sem); /** @@ -71,8 +69,7 @@ zix_sem_destroy(ZixSem* ZIX_NONNULL sem); if the maximum possible value would have been exceeded, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_post(ZixSem* ZIX_NONNULL sem); /** @@ -83,8 +80,7 @@ zix_sem_post(ZixSem* ZIX_NONNULL sem); @return #ZIX_STATUS_SUCCESS if `sem` was decremented, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_wait(ZixSem* ZIX_NONNULL sem); /** @@ -94,8 +90,7 @@ zix_sem_wait(ZixSem* ZIX_NONNULL sem); #ZIX_STATUS_UNAVAILABLE if it was already zero, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_try_wait(ZixSem* ZIX_NONNULL sem); /** @@ -108,8 +103,7 @@ zix_sem_try_wait(ZixSem* ZIX_NONNULL sem); the system does not support timed waits, or #ZIX_STATUS_BAD_ARG if `sem` is invalid. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_sem_timed_wait(ZixSem* ZIX_NONNULL sem, uint32_t seconds, uint32_t nanoseconds); diff --git a/include/zix/status.h b/include/zix/status.h index 47aede8..d03160e 100644 --- a/include/zix/status.h +++ b/include/zix/status.h @@ -4,7 +4,7 @@ #ifndef ZIX_STATUS_H #define ZIX_STATUS_H -#include "zix/attributes.h" +#include <zix/attributes.h> ZIX_BEGIN_DECLS @@ -38,8 +38,7 @@ typedef enum { The returned string is always one sentence, with an uppercase first character, and no trailing period. */ -ZIX_CONST_API -const char* +ZIX_CONST_API const char* zix_strerror(ZixStatus status); /** diff --git a/include/zix/string_view.h b/include/zix/string_view.h index cfd478d..be15018 100644 --- a/include/zix/string_view.h +++ b/include/zix/string_view.h @@ -4,8 +4,8 @@ #ifndef ZIX_STRING_VIEW_H #define ZIX_STRING_VIEW_H -#include "zix/allocator.h" -#include "zix/attributes.h" +#include <zix/allocator.h> +#include <zix/attributes.h> #include <stdbool.h> #include <stddef.h> @@ -40,9 +40,7 @@ typedef struct { // clang-format on /// Return a view of an empty string -ZIX_ALWAYS_INLINE_FUNC -ZIX_CONST_FUNC -static inline ZixStringView +ZIX_ALWAYS_INLINE_FUNC ZIX_CONST_FUNC static inline ZixStringView zix_empty_string(void) { const ZixStringView view = {"", 0U}; @@ -62,9 +60,7 @@ zix_empty_string(void) @param len Length of the substring in bytes, not including the trailing null terminator if present. */ -ZIX_ALWAYS_INLINE_FUNC -ZIX_CONST_FUNC -static inline ZixStringView +ZIX_ALWAYS_INLINE_FUNC ZIX_CONST_FUNC static inline ZixStringView zix_substring(const char* const ZIX_NONNULL str, const size_t len) { const ZixStringView view = {str, len}; @@ -78,9 +74,7 @@ zix_substring(const char* const ZIX_NONNULL str, const size_t len) @param str Pointer to the start of a null-terminated C string, or null. */ -ZIX_ALWAYS_INLINE_FUNC -ZIX_PURE_FUNC -static inline ZixStringView +ZIX_ALWAYS_INLINE_FUNC ZIX_PURE_FUNC static inline ZixStringView // NOLINTNEXTLINE(clang-diagnostic-unused-function) zix_string(const char* const ZIX_NULLABLE str) { @@ -90,8 +84,7 @@ zix_string(const char* const ZIX_NULLABLE str) /** Copy a string view into a newly allocated null-terminated string. */ -ZIX_MALLOC_API -char* ZIX_ALLOCATED +ZIX_MALLOC_API char* ZIX_ALLOCATED zix_string_view_copy(ZixAllocator* ZIX_NULLABLE allocator, ZixStringView view); /** @@ -101,8 +94,7 @@ zix_string_view_copy(ZixAllocator* ZIX_NULLABLE allocator, ZixStringView view); has fast paths for when the operands have different lengths, or point to the same string data. */ -ZIX_PURE_API -bool +ZIX_PURE_API bool zix_string_view_equals(ZixStringView lhs, ZixStringView rhs); /** diff --git a/include/zix/thread.h b/include/zix/thread.h index 2493ed3..77a6bf5 100644 --- a/include/zix/thread.h +++ b/include/zix/thread.h @@ -4,8 +4,8 @@ #ifndef ZIX_THREAD_H #define ZIX_THREAD_H -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/attributes.h> +#include <zix/status.h> #ifdef _WIN32 # include <windows.h> @@ -62,8 +62,7 @@ typedef ZixThreadResult(ZIX_THREAD_FUNC* ZixThreadFunc)(void*); @return #ZIX_STATUS_SUCCESS on success, or #ZIX_STATUS_ERROR. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_thread_create(ZixThread* thread, size_t stack_size, ZixThreadFunc function, @@ -74,8 +73,7 @@ zix_thread_create(ZixThread* thread, @return #ZIX_STATUS_SUCCESS on success, or #ZIX_STATUS_ERROR. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_thread_join(ZixThread thread); /** diff --git a/include/zix/tree.h b/include/zix/tree.h index b51fa72..7a223a5 100644 --- a/include/zix/tree.h +++ b/include/zix/tree.h @@ -4,9 +4,9 @@ #ifndef ZIX_TREE_H #define ZIX_TREE_H -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <stdbool.h> #include <stddef.h> @@ -20,7 +20,7 @@ ZIX_BEGIN_DECLS */ /** - @defgroup zix_tree_setup Setup + @defgroup zix_tree_types Types @{ */ @@ -36,10 +36,14 @@ typedef int (*ZixTreeCompareFunc)(const void* ZIX_UNSPECIFIED a, typedef void (*ZixTreeDestroyFunc)(void* ZIX_UNSPECIFIED ptr, const void* ZIX_UNSPECIFIED user_data); +/** + @} + @defgroup zix_tree_setup Setup + @{ +*/ + /// Create a new (empty) tree -ZIX_API -ZIX_NODISCARD -ZixTree* ZIX_ALLOCATED +ZIX_API ZIX_NODISCARD ZixTree* ZIX_ALLOCATED zix_tree_new(ZixAllocator* ZIX_NULLABLE allocator, bool allow_duplicates, ZixTreeCompareFunc ZIX_NONNULL cmp, @@ -48,13 +52,11 @@ zix_tree_new(ZixAllocator* ZIX_NULLABLE allocator, const void* ZIX_NULLABLE destroy_user_data); /// Free `t` -ZIX_API -void +ZIX_API void zix_tree_free(ZixTree* ZIX_NULLABLE t); /// Return the number of elements in `t` -ZIX_PURE_API -size_t +ZIX_PURE_API size_t zix_tree_size(const ZixTree* ZIX_NONNULL t); /** @@ -67,48 +69,39 @@ zix_tree_size(const ZixTree* ZIX_NONNULL t); typedef struct ZixTreeNodeImpl ZixTreeIter; /// Return the data associated with the given tree item -ZIX_PURE_API -void* ZIX_UNSPECIFIED +ZIX_PURE_API void* ZIX_UNSPECIFIED zix_tree_get(const ZixTreeIter* ZIX_NULLABLE ti); /// Return an iterator to the first (smallest) element in `t` -ZIX_PURE_API -ZixTreeIter* ZIX_NULLABLE +ZIX_PURE_API ZixTreeIter* ZIX_NULLABLE zix_tree_begin(ZixTree* ZIX_NONNULL t); /// Return an iterator the the element one past the last element in `t` -ZIX_CONST_API -ZixTreeIter* ZIX_NULLABLE +ZIX_CONST_API ZixTreeIter* ZIX_NULLABLE zix_tree_end(ZixTree* ZIX_NONNULL t); /// Return true iff `i` is an iterator to the end of its tree -ZIX_CONST_API -bool +ZIX_CONST_API bool zix_tree_iter_is_end(const ZixTreeIter* ZIX_NULLABLE i); /// Return an iterator to the last (largest) element in `t` -ZIX_PURE_API -ZixTreeIter* ZIX_NULLABLE +ZIX_PURE_API ZixTreeIter* ZIX_NULLABLE zix_tree_rbegin(ZixTree* ZIX_NONNULL t); /// Return an iterator the the element one before the first element in `t` -ZIX_CONST_API -ZixTreeIter* ZIX_NULLABLE +ZIX_CONST_API ZixTreeIter* ZIX_NULLABLE zix_tree_rend(ZixTree* ZIX_NONNULL t); /// Return true iff `i` is an iterator to the reverse end of its tree -ZIX_CONST_API -bool +ZIX_CONST_API bool zix_tree_iter_is_rend(const ZixTreeIter* ZIX_NULLABLE i); /// Return an iterator that points to the element one past `i` -ZIX_PURE_API -ZixTreeIter* ZIX_NULLABLE +ZIX_PURE_API ZixTreeIter* ZIX_NULLABLE zix_tree_iter_next(ZixTreeIter* ZIX_NULLABLE i); /// Return an iterator that points to the element one before `i` -ZIX_PURE_API -ZixTreeIter* ZIX_NULLABLE +ZIX_PURE_API ZixTreeIter* ZIX_NULLABLE zix_tree_iter_prev(ZixTreeIter* ZIX_NULLABLE i); /** @@ -118,15 +111,13 @@ zix_tree_iter_prev(ZixTreeIter* ZIX_NULLABLE i); */ /// Insert the element `e` into `t` and point `ti` at the new element -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_tree_insert(ZixTree* ZIX_NONNULL t, void* ZIX_UNSPECIFIED e, ZixTreeIter* ZIX_NULLABLE* ZIX_NULLABLE ti); /// Remove the item pointed at by `ti` from `t` -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_tree_remove(ZixTree* ZIX_NONNULL t, ZixTreeIter* ZIX_NONNULL ti); /** @@ -140,8 +131,7 @@ zix_tree_remove(ZixTree* ZIX_NONNULL t, ZixTreeIter* ZIX_NONNULL ti); If no such item exists, `ti` is set to NULL. */ -ZIX_API -ZixStatus +ZIX_API ZixStatus zix_tree_find(const ZixTree* ZIX_NONNULL t, const void* ZIX_UNSPECIFIED e, ZixTreeIter* ZIX_NULLABLE* ZIX_NONNULL ti); diff --git a/include/zix/zix.h b/include/zix/zix.h index ca72d35..ceaadc0 100644 --- a/include/zix/zix.h +++ b/include/zix/zix.h @@ -1,4 +1,4 @@ -// Copyright 2016-2022 David Robillard <d@drobilla.net> +// Copyright 2016-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #ifndef ZIX_ZIX_H @@ -16,9 +16,9 @@ @{ */ -#include "zix/attributes.h" -#include "zix/status.h" -#include "zix/string_view.h" +#include <zix/attributes.h> +#include <zix/status.h> +#include <zix/string_view.h> /** @} @@ -26,8 +26,8 @@ @{ */ -#include "zix/allocator.h" -#include "zix/bump_allocator.h" +#include <zix/allocator.h> +#include <zix/bump_allocator.h> /** @} @@ -35,7 +35,7 @@ @{ */ -#include "zix/digest.h" +#include <zix/digest.h> /** @} @@ -43,10 +43,10 @@ @{ */ -#include "zix/btree.h" -#include "zix/hash.h" -#include "zix/ring.h" -#include "zix/tree.h" +#include <zix/btree.h> +#include <zix/hash.h> +#include <zix/ring.h> +#include <zix/tree.h> /** @} @@ -54,8 +54,8 @@ @{ */ -#include "zix/sem.h" -#include "zix/thread.h" +#include <zix/sem.h> +#include <zix/thread.h> /** @} @@ -63,8 +63,16 @@ @{ */ -#include "zix/filesystem.h" -#include "zix/path.h" +#include <zix/filesystem.h> +#include <zix/path.h> + +/** + @} + @defgroup zix_environment Environment + @{ +*/ + +#include <zix/environment.h> /** @} diff --git a/meson.build b/meson.build index f84642b..7a8d68c 100644 --- a/meson.build +++ b/meson.build @@ -1,4 +1,4 @@ -# Copyright 2020-2023 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC project( @@ -12,7 +12,7 @@ project( ], license: 'ISC', meson_version: '>= 0.56.0', - version: '0.5.0', + version: '0.6.3', ) zix_src_root = meson.current_source_dir() @@ -29,37 +29,27 @@ versioned_name = 'zix' + version_suffix pkg = import('pkgconfig') cc = meson.get_compiler('c') -# Restrict Windows API usage to Vista and earlier -if host_machine.system() == 'windows' - if cc.get_id() == 'msvc' - add_project_arguments('/D_WIN32_WINNT=0x0600', language: ['c', 'cpp']) - else - add_project_arguments('-D_WIN32_WINNT=0x0600', language: ['c', 'cpp']) - endif -endif - # Set global warning suppressions warning_level = get_option('warning_level') c_suppressions = [] if cc.get_id() in ['clang', 'emscripten'] if warning_level == 'everything' c_suppressions += [ - '-Wno-bad-function-cast', - '-Wno-c11-extensions', # Glib '-Wno-declaration-after-statement', '-Wno-implicit-fallthrough', # Really for clang < 12 '-Wno-padded', + '-Wno-switch-default', '-Wno-unsafe-buffer-usage', + '-Wno-unsafe-buffer-usage-in-libc-call', ] if not meson.is_cross_build() - c_suppressions += [ - '-Wno-poison-system-directories', - ] + c_suppressions += ['-Wno-poison-system-directories'] endif if host_machine.system() == 'windows' c_suppressions += [ + '-Wno-bad-function-cast', '-Wno-deprecated-declarations', '-Wno-nonportable-system-include-path', ] @@ -67,22 +57,16 @@ if cc.get_id() in ['clang', 'emscripten'] endif if warning_level in ['everything', '3'] - c_suppressions += [ - '-Wno-nullability-extension', - ] + c_suppressions += ['-Wno-nullability-extension'] endif if cc.get_id() == 'emscripten' - c_suppressions += [ - '-Wno-format', - ] + c_suppressions += ['-Wno-format'] endif elif cc.get_id() == 'gcc' if warning_level == 'everything' c_suppressions += [ - '-Wno-bad-function-cast', - '-Wno-cast-function-type', '-Wno-inline', '-Wno-padded', '-Wno-strict-overflow', @@ -92,7 +76,7 @@ elif cc.get_id() == 'gcc' if host_machine.system() == 'windows' c_suppressions += [ - '-Wno-format', + '-Wno-bad-function-cast', '-Wno-suggest-attribute=const', '-Wno-suggest-attribute=format', '-Wno-suggest-attribute=pure', @@ -101,9 +85,7 @@ elif cc.get_id() == 'gcc' endif if warning_level in ['everything', '3'] - c_suppressions += [ - '-Wno-pedantic', # C11 - ] + c_suppressions += ['-Wno-pedantic'] # C11 endif elif cc.get_id() == 'msvc' @@ -181,6 +163,21 @@ elif host_machine.system() in ['dragonfly', 'freebsd', 'netbsd', 'openbsd'] system_c_args += [ '-D_BSD_SOURCE', ] +elif host_machine.system() == 'windows' + if not get_option('win_wchar').disabled() + system_c_args += ['-DUNICODE', '-D_UNICODE'] + endif + + winvers = { + 'nt4': '0x0400', + 'winxp': '0x0501', + 'vista': '0x0600', + 'win8': '0x0602', + } + system_c_args += [ + '-DWIN32_LEAN_AND_MEAN', + '-D_WIN32_WINNT=' + winvers[get_option('win_ver')], + ] else system_c_args += [ '-D_POSIX_C_SOURCE=200809L', @@ -258,16 +255,37 @@ else } windows_checks = { + 'CreateFile2': template.format( + 'windows.h', + 'CREATEFILE2_EXTENDED_PARAMETERS p = {0};\n' + + 'return CreateFile2(NULL, FILE_READ_ATTRIBUTES, 0U, OPEN_EXISTING, &p);', + ), + 'CreateHardLink': template.format( + 'windows.h', + 'return CreateHardLink(TEXT("l"), TEXT("t"), NULL);', + ), 'CreateSymbolicLink': template.format( 'windows.h', - 'return CreateSymbolicLink(' + '"l", "t", SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);', + 'return CreateSymbolicLink(TEXT("l"), TEXT("t"), SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);', + ), + 'GetFinalPathNameByHandle': template.format( + 'windows.h', + 'return GetFinalPathNameByHandle(NULL, NULL, 0U, 0U);', + ), + 'VirtualLock': template.format( + 'windows.h', + 'return VirtualLock(NULL, sizeof(void*));', ), } + check_c_args = system_c_args if host_machine.system() == 'darwin' checks = posix_checks + mac_checks elif host_machine.system() == 'windows' checks = windows_checks + if cc.get_id() == 'msvc' + check_c_args += ['/Wall', '/WX'] + endif else checks = posix_checks @@ -276,7 +294,7 @@ else '''#include <semaphore.h> #include <time.h> int main(void) { sem_t s; struct timespec t; return sem_timedwait(&s, &t); }''', - args: system_c_args, + args: check_c_args, dependencies: thread_dep, name: 'sem_timedwait', ) @@ -286,7 +304,18 @@ int main(void) { sem_t s; struct timespec t; return sem_timedwait(&s, &t); }''', endif foreach name, check_code : checks - if cc.links(check_code, args: system_c_args, name: name) + is_mingw = host_machine.system() == 'windows' and cc.get_id() == 'gcc' + if is_mingw and name in ['CreateFile2', 'CreateSymbolicLink'] + message('Ignoring MinGW stub @0@'.format(name)) + continue + endif + + if cc.get_id() == 'emscripten' and name in ['flock', 'fileno'] + message('Ignoring emscripten stub @0@'.format(name)) + continue + endif + + if cc.links(check_code, args: check_c_args, name: name) platform_c_args += ['-DHAVE_@0@'.format(name.to_upper())] endif endforeach @@ -304,6 +333,7 @@ c_headers = files( 'include/zix/btree.h', 'include/zix/bump_allocator.h', 'include/zix/digest.h', + 'include/zix/environment.h', 'include/zix/filesystem.h', 'include/zix/hash.h', 'include/zix/path.h', @@ -332,18 +362,16 @@ sources = files( 'src/tree.c', ) -if host_machine.system() == 'darwin' - sources += files( - 'src/posix/filesystem_posix.c', - 'src/posix/system_posix.c', - ) -elif host_machine.system() == 'windows' +if host_machine.system() == 'windows' sources += files( + 'src/win32/environment_win32.c', 'src/win32/filesystem_win32.c', 'src/win32/system_win32.c', + 'src/win32/win32_util.c', ) else sources += files( + 'src/posix/environment_posix.c', 'src/posix/filesystem_posix.c', 'src/posix/system_posix.c', ) @@ -378,7 +406,7 @@ endif # Set any additional arguments required for building libraries or programs library_c_args = platform_c_args + extra_c_args + ['-DZIX_INTERNAL'] library_link_args = [] -program_c_args = platform_c_args + extra_c_args +program_c_args = extra_c_args program_link_args = [] if cc.get_id() == 'emscripten' wasm_c_args = [ @@ -387,10 +415,7 @@ if cc.get_id() == 'emscripten' '-pthread', ] - wasm_link_args = [ - '-matomics', - '-mbulk-memory', - '-pthread', + wasm_link_args = wasm_c_args + [ ['-s', 'ENVIRONMENT=node,worker'], ['-s', 'INITIAL_MEMORY=33554432'], ] @@ -410,9 +435,10 @@ libzix = library( versioned_name, sources, c_args: c_suppressions + library_c_args, - darwin_versions: ['0.5.0', meson.project_version()], + darwin_versions: ['0.6.0', meson.project_version()], dependencies: dependencies, gnu_symbol_visibility: 'hidden', + implicit_include_directories: false, include_directories: include_dirs, install: true, link_args: library_link_args, @@ -427,7 +453,7 @@ zix_dep = declare_dependency( link_with: libzix, ) -# Generage pkg-config file for external dependants +# Generate pkg-config file for external dependants pkg.generate( libzix, description: 'Lightweight C library of portability wrappers and data structures', diff --git a/meson_options.txt b/meson_options.txt index 3eac738..4222993 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -1,35 +1,42 @@ -# Copyright 2020-2023 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -option('benchmarks', type: 'feature', value: 'auto', yield: true, +option('benchmarks', type: 'feature', yield: true, description: 'Build benchmarks') option('checks', type: 'feature', value: 'enabled', yield: true, description: 'Check for platform-specific features') -option('docs', type: 'feature', value: 'auto', yield: true, +option('docs', type: 'feature', yield: true, description: 'Build documentation') -option('html', type: 'feature', value: 'auto', yield: true, +option('html', type: 'feature', yield: true, description: 'Build paginated HTML documentation') -option('lint', type: 'boolean', value: false, yield: true, +option('lint', type: 'boolean', value: false, description: 'Run code quality checks') -option('posix', type: 'feature', value: 'auto', yield: true, +option('posix', type: 'feature', yield: true, description: 'Use POSIX system facilities') -option('singlehtml', type: 'feature', value: 'auto', yield: true, +option('singlehtml', type: 'feature', yield: true, description: 'Build single-page HTML documentation') -option('threads', type: 'feature', value: 'auto', yield: true, +option('threads', type: 'feature', yield: true, description: 'Enable thread support') -option('tests', type: 'feature', value: 'auto', yield: true, +option('tests', type: 'feature', yield: true, description: 'Build tests') -option('tests_cpp', type: 'feature', value: 'auto', yield: true, +option('tests_cpp', type: 'feature', yield: true, description: 'Build C++ standard library comparison tests') option('title', type: 'string', value: 'Zix', description: 'Project title') + +option('win_ver', type: 'combo', value: 'win8', + choices: ['nt4', 'winxp', 'vista', 'win8'], + description: 'Latest Windows API version to use') + +option('win_wchar', type: 'feature', yield: true, + description: 'Use UTF-16 wchar_t and UNICODE with Windows API') diff --git a/scripts/benchmark.py b/scripts/benchmark.py index c160445..6721309 100755 --- a/scripts/benchmark.py +++ b/scripts/benchmark.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 -# Copyright 2011-2022 David Robillard <d@drobilla.net> +# Copyright 2011-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: ISC """ @@ -14,7 +14,7 @@ import subprocess # Benchmark trees -subprocess.call(["./tree_bench", "40000", "640000"]) +subprocess.call(["benchmark/tree_bench", "40000", "640000"]) subprocess.call( [ "../scripts/plot.py", @@ -64,7 +64,7 @@ if not os.path.exists(FILENAME): out.write(random_word()) out.write("\n") -subprocess.call(["./dict_bench", "gibberish.txt"]) +subprocess.call(["benchmark/dict_bench", "gibberish.txt"]) subprocess.call( [ "../scripts/plot.py", diff --git a/src/.clang-tidy b/src/.clang-tidy index 5647a8f..b3b1960 100644 --- a/src/.clang-tidy +++ b/src/.clang-tidy @@ -4,6 +4,7 @@ Checks: > -*-magic-numbers, -bugprone-easily-swappable-parameters, + -bugprone-multi-level-implicit-pointer-conversion, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, -llvm-header-guard, -misc-no-recursion, diff --git a/src/allocator.c b/src/allocator.c index 6c18804..b1453b4 100644 --- a/src/allocator.c +++ b/src/allocator.c @@ -1,30 +1,27 @@ // Copyright 2011-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/allocator.h" - -#include "zix/attributes.h" +#include <zix/allocator.h> #include "zix_config.h" +#include <zix/attributes.h> + #ifdef _WIN32 -# define WIN32_LEAN_AND_MEAN # include <malloc.h> # include <windows.h> #endif #include <stdlib.h> -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_malloc(ZixAllocator* const allocator, const size_t size) { (void)allocator; return malloc(size); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_calloc(ZixAllocator* const allocator, const size_t nmemb, const size_t size) @@ -49,8 +46,7 @@ zix_default_free(ZixAllocator* const allocator, void* const ptr) free(ptr); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_default_aligned_alloc(ZixAllocator* const allocator, const size_t alignment, const size_t size) diff --git a/src/btree.c b/src/btree.c index 7970796..dabd406 100644 --- a/src/btree.c +++ b/src/btree.c @@ -1,7 +1,11 @@ -// Copyright 2011-2021 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/btree.h" +#include <zix/btree.h> + +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <assert.h> #include <stdint.h> @@ -10,9 +14,9 @@ // #define ZIX_BTREE_SORTED_CHECK 1 // Define ZixShort as an integer type half the size of a pointer -#if UINTPTR_MAX >= UINT32_MAX +#if UINTPTR_MAX > UINT32_MAX // 64-bit typedef uint32_t ZixShort; -#else +#else // 32-bit typedef uint16_t ZixShort; #endif @@ -78,8 +82,7 @@ zix_btree_node_new(ZixAllocator* const allocator, const bool leaf) return node; } -ZIX_PURE_FUNC -static ZixBTreeNode* +ZIX_PURE_FUNC static ZixBTreeNode* zix_btree_child(const ZixBTreeNode* const node, const unsigned i) { assert(!node->is_leaf); @@ -99,23 +102,15 @@ zix_btree_new(ZixAllocator* const allocator, assert(cmp); - ZixBTree* const t = (ZixBTree*)zix_aligned_alloc( - allocator, ZIX_BTREE_PAGE_SIZE, ZIX_BTREE_PAGE_SIZE); - - if (!t) { - return NULL; - } - - if (!(t->root = zix_btree_node_new(allocator, true))) { - zix_aligned_free(allocator, t); - return NULL; + ZixBTree* const t = (ZixBTree*)zix_malloc(allocator, sizeof(ZixBTree)); + if (t) { + t->allocator = allocator; + t->root = NULL; + t->cmp = cmp; + t->cmp_data = cmp_data; + t->size = 0U; } - t->allocator = allocator; - t->cmp = cmp; - t->cmp_data = cmp_data; - t->size = 0U; - return t; } @@ -154,20 +149,22 @@ zix_btree_free(ZixBTree* const t, if (t) { zix_btree_clear(t, destroy, destroy_user_data); zix_aligned_free(t->allocator, t->root); - zix_aligned_free(t->allocator, t); + zix_free(t->allocator, t); } } void -zix_btree_clear(ZixBTree* const t, - ZixBTreeDestroyFunc destroy, - const void* destroy_user_data) +zix_btree_clear(ZixBTree* const t, + const ZixBTreeDestroyFunc destroy, + const void* const destroy_user_data) { - zix_btree_free_children(t, t->root, destroy, destroy_user_data); + if (t->root) { + zix_btree_free_children(t, t->root, destroy, destroy_user_data); + zix_aligned_free(t->allocator, t->root); + t->root = NULL; + } - memset(t->root, 0U, sizeof(ZixBTreeNode)); - t->root->is_leaf = true; - t->size = 0U; + t->size = 0U; } size_t @@ -196,7 +193,7 @@ zix_btree_ainsert(void** const array, const unsigned i, void* const e) { - memmove(array + i + 1U, array + i, (n - i) * sizeof(e)); + memmove(array + i + 1U, array + i, ((size_t)n - i) * sizeof(e)); array[i] = e; } @@ -205,7 +202,7 @@ static void* zix_btree_aerase(void** const array, const unsigned n, const unsigned i) { void* const ret = array[i]; - memmove(array + i, array + i + 1U, (n - i) * sizeof(ret)); + memmove(array + i, array + i + 1U, ((size_t)n - i) * sizeof(ret)); return ret; } @@ -221,7 +218,7 @@ zix_btree_split_child(ZixAllocator* const allocator, assert(i < n->n_vals + 1U); assert(zix_btree_child(n, i) == lhs); - const ZixShort max_n_vals = zix_btree_max_vals(lhs); + const unsigned max_n_vals = zix_btree_max_vals(lhs); ZixBTreeNode* rhs = zix_btree_node_new(allocator, lhs->is_leaf); if (!rhs) { return NULL; @@ -247,7 +244,7 @@ zix_btree_split_child(ZixAllocator* const allocator, rhs->n_vals * sizeof(void*)); memcpy(rhs->data.inode.children, lhs->data.inode.children + lhs->n_vals + 1U, - (rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); + ((size_t)rhs->n_vals + 1U) * sizeof(ZixBTreeNode*)); // Move middle value up to parent zix_btree_ainsert( @@ -394,14 +391,14 @@ zix_btree_leaf_find(const ZixBTree* const t, t->cmp, t->cmp_data, n->data.leaf.vals, n->n_vals, e, equal); } -static inline bool +ZIX_PURE_FUNC static inline bool zix_btree_can_remove_from(const ZixBTreeNode* const n) { assert(n->n_vals >= zix_btree_min_vals(n)); return n->n_vals > zix_btree_min_vals(n); } -static inline bool +ZIX_PURE_FUNC static inline bool zix_btree_is_full(const ZixBTreeNode* const n) { assert(n->n_vals <= zix_btree_max_vals(n)); @@ -433,8 +430,13 @@ zix_btree_insert(ZixBTree* const t, void* const e) ZixStatus st = ZIX_STATUS_SUCCESS; - // Grow up if necessary to ensure the root is not full - if (zix_btree_is_full(t->root)) { + if (!t->root) { + // Empty tree, create a new leaf root + if (!(t->root = zix_btree_node_new(t->allocator, true))) { + return ZIX_STATUS_NO_MEM; + } + } else if (zix_btree_is_full(t->root)) { + // Grow up if necessary to ensure the root is not full if ((st = zix_btree_grow_up(t))) { return st; } @@ -490,8 +492,9 @@ zix_btree_insert(ZixBTree* const t, void* const e) static void zix_btree_iter_set_frame(ZixBTreeIter* const ti, ZixBTreeNode* const n, - const ZixShort i) + const unsigned i) { + assert(i <= UINT16_MAX); ti->nodes[ti->level] = n; ti->indexes[ti->level] = (uint16_t)i; } @@ -617,7 +620,7 @@ zix_btree_merge(ZixBTree* const t, ZixBTreeNode* const n, const unsigned i) rhs->n_vals * sizeof(void*)); memcpy(lhs->data.inode.children + lhs->n_vals, rhs->data.inode.children, - (rhs->n_vals + 1U) * sizeof(void*)); + ((size_t)rhs->n_vals + 1U) * sizeof(void*)); } lhs->n_vals += rhs->n_vals; @@ -677,6 +680,7 @@ zix_btree_fatten_child(ZixBTree* const t, ZixBTreeIter* const iter) assert(n); assert(!n->is_leaf); + assert(n->n_vals); ZixBTreeNode* const* const children = n->data.inode.children; if (i > 0U && zix_btree_can_remove_from(children[i - 1U])) { @@ -825,6 +829,9 @@ zix_btree_find(const ZixBTree* const t, ZixBTreeNode* n = t->root; *ti = zix_btree_end_iter; + if (!n) { + return ZIX_STATUS_NOT_FOUND; + } while (!n->is_leaf) { bool equal = false; @@ -866,6 +873,9 @@ zix_btree_lower_bound(const ZixBTree* const t, ZixBTreeNode* n = t->root; // Current node uint16_t found_level = 0U; // Lowest level a match was found at bool found = false; // True if a match was ever found + if (!n) { + return ZIX_STATUS_SUCCESS; + } // Search down until we reach a leaf while (!n->is_leaf) { @@ -959,7 +969,7 @@ zix_btree_end(const ZixBTree* const t) bool zix_btree_iter_equals(const ZixBTreeIter lhs, const ZixBTreeIter rhs) { - const size_t indexes_size = (lhs.level + 1U) * sizeof(uint16_t); + const size_t indexes_size = ((size_t)lhs.level + 1U) * sizeof(uint16_t); return (lhs.level == rhs.level) && (lhs.nodes[0U] == rhs.nodes[0U]) && (!lhs.nodes[0U] || !memcmp(lhs.indexes, rhs.indexes, indexes_size)); diff --git a/src/bump_allocator.c b/src/bump_allocator.c index 924a1c8..01c1652 100644 --- a/src/bump_allocator.c +++ b/src/bump_allocator.c @@ -1,10 +1,10 @@ // Copyright 2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/bump_allocator.h" +#include <zix/bump_allocator.h> -#include "zix/allocator.h" -#include "zix/attributes.h" +#include <zix/allocator.h> +#include <zix/attributes.h> #include <assert.h> #include <stddef.h> @@ -13,8 +13,7 @@ static const size_t min_alignment = sizeof(uintmax_t); -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t round_up_multiple(const size_t number, const size_t factor) { assert(factor); // Factor must be non-zero @@ -23,8 +22,7 @@ round_up_multiple(const size_t number, const size_t factor) return (number + factor - 1U) & ~(factor - 1U); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_malloc(ZixAllocator* const allocator, const size_t size) { ZixBumpAllocator* const state = (ZixBumpAllocator*)allocator; @@ -46,8 +44,7 @@ zix_bump_malloc(ZixAllocator* const allocator, const size_t size) return (void*)((char*)state->buffer + state->last); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_calloc(ZixAllocator* const allocator, const size_t nmemb, const size_t size) @@ -91,8 +88,7 @@ zix_bump_free(ZixAllocator* const allocator, void* const ptr) } } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_bump_aligned_alloc(ZixAllocator* const allocator, const size_t alignment, const size_t size) @@ -138,8 +134,7 @@ zix_bump_aligned_free(ZixAllocator* const allocator, void* const ptr) zix_bump_free(allocator, ptr); } -ZIX_CONST_FUNC -ZixBumpAllocator +ZIX_CONST_FUNC ZixBumpAllocator zix_bump_allocator(const size_t capacity, void* buffer) { const size_t aligned_top = (uintptr_t)buffer % min_alignment; diff --git a/src/darwin/sem_darwin.c b/src/darwin/sem_darwin.c index 0ace564..dd5750b 100644 --- a/src/darwin/sem_darwin.c +++ b/src/darwin/sem_darwin.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> -#include "zix/status.h" +#include <zix/status.h> #include <mach/mach.h> diff --git a/src/digest.c b/src/digest.c index bc65f90..c40b1cc 100644 --- a/src/digest.c +++ b/src/digest.c @@ -1,7 +1,7 @@ // Copyright 2012-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/digest.h" +#include <zix/digest.h> #include <assert.h> #include <stdint.h> diff --git a/src/errno_status.c b/src/errno_status.c index 887158f..ed09b24 100644 --- a/src/errno_status.c +++ b/src/errno_status.c @@ -3,7 +3,7 @@ #include "errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #include <errno.h> #include <stddef.h> @@ -41,7 +41,7 @@ zix_errno_status(const int e) {0, ZIX_STATUS_ERROR}, // Fallback mapping }; - static const size_t n_mappings = sizeof(map) / sizeof(Mapping) - 1U; + static const size_t n_mappings = (sizeof(map) / sizeof(Mapping)) - 1U; // Find the index of the matching mapping (or leave it at the fallback entry) size_t m = 0; diff --git a/src/errno_status.h b/src/errno_status.h index 2f81e4e..969d0dc 100644 --- a/src/errno_status.h +++ b/src/errno_status.h @@ -4,17 +4,15 @@ #ifndef ZIX_ERRNO_STATUS_H #define ZIX_ERRNO_STATUS_H -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/attributes.h> +#include <zix/status.h> /// Return an errno value converted to a status code -ZIX_CONST_FUNC -ZixStatus +ZIX_CONST_FUNC ZixStatus zix_errno_status(int e); /// Return success if `r` is non-zero, or `errno` as a status code otherwise -ZIX_PURE_FUNC -ZixStatus +ZIX_PURE_FUNC ZixStatus zix_errno_status_if(int r); #endif // ZIX_ERRNO_STATUS_H diff --git a/src/filesystem.c b/src/filesystem.c index 61487e9..a142e32 100644 --- a/src/filesystem.c +++ b/src/filesystem.c @@ -1,13 +1,13 @@ // Copyright 2007-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "path_iter.h" #include "system.h" -#include "zix/allocator.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/status.h> #ifdef _WIN32 # include <direct.h> @@ -18,6 +18,7 @@ #include <fcntl.h> #include <sys/stat.h> +#include <sys/types.h> #include <errno.h> #include <stdbool.h> @@ -1,7 +1,10 @@ // Copyright 2011-2021 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/hash.h" +#include <zix/hash.h> + +#include <zix/allocator.h> +#include <zix/status.h> #include <assert.h> #include <stdbool.h> @@ -327,7 +330,7 @@ zix_hash_insert_at(ZixHash* const hash, entry->value = record; // Update size and rehash if we exceeded the maximum load - const size_t max_load = hash->n_entries / 2U + hash->n_entries / 8U; + const size_t max_load = (hash->n_entries / 2U) + (hash->n_entries / 8U); const size_t new_count = hash->count + 1U; if (new_count >= max_load) { const ZixStatus st = grow(hash); @@ -1,12 +1,14 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/path.h" +#include <zix/path.h> #include "index_range.h" #include "path_iter.h" -#include "zix/string_view.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/string_view.h> #include <stdbool.h> #include <stddef.h> @@ -92,8 +94,7 @@ typedef struct { ZixIndexRange dir; } ZixRootSlices; -ZIX_PURE_FUNC -static ZixRootSlices +ZIX_PURE_FUNC static ZixRootSlices zix_path_root_slices(const char* const path) { // A root name not trailed by a separator has no root directory @@ -128,8 +129,7 @@ zix_string_ranges_equal(const char* const lhs, !strncmp(lhs + lhs_range.begin, rhs + rhs_range.begin, lhs_len)); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_root_path_range(const char* const path) { const ZixRootSlices root = zix_path_root_slices(path); @@ -140,8 +140,7 @@ zix_path_root_path_range(const char* const path) : zix_make_range(root.name.begin, root.name.end + dir_len); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_parent_path_range(const ZixStringView path) { if (!path.length) { @@ -178,8 +177,7 @@ zix_path_parent_path_range(const ZixStringView path) return zix_make_range(root.begin, root.begin + l + 1U - p); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_filename_range(const ZixStringView path) { if (!path.length) { @@ -201,8 +199,7 @@ zix_path_filename_range(const ZixStringView path) return zix_make_range(f, path.length); } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_stem_range(const ZixStringView path) { const ZixIndexRange name = zix_path_filename_range(path); @@ -221,8 +218,7 @@ zix_path_stem_range(const ZixStringView path) return zix_is_empty_range(stem) ? name : stem; } -ZIX_PURE_FUNC -static ZixIndexRange +ZIX_PURE_FUNC static ZixIndexRange zix_path_extension_range(const ZixStringView path) { const ZixIndexRange stem = zix_path_stem_range(path); @@ -701,7 +697,7 @@ zix_path_is_absolute(const char* const path) { #ifdef _WIN32 const ZixRootSlices root = zix_path_root_slices(path); - return (!zix_is_empty_range(root.name) && + return (path && !zix_is_empty_range(root.name) && (!zix_is_empty_range(root.dir) || (is_dir_sep(path[0]) && is_dir_sep(path[1])))); diff --git a/src/path_iter.h b/src/path_iter.h index b3b23e8..43eb8c9 100644 --- a/src/path_iter.h +++ b/src/path_iter.h @@ -6,7 +6,7 @@ #include "index_range.h" -#include "zix/attributes.h" +#include <zix/attributes.h> typedef enum { ZIX_PATH_ROOT_NAME, @@ -20,12 +20,10 @@ typedef struct { ZixPathIterState state; } ZixPathIter; -ZIX_PURE_FUNC -ZixPathIter +ZIX_PURE_FUNC ZixPathIter zix_path_begin(const char* ZIX_NULLABLE path); -ZIX_PURE_FUNC -ZixPathIter +ZIX_PURE_FUNC ZixPathIter zix_path_next(const char* ZIX_NONNULL path, ZixPathIter iter); #endif // ZIX_PATH_ITER_H diff --git a/src/posix/environment_posix.c b/src/posix/environment_posix.c new file mode 100644 index 0000000..8447753 --- /dev/null +++ b/src/posix/environment_posix.c @@ -0,0 +1,134 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include <zix/environment.h> + +#include <zix/allocator.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +static bool +is_path_delim(const char c) +{ + return c == '/' || c == ':' || c == '\0'; +} + +static bool +is_var_name_char(const char c) +{ + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || (c == '_'); +} + +// Append suffix to dst, update dst_len, and return the realloc'd result +static char* +append_str(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t suffix_len, + const char* const suffix) +{ + const size_t out_len = *dst_len + suffix_len; + char* const out = (char*)zix_realloc(allocator, dst, out_len + 1U); + if (!out) { + zix_free(allocator, dst); + return NULL; + } + + memcpy(out + *dst_len, suffix, suffix_len); + out[(*dst_len += suffix_len)] = '\0'; + return out; +} + +// Append the value of an environment variable to dst if it's set +static char* +append_var(ZixAllocator* const allocator, + size_t* const dst_len, + char* const dst, + const size_t ref_len, + const char* const ref) +{ + // Get value from environment + const char* const val = getenv(ref + 1U); // NOLINT(concurrency-mt-unsafe) + if (val) { + return append_str(allocator, dst_len, dst, strlen(val), val); + } + + // No value found, append variable reference as-is + return append_str(allocator, dst_len, dst, ref_len, ref); +} + +// Copy a variable reference string like $NAME to a null-terminated string +static char* +set_ref(ZixAllocator* const allocator, + char** const buffer, + const size_t ref_len, + const char* const ref) +{ + char* const out = (char*)zix_realloc(allocator, *buffer, ref_len + 1U); + + if (out) { + memcpy(out, ref, ref_len); + out[ref_len] = '\0'; + } else { + zix_free(allocator, *buffer); + } + + return out; +} + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + char* ref = NULL; // Reference string like $NAME + char* out = NULL; // Expanded result + size_t len = 0U; // Length of expanded result + + size_t start = 0U; // Start of current chunk to copy + for (size_t s = 0U; string[s];) { + const char c = string[s]; + if (c == '$' && is_var_name_char(string[s + 1U])) { + // Hit $ (variable reference like $VAR_NAME) + for (size_t t = 1U;; ++t) { + if (!is_var_name_char(string[s + t])) { + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(ref = set_ref(allocator, &ref, t, string + s)) || + !(out = append_var(allocator, &len, out, t, ref))) { + zix_free(allocator, ref); + zix_free(allocator, out); + return NULL; + } + start = s = t; + break; + } + } + } else if (c == '~' && is_path_delim(string[s + 1U])) { + // Hit ~ before delimiter or end of string (home directory reference) + const char* const prefix = string + start; + const size_t prefix_len = s - start; + if ((prefix_len && + !(out = append_str(allocator, &len, out, prefix_len, prefix))) || + !(out = append_var(allocator, &len, out, 5U, "$HOME"))) { + zix_free(allocator, ref); + return NULL; + } + start = ++s; + } else { + ++s; + } + } + + if (string[start]) { + const char* const tail = string + start; + const size_t tail_len = strlen(tail); + out = append_str(allocator, &len, out, tail_len, tail); + } + + zix_free(allocator, ref); + return out; +} diff --git a/src/posix/filesystem_posix.c b/src/posix/filesystem_posix.c index be13995..ea091e0 100644 --- a/src/posix/filesystem_posix.c +++ b/src/posix/filesystem_posix.c @@ -1,15 +1,15 @@ // Copyright 2007-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "../errno_status.h" #include "../system.h" #include "../zix_config.h" -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #if USE_FLOCK && USE_FILENO # include <sys/file.h> @@ -300,11 +300,9 @@ zix_remove(const char* const path) } void -zix_dir_for_each(const char* const path, - void* const data, - void (*const f)(const char* path, - const char* name, - void* data)) +zix_dir_for_each(const char* const path, + void* const data, + const ZixDirEntryVisitFunc f) { DIR* dir = opendir(path); if (dir) { @@ -343,6 +341,9 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) } zix_free(allocator, buffer); + +#else + (void)allocator; #endif return NULL; @@ -351,7 +352,7 @@ zix_canonical_path(ZixAllocator* const allocator, const char* const path) ZixStatus zix_file_lock(FILE* const file, const ZixFileLockMode mode) { -#if !defined(__EMSCRIPTEN__) && USE_FLOCK && USE_FILENO +#if USE_FLOCK && USE_FILENO return zix_posix_status( flock(fileno(file), (mode == ZIX_FILE_LOCK_BLOCK) ? LOCK_EX : (LOCK_EX | LOCK_NB))); @@ -366,7 +367,7 @@ zix_file_lock(FILE* const file, const ZixFileLockMode mode) ZixStatus zix_file_unlock(FILE* const file, const ZixFileLockMode mode) { -#if !defined(__EMSCRIPTEN__) && USE_FLOCK && USE_FILENO +#if USE_FLOCK && USE_FILENO return zix_posix_status( flock(fileno(file), (mode == ZIX_FILE_LOCK_BLOCK) ? LOCK_UN : (LOCK_UN | LOCK_NB))); @@ -378,8 +379,7 @@ zix_file_unlock(FILE* const file, const ZixFileLockMode mode) #endif } -ZIX_CONST_FUNC -static ZixFileType +ZIX_CONST_FUNC static ZixFileType stat_file_type(const struct stat* sb) { typedef struct { @@ -452,6 +452,7 @@ zix_current_path(ZixAllocator* const allocator) return current; #else + (void)allocator; return NULL; #endif diff --git a/src/posix/sem_posix.c b/src/posix/sem_posix.c index 452830d..5b14e37 100644 --- a/src/posix/sem_posix.c +++ b/src/posix/sem_posix.c @@ -1,12 +1,12 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> #include "../errno_status.h" #include "../zix_config.h" -#include "zix/status.h" +#include <zix/status.h> #include <semaphore.h> @@ -61,6 +61,9 @@ zix_sem_timed_wait(ZixSem* sem, { #if !USE_CLOCK_GETTIME || !USE_SEM_TIMEDWAIT + (void)sem; + (void)seconds; + (void)nanoseconds; return ZIX_STATUS_NOT_SUPPORTED; #else diff --git a/src/posix/thread_posix.c b/src/posix/thread_posix.c index 2073448..468fe38 100644 --- a/src/posix/thread_posix.c +++ b/src/posix/thread_posix.c @@ -1,11 +1,11 @@ // Copyright 2012-2020 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/thread.h" +#include <zix/thread.h> #include "../errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #include <pthread.h> @@ -1,15 +1,15 @@ -// Copyright 2011-2022 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/ring.h" +#include <zix/ring.h> #include "errno_status.h" #include "zix_config.h" -#include "zix/allocator.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/status.h> -#if defined(_WIN32) +#if USE_VIRTUALLOCK # include <windows.h> #elif USE_MLOCK # include <sys/mman.h> @@ -108,7 +108,7 @@ zix_ring_free(ZixRing* const ring) ZixStatus zix_ring_mlock(ZixRing* const ring) { -#if defined(_WIN32) +#if USE_VIRTUALLOCK return (VirtualLock(ring, sizeof(ZixRing)) && VirtualLock(ring->buf, ring->size)) ? ZIX_STATUS_SUCCESS @@ -119,6 +119,7 @@ zix_ring_mlock(ZixRing* const ring) mlock(ring->buf, ring->size)); #else + (void)ring; return ZIX_STATUS_NOT_SUPPORTED; #endif } @@ -190,7 +191,7 @@ peek_internal(const ZixRing* const ring, } else { const uint32_t first_size = ring->size - r; memcpy(dst, &ring->buf[r], first_size); - memcpy((char*)dst + first_size, &ring->buf[0], size - first_size); + memcpy((char*)dst + first_size, &ring->buf[0], (size_t)size - first_size); } return size; diff --git a/src/status.c b/src/status.c index e3d8b99..52057b8 100644 --- a/src/status.c +++ b/src/status.c @@ -1,7 +1,7 @@ // Copyright 2014-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/status.h" +#include <zix/status.h> const char* zix_strerror(const ZixStatus status) diff --git a/src/string_view.c b/src/string_view.c index 192f918..b02e537 100644 --- a/src/string_view.c +++ b/src/string_view.c @@ -1,8 +1,9 @@ // Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/string_view.h" -#include "zix/allocator.h" +#include <zix/string_view.h> + +#include <zix/allocator.h> #include <stdbool.h> #include <string.h> diff --git a/src/system.c b/src/system.c index eab568b..f672d76 100644 --- a/src/system.c +++ b/src/system.c @@ -5,7 +5,7 @@ #include "errno_status.h" -#include "zix/status.h" +#include <zix/status.h> #ifdef _WIN32 # include <io.h> diff --git a/src/system.h b/src/system.h index ca55161..45a0490 100644 --- a/src/system.h +++ b/src/system.h @@ -4,7 +4,7 @@ #ifndef ZIX_SYSTEM_H #define ZIX_SYSTEM_H -#include "zix/status.h" +#include <zix/status.h> #include <stdint.h> #include <sys/types.h> @@ -1,9 +1,11 @@ // Copyright 2011-2020 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/tree.h" +#include <zix/tree.h> -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> #include <assert.h> @@ -31,13 +33,12 @@ struct ZixTreeNodeImpl { #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) (((a) > (b)) ? (a) : (b)) -// Uncomment these for debugging features -// #define ZIX_TREE_VERIFY 1 -// #define ZIX_TREE_HYPER_VERIFY 1 - -#if defined(ZIX_TREE_VERIFY) || defined(ZIX_TREE_HYPER_VERIFY) -# include "tree_debug.h" -# define ASSERT_BALANCE(n) assert(verify_balance(n)) +#ifndef NDEBUG +# define ASSERT_BALANCE(n) \ + assert(!(((n)->balance < -2 || (n)->balance > 2) || \ + ((n)->balance < 0 && !(n)->left) || \ + ((n)->balance > 0 && !(n)->right) || \ + ((n)->balance != 0 && !(n)->left && !(n)->right))) #else # define ASSERT_BALANCE(n) #endif @@ -282,10 +283,6 @@ rotate_right_left(ZixTreeNode* p, int* height_change) static ZixTreeNode* zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) { -#ifdef ZIX_TREE_HYPER_VERIFY - const size_t old_height = height(node); -#endif - *height_change = 0; const bool is_root = !node->parent; @@ -294,6 +291,7 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) ZixTreeNode* replacement = node; if (node->balance == -2) { assert(node->left); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) if (node->left->balance == 1) { replacement = rotate_left_right(node, height_change); } else { @@ -301,6 +299,7 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) } } else if (node->balance == 2) { assert(node->right); + // NOLINTNEXTLINE(clang-analyzer-core.NullDereference) if (node->right->balance == -1) { replacement = rotate_right_left(node, height_change); } else { @@ -312,10 +311,6 @@ zix_tree_rebalance(ZixTree* t, ZixTreeNode* node, int* height_change) t->root = replacement; } -#ifdef ZIX_TREE_HYPER_VERIFY - assert(old_height + *height_change == height(replacement)); -#endif - return replacement; } @@ -395,12 +390,6 @@ zix_tree_insert(ZixTree* t, void* e, ZixTreeIter** ti) ++t->size; -#ifdef ZIX_TREE_VERIFY - if (!verify(t, t->root)) { - return ZIX_STATUS_ERROR; - } -#endif - return ZIX_STATUS_SUCCESS; } @@ -548,13 +537,6 @@ zix_tree_remove(ZixTree* t, ZixTreeIter* ti) zix_free(t->allocator, n); --t->size; - -#ifdef ZIX_TREE_VERIFY - if (!verify(t, t->root)) { - return ZIX_STATUS_ERROR; - } -#endif - return ZIX_STATUS_SUCCESS; } diff --git a/src/tree_debug.h b/src/tree_debug.h deleted file mode 100644 index b1a2d2e..0000000 --- a/src/tree_debug.h +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2011-2020 David Robillard <d@drobilla.net> -// SPDX-License-Identifier: ISC - -#ifndef ZIX_TREE_DEBUG_H -#define ZIX_TREE_DEBUG_H - -#include <inttypes.h> - -#ifdef ZIX_TREE_HYPER_VERIFY -static size_t -height(ZixTreeNode* n) -{ - if (!n) { - return 0; - } else { - return 1 + MAX(height(n->left), height(n->right)); - } -} -#endif - -#if defined(ZIX_TREE_VERIFY) || !defined(NDEBUG) -static bool -verify_balance(ZixTreeNode* n) -{ - if (!n) { - return true; - } - - if (n->balance < -2 || n->balance > 2) { - fprintf(stderr, - "Balance out of range : %ld (balance %d)\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance < 0 && !n->left) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no left child\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance > 0 && !n->right) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no right child\n", - (intptr_t)n->data, - n->balance); - return false; - } - - if (n->balance != 0 && !n->left && !n->right) { - fprintf(stderr, - "Bad balance : %ld (balance %d) has no children\n", - (intptr_t)n->data, - n->balance); - return false; - } - -# ifdef ZIX_TREE_HYPER_VERIFY - const intptr_t left_height = (intptr_t)height(n->left); - const intptr_t right_height = (intptr_t)height(n->right); - if (n->balance != right_height - left_height) { - fprintf(stderr, - "Bad balance at %ld: h_r (%" PRIdPTR ")" - "- l_h (%" PRIdPTR ") != %d\n", - (intptr_t)n->data, - right_height, - left_height, - n->balance); - assert(false); - return false; - } -# endif - - return true; -} -#endif - -#ifdef ZIX_TREE_VERIFY -static bool -verify(ZixTree* t, ZixTreeNode* n) -{ - if (!n) { - return true; - } - - if (n->parent) { - if ((n->parent->left != n) && (n->parent->right != n)) { - fprintf(stderr, "Corrupt child/parent pointers\n"); - return false; - } - } - - if (n->left) { - if (t->cmp(n->left->data, n->data, t->cmp_data) > 0) { - fprintf( - stderr, "Bad order: %" PRIdPTR " with left child\n", (intptr_t)n->data); - fprintf(stderr, "%p ? %p\n", (void*)n, (void*)n->left); - fprintf(stderr, - "%" PRIdPTR " ? %" PRIdPTR "\n", - (intptr_t)n->data, - (intptr_t)n->left->data); - return false; - } - - if (!verify(t, n->left)) { - return false; - } - } - - if (n->right) { - if (t->cmp(n->right->data, n->data, t->cmp_data) < 0) { - fprintf(stderr, - "Bad order: %" PRIdPTR " with right child\n", - (intptr_t)n->data); - return false; - } - - if (!verify(t, n->right)) { - return false; - } - } - - if (!verify_balance(n)) { - return false; - } - - if (n->balance <= -2 || n->balance >= 2) { - fprintf(stderr, "Imbalance: %p (balance %d)\n", (void*)n, n->balance); - return false; - } - - return true; -} -#endif - -#endif // ZIX_TREE_DEBUG_H diff --git a/src/win32/environment_win32.c b/src/win32/environment_win32.c new file mode 100644 index 0000000..ce5f00e --- /dev/null +++ b/src/win32/environment_win32.c @@ -0,0 +1,37 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "win32_util.h" + +#include <zix/environment.h> + +#include <windows.h> + +char* +zix_expand_environment_strings(ZixAllocator* const allocator, + const char* const string) +{ + ArgPathChar* const wstring = arg_path_new(allocator, string); + + const DWORD size = ExpandEnvironmentStrings(wstring, NULL, 0U); + if (!size) { + arg_path_free(allocator, wstring); + return NULL; + } + + TCHAR* const out = + (TCHAR*)zix_calloc(allocator, (size_t)size + 1U, sizeof(TCHAR)); + if (out) { + ExpandEnvironmentStrings(wstring, out, size + 1U); + } + + arg_path_free(allocator, wstring); + +#ifdef UNICODE + char* const result = zix_wchar_to_utf8(allocator, out); + zix_free(allocator, out); + return result; +#else + return out; +#endif +} diff --git a/src/win32/filesystem_win32.c b/src/win32/filesystem_win32.c index 5dc36a8..fa40cde 100644 --- a/src/win32/filesystem_win32.c +++ b/src/win32/filesystem_win32.c @@ -1,21 +1,22 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/bump_allocator.h" -#include "zix/filesystem.h" +#include <zix/filesystem.h> #include "../errno_status.h" #include "../zix_config.h" +#include "win32_util.h" -#include "zix/allocator.h" -#include "zix/path.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/path.h> +#include <zix/status.h> #include <direct.h> #include <fcntl.h> #include <io.h> #include <limits.h> #include <sys/stat.h> +#include <tchar.h> #include <windows.h> #include <errno.h> @@ -25,6 +26,34 @@ #include <stdlib.h> #include <string.h> +#ifdef UNICODE + +static char* +path_result(ZixAllocator* const allocator, wchar_t* const path) +{ + if (!path) { + return NULL; + } + + static const wchar_t* const long_prefix = L"\\\\?\\"; + + const size_t p = !wcsncmp(path, long_prefix, 4U) ? 4U : 0U; + char* const result = zix_wchar_to_utf8(allocator, path + p); + zix_free(allocator, path); + return result; +} + +#else // !defined(UNICODE) + +static char* +path_result(ZixAllocator* const allocator, char* const path) +{ + (void)allocator; + return path; +} + +#endif + static inline ZixStatus zix_winerror_status(const DWORD e) { @@ -67,8 +96,15 @@ zix_copy_file(ZixAllocator* const allocator, { (void)allocator; - return zix_windows_status( - CopyFile(src, dst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING))); + ArgPathChar* const wsrc = arg_path_new(allocator, src); + ArgPathChar* const wdst = arg_path_new(allocator, dst); + + const BOOL ret = + CopyFile(wsrc, wdst, !(options & ZIX_COPY_OPTION_OVERWRITE_EXISTING)); + + arg_path_free(allocator, wdst); + arg_path_free(allocator, wsrc); + return zix_windows_status(ret); } /// Linear Congruential Generator for making random 32-bit integers @@ -111,7 +147,7 @@ zix_create_temporary_directory(ZixAllocator* const allocator, suffix[i] = chars[seed % n_chars]; } - if (!_mkdir(result)) { + if (!zix_create_directory(result)) { return result; } } @@ -123,22 +159,44 @@ zix_create_temporary_directory(ZixAllocator* const allocator, ZixStatus zix_remove(const char* const path) { - return ((zix_file_type(path) == ZIX_FILE_TYPE_DIRECTORY) - ? zix_windows_status(RemoveDirectory(path)) - : remove(path) ? zix_errno_status(errno) - : ZIX_STATUS_SUCCESS); + ArgPathChar* const wpath = arg_path_new(NULL, path); + const DWORD attrs = GetFileAttributes(wpath); + + const BOOL success = + ((attrs & FILE_ATTRIBUTE_DIRECTORY) ? RemoveDirectory(wpath) + : DeleteFile(wpath)); + + arg_path_free(NULL, wpath); + return zix_windows_status(success); } void -zix_dir_for_each(const char* const path, - void* const data, - void (*const f)(const char* path, - const char* name, - void* data)) +zix_dir_for_each(const char* const path, + void* const data, + const ZixDirEntryVisitFunc f) { + static const TCHAR* const dot = TEXT("."); + static const TCHAR* const dotdot = TEXT(".."); + +#ifdef UNICODE + const int path_size = MultiByteToWideChar(CP_UTF8, 0, path, -1, NULL, 0); + if (path_size < 1) { + return; + } + + const size_t path_len = (size_t)path_size - 1U; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 4U, sizeof(TCHAR)); + if (!pat) { + return; + } + + MultiByteToWideChar(CP_UTF8, 0, path, -1, pat, path_size); +#else const size_t path_len = strlen(path); - char pat[MAX_PATH + 2U]; + TCHAR* const pat = (TCHAR*)zix_calloc(NULL, path_len + 2U, sizeof(TCHAR)); memcpy(pat, path, path_len + 1U); +#endif + pat[path_len] = '\\'; pat[path_len + 1U] = '*'; pat[path_len + 2U] = '\0'; @@ -147,8 +205,14 @@ zix_dir_for_each(const char* const path, HANDLE fh = FindFirstFile(pat, &fd); if (fh != INVALID_HANDLE_VALUE) { do { - if (!!strcmp(fd.cFileName, ".") && !!strcmp(fd.cFileName, "..")) { + if (!!_tcscmp(fd.cFileName, dot) && !!_tcscmp(fd.cFileName, dotdot)) { +#ifdef UNICODE + char* const name = zix_wchar_to_utf8(NULL, fd.cFileName); + f(path, name, data); + zix_free(NULL, name); +#else f(path, fd.cFileName, data); +#endif } } while (FindNextFile(fh, &fd)); } @@ -158,6 +222,12 @@ zix_dir_for_each(const char* const path, ZixStatus zix_file_lock(FILE* const file, const ZixFileLockMode mode) { +#ifdef __clang__ + (void)file; + (void)mode; + return ZIX_STATUS_NOT_SUPPORTED; +#else + HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; @@ -167,58 +237,112 @@ zix_file_lock(FILE* const file, const ZixFileLockMode mode) return zix_windows_status( LockFileEx(handle, flags, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } ZixStatus zix_file_unlock(FILE* const file, const ZixFileLockMode mode) { (void)mode; +#ifdef __clang__ + (void)file; + return ZIX_STATUS_NOT_SUPPORTED; +#else HANDLE handle = (HANDLE)_get_osfhandle(fileno(file)); OVERLAPPED overlapped = {0}; return zix_windows_status( UnlockFileEx(handle, 0, UINT32_MAX, UINT32_MAX, &overlapped)); +#endif } +#if USE_GETFINALPATHNAMEBYHANDLE && USE_CREATEFILE2 + +static HANDLE +open_attribute_handle(const char* const path) +{ + wchar_t* const wpath = zix_utf8_to_wchar(NULL, path); + + CREATEFILE2_EXTENDED_PARAMETERS params = { + sizeof(CREATEFILE2_EXTENDED_PARAMETERS), + 0U, + FILE_FLAG_BACKUP_SEMANTICS, + 0U, + NULL, + NULL}; + + const HANDLE handle = + CreateFile2(wpath, FILE_READ_ATTRIBUTES, 0U, OPEN_EXISTING, ¶ms); + + zix_free(NULL, wpath); + return handle; +} + +#elif USE_GETFINALPATHNAMEBYHANDLE + +static HANDLE +open_attribute_handle(const char* const path) +{ + ArgPathChar* const wpath = arg_path_new(NULL, path); + + const HANDLE handle = CreateFile(wpath, + FILE_READ_ATTRIBUTES, + 0U, + NULL, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + NULL); + + arg_path_free(NULL, wpath); + return handle; +} + +#endif + char* zix_canonical_path(ZixAllocator* const allocator, const char* const path) { - char full[MAX_PATH] = {0}; - if (!path || !GetFullPathName(path, MAX_PATH, full, NULL)) { + if (!path) { return NULL; } - const HANDLE h = - CreateFile(full, - FILE_READ_ATTRIBUTES, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - NULL, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - NULL); - - const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; - const DWORD final_size = GetFinalPathNameByHandle(h, NULL, 0U, flags); - if (!final_size) { - CloseHandle(h); - return NULL; +#if USE_GETFINALPATHNAMEBYHANDLE // Vista+ + + const HANDLE h = open_attribute_handle(path); + TCHAR* final = NULL; + if (h != INVALID_HANDLE_VALUE) { + const DWORD flags = FILE_NAME_NORMALIZED | VOLUME_NAME_DOS; + const DWORD length = GetFinalPathNameByHandle(h, NULL, 0U, flags); + if (length) { + final = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); + if (final) { + GetFinalPathNameByHandle(h, final, length + 1U, flags); + } + } } - char* const final = (char*)zix_calloc(allocator, final_size + 1U, 1U); - if (!final || !GetFinalPathNameByHandle(h, final, final_size + 1U, flags)) { - zix_free(allocator, final); - CloseHandle(h); - return NULL; + CloseHandle(h); + return path_result(allocator, final); + +#else // Fall back to "full path iff it exists" for older Windows + + ArgPathChar* const wpath = arg_path_new(allocator, path); + TCHAR* full = NULL; + if (GetFileAttributes(wpath) != INVALID_FILE_ATTRIBUTES) { + const DWORD length = GetFullPathName(wpath, 0U, NULL, NULL); + if (length) { + full = (TCHAR*)zix_calloc(allocator, (size_t)length + 1U, sizeof(TCHAR)); + if (full) { + GetFullPathName(wpath, length + 1U, full, NULL); + } + } } - if (final_size > 4U && !strncmp(final, "\\\\?\\", 4)) { - memmove(final, final + 4U, final_size - 4U); - final[final_size - 4U] = '\0'; - } + arg_path_free(allocator, wpath); + return path_result(allocator, full); - CloseHandle(h); - return final; +#endif } static ZixFileType @@ -246,30 +370,44 @@ attrs_file_type(const DWORD attrs) ZixFileType zix_file_type(const char* const path) { - const ZixFileType type = attrs_file_type(GetFileAttributes(path)); + const ZixFileType type = zix_symlink_type(path); if (type != ZIX_FILE_TYPE_SYMLINK) { return type; } // Resolve symlink to find the canonical type - char buf[MAX_PATH]; - ZixBumpAllocator allocator = zix_bump_allocator(sizeof(buf), buf); - char* const canonical = zix_canonical_path(&allocator.base, path); - return zix_file_type(canonical); + char* const canonical = zix_canonical_path(NULL, path); + const ZixFileType real_type = + canonical ? zix_symlink_type(canonical) : ZIX_FILE_TYPE_NONE; + + zix_free(NULL, canonical); + return real_type; } ZixFileType zix_symlink_type(const char* const path) { - return attrs_file_type(GetFileAttributes(path)); + ArgPathChar* const wpath = arg_path_new(NULL, path); + if (!wpath) { + return ZIX_FILE_TYPE_NONE; + } + + const ZixFileType type = attrs_file_type(GetFileAttributes(wpath)); + arg_path_free(NULL, wpath); + return type; } ZixStatus zix_create_directory(const char* const dir_path) { - return (!dir_path[0]) ? ZIX_STATUS_BAD_ARG - : _mkdir(dir_path) ? zix_errno_status(errno) - : ZIX_STATUS_SUCCESS; + if (!dir_path[0]) { + return ZIX_STATUS_BAD_ARG; + } + + ArgPathChar* const wpath = arg_path_new(NULL, dir_path); + const ZixStatus st = zix_windows_status(CreateDirectory(wpath, NULL)); + arg_path_free(NULL, wpath); + return st; } ZixStatus @@ -287,7 +425,15 @@ zix_create_symlink(const char* const target_path, const char* const link_path) #if USE_CREATESYMBOLICLINK static const DWORD flags = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -303,7 +449,15 @@ zix_create_directory_symlink(const char* const target_path, static const DWORD flags = SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; - return zix_windows_status(CreateSymbolicLink(link_path, target_path, flags)); + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateSymbolicLink(wlink, wtarget, flags); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + #else (void)target_path; (void)link_path; @@ -314,31 +468,45 @@ zix_create_directory_symlink(const char* const target_path, ZixStatus zix_create_hard_link(const char* const target_path, const char* const link_path) { - return zix_windows_status(CreateHardLink(link_path, target_path, NULL)); +#if USE_CREATEHARDLINK + ArgPathChar* const wtarget = arg_path_new(NULL, target_path); + ArgPathChar* const wlink = arg_path_new(NULL, link_path); + + const BOOL success = CreateHardLink(wlink, wtarget, NULL); + + arg_path_free(NULL, wlink); + arg_path_free(NULL, wtarget); + return zix_windows_status(success); + +#else + (void)target_path; + (void)link_path; + return ZIX_STATUS_NOT_SUPPORTED; +#endif } char* zix_temp_directory_path(ZixAllocator* const allocator) { - const DWORD size = GetTempPath(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetTempPath(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetTempPath(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } char* zix_current_path(ZixAllocator* const allocator) { - const DWORD size = GetCurrentDirectory(0U, NULL); - char* const buf = (char*)zix_calloc(allocator, size, 1); + const DWORD size = GetCurrentDirectory(0U, NULL); + TCHAR* const buf = (TCHAR*)zix_calloc(allocator, size, sizeof(TCHAR)); if (buf && (GetCurrentDirectory(size, buf) != size - 1U)) { zix_free(allocator, buf); return NULL; } - return buf; + return path_result(allocator, buf); } diff --git a/src/win32/sem_win32.c b/src/win32/sem_win32.c index 979390e..d28f39f 100644 --- a/src/win32/sem_win32.c +++ b/src/win32/sem_win32.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/sem.h" +#include <zix/sem.h> -#include "zix/status.h" +#include <zix/status.h> #include <windows.h> diff --git a/src/win32/thread_win32.c b/src/win32/thread_win32.c index e4411b1..dbfe026 100644 --- a/src/win32/thread_win32.c +++ b/src/win32/thread_win32.c @@ -1,9 +1,9 @@ // Copyright 2012-2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/thread.h" +#include <zix/thread.h> -#include "zix/status.h" +#include <zix/status.h> #include <windows.h> diff --git a/src/win32/win32_util.c b/src/win32/win32_util.c new file mode 100644 index 0000000..959867c --- /dev/null +++ b/src/win32/win32_util.c @@ -0,0 +1,73 @@ +// Copyright 2019-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#include "win32_util.h" + +#include <limits.h> +#include <windows.h> + +#ifdef UNICODE + +ArgPathChar* +arg_path_new(ZixAllocator* const allocator, const char* const path) +{ + return zix_utf8_to_wchar(allocator, path); +} + +void +arg_path_free(ZixAllocator* const allocator, ArgPathChar* const path) +{ + zix_free(allocator, path); +} + +#else // !defined(UNICODE) + +ArgPathChar* +arg_path_new(ZixAllocator* const allocator, const char* const path) +{ + (void)allocator; + return path; +} + +void +arg_path_free(ZixAllocator* const allocator, ArgPathChar* const path) +{ + (void)allocator; + (void)path; +} + +#endif + +wchar_t* +zix_utf8_to_wchar(ZixAllocator* const allocator, const char* const utf8) +{ + const int rc = utf8 ? MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0) : 0; + if (rc <= 0 || rc == INT_MAX) { + return NULL; + } + + wchar_t* const result = + (wchar_t*)zix_calloc(allocator, (size_t)rc, sizeof(wchar_t)); + if (result) { + MultiByteToWideChar(CP_UTF8, 0, utf8, -1, result, rc); + } + + return result; +} + +char* +zix_wchar_to_utf8(ZixAllocator* const allocator, const wchar_t* const wstr) +{ + const int rc = + wstr ? WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL) : 0; + if (rc <= 0 || rc == INT_MAX) { + return NULL; + } + + char* const result = (char*)zix_calloc(allocator, (size_t)rc, sizeof(char)); + if (result) { + WideCharToMultiByte(CP_UTF8, 0, wstr, -1, result, rc, NULL, NULL); + } + + return result; +} diff --git a/src/win32/win32_util.h b/src/win32/win32_util.h new file mode 100644 index 0000000..3ae2e2b --- /dev/null +++ b/src/win32/win32_util.h @@ -0,0 +1,31 @@ +// Copyright 2019-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#ifndef ZIX_WIN32_UTIL_H +#define ZIX_WIN32_UTIL_H + +#include <zix/allocator.h> + +#ifdef UNICODE +typedef wchar_t ArgPathChar; +#else +typedef const char ArgPathChar; +#endif + +/// Copy and convert a path argument if necessary +ArgPathChar* +arg_path_new(ZixAllocator* allocator, const char* path); + +/// Free a path from arg_path_new() if necessary +void +arg_path_free(ZixAllocator* allocator, ArgPathChar* path); + +/// Convert from (user) UTF-8 to (Windows) UTF-16 +wchar_t* +zix_utf8_to_wchar(ZixAllocator* allocator, const char* utf8); + +/// Convert from (Windows) UTF-16 to (user) UTF-8 +char* +zix_wchar_to_utf8(ZixAllocator* allocator, const wchar_t* wstr); + +#endif // ZIX_WIN32_UTIL_H diff --git a/src/zix_config.h b/src/zix_config.h index 0aab586..460b712 100644 --- a/src/zix_config.h +++ b/src/zix_config.h @@ -1,4 +1,4 @@ -// Copyright 2021-2023 David Robillard <d@drobilla.net> +// Copyright 2021-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC /* @@ -55,6 +55,13 @@ # define ZIX_POSIX_VERSION 0 # endif +// Define ZIX_WINAPI_UWP to 1 (if this is a sandboxed UWP app) or 0 +# if defined(WINAPI_FAMILY) && WINAPI_FAMILY < 10 +# define ZIX_WINAPI_UWP 1 +# else +# define ZIX_WINAPI_UWP 0 +# endif + // POSIX.1-2001: clock_gettime() # ifndef HAVE_CLOCK_GETTIME # if ZIX_POSIX_VERSION >= 200112L @@ -78,9 +85,24 @@ # endif # endif -// Windows: CreateSymbolicLink() +// Windows 8 (Desktop, UWP): CreateFile2() +# ifndef HAVE_CREATEFILE2 +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0602 && !defined(__MINGW32__) +# define HAVE_CREATEFILE2 1 +# endif +# endif + +// Windows XP (Desktop): CreateHardLink() +# ifndef HAVE_CREATEHARDLINK +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 && !ZIX_WINAPI_UWP +# define HAVE_CREATEHARDLINK 1 +# endif +# endif + +// Windows Vista (Desktop): CreateSymbolicLink() # ifndef HAVE_CREATESYMBOLICLINK -# if defined(_MSC_VER) && _MSC_VER >= 1910 +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 && \ + !defined(__MINGW32__) && !ZIX_WINAPI_UWP # define HAVE_CREATESYMBOLICLINK 1 # endif # endif @@ -94,11 +116,18 @@ // Classic UNIX: flock() # ifndef HAVE_FLOCK -# if defined(__APPLE__) || defined(__unix__) +# if (defined(__APPLE__) || defined(__unix__)) && !defined(__EMSCRIPTEN__) # define HAVE_FLOCK 1 # endif # endif +// Windows Vista (Desktop, UWP): GetFinalPathNameByHandle() +# ifndef HAVE_GETFINALPATHNAMEBYHANDLE +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0600 +# define HAVE_GETFINALPATHNAMEBYHANDLE 1 +# endif +# endif + // POSIX.1-2001: mlock() # ifndef HAVE_MLOCK # if ZIX_POSIX_VERSION >= 200112L @@ -148,6 +177,13 @@ # endif # endif +// Windows XP (Desktop): VirtualLock +# ifndef HAVE_VIRTUALLOCK +# if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x0501 && !ZIX_WINAPI_UWP +# define HAVE_VIRTUALLOCK 1 +# endif +# endif + #endif // !defined(ZIX_NO_DEFAULT_CONFIG) /* @@ -176,6 +212,18 @@ # define USE_COPY_FILE_RANGE 0 #endif +#if defined(HAVE_CREATEFILE2) && HAVE_CREATEFILE2 +# define USE_CREATEFILE2 1 +#else +# define USE_CREATEFILE2 0 +#endif + +#if defined(HAVE_CREATEHARDLINK) && HAVE_CREATEHARDLINK +# define USE_CREATEHARDLINK 1 +#else +# define USE_CREATEHARDLINK 0 +#endif + #if defined(HAVE_CREATESYMBOLICLINK) && HAVE_CREATESYMBOLICLINK # define USE_CREATESYMBOLICLINK 1 #else @@ -194,6 +242,12 @@ # define USE_FLOCK 0 #endif +#if defined(HAVE_GETFINALPATHNAMEBYHANDLE) && HAVE_GETFINALPATHNAMEBYHANDLE +# define USE_GETFINALPATHNAMEBYHANDLE 1 +#else +# define USE_GETFINALPATHNAMEBYHANDLE 0 +#endif + #if defined(HAVE_MLOCK) && HAVE_MLOCK # define USE_MLOCK 1 #else @@ -236,4 +290,10 @@ # define USE_SYSCONF 0 #endif +#if defined(HAVE_VIRTUALLOCK) && HAVE_VIRTUALLOCK +# define USE_VIRTUALLOCK 1 +#else +# define USE_VIRTUALLOCK 0 +#endif + #endif // ZIX_CONFIG_H diff --git a/subprojects/sphinxygen.wrap b/subprojects/sphinxygen.wrap index 9707d1a..898ee52 100644 --- a/subprojects/sphinxygen.wrap +++ b/subprojects/sphinxygen.wrap @@ -1,14 +1,14 @@ -# Copyright 2022-2023 David Robillard <d@drobilla.net> +# Copyright 2022-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC [wrap-file] -directory = sphinxygen-1.0.4 -source_url = https://download.drobilla.net/sphinxygen-1.0.4.tar.gz -source_filename = sphinxygen-1.0.4.tar.gz -source_hash = 12fa9f18ed9fca608f272520072257ba61fd9eff25613f86d83d4fce14fc01f5 +directory = sphinxygen-1.0.10 +source_url = https://download.drobilla.net/sphinxygen-1.0.10.tar.gz +source_filename = sphinxygen-1.0.10.tar.gz +source_hash = 4b5eeb1ff47d43ee1ddae9327c2b49bab1e49363538c569bc544705284f3695d # [wrap-git] # url = https://gitlab.com/drobilla/sphinxygen.git # push-url = ssh://git@gitlab.com:drobilla/sphinxygen.git -# revision = v1.0.4 +# revision = v1.0.10 # depth = 1 diff --git a/test/.clang-tidy b/test/.clang-tidy index 2d684a6..401b1b0 100644 --- a/test/.clang-tidy +++ b/test/.clang-tidy @@ -1,16 +1,18 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > + -*-macro-to-enum, -*-magic-numbers, -android-cloexec-fopen, -bugprone-easily-swappable-parameters, + -bugprone-multi-level-implicit-pointer-conversion, -cert-err33-c, -clang-analyzer-security.insecureAPI.DeprecatedOrUnsafeBufferHandling, + -concurrency-mt-unsafe, -cppcoreguidelines-avoid-non-const-global-variables, -google-readability-casting, -llvm-header-guard, - -modernize-macro-to-enum, -performance-no-int-to-ptr, -readability-function-cognitive-complexity, InheritParentConfig: true diff --git a/test/cpp/.clang-tidy b/test/cpp/.clang-tidy index 9a3cd8c..1cbcc07 100644 --- a/test/cpp/.clang-tidy +++ b/test/cpp/.clang-tidy @@ -1,13 +1,19 @@ -# Copyright 2020-2022 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC Checks: > -*-avoid-c-arrays, -*-no-malloc, + -*-use-nullptr, -android-cloexec-fopen, + -bugprone-exception-escape, + -cppcoreguidelines-macro-usage, -cppcoreguidelines-owning-memory, -fuchsia-default-arguments-calls, -modernize-raw-string-literal, + -modernize-redundant-void-arg, -modernize-use-trailing-return-type, + -modernize-use-using, + -performance-enum-size, -readability-implicit-bool-conversion, InheritParentConfig: true diff --git a/test/cpp/test_headers_cpp.cpp b/test/cpp/test_headers_cpp.cpp index f8038c1..fe367ad 100644 --- a/test/cpp/test_headers_cpp.cpp +++ b/test/cpp/test_headers_cpp.cpp @@ -5,21 +5,22 @@ # define WIN32_LEAN_AND_MEAN #endif -#include "zix/allocator.h" // IWYU pragma: keep -#include "zix/attributes.h" // IWYU pragma: keep -#include "zix/btree.h" // IWYU pragma: keep -#include "zix/bump_allocator.h" // IWYU pragma: keep -#include "zix/digest.h" // IWYU pragma: keep -#include "zix/filesystem.h" // IWYU pragma: keep -#include "zix/hash.h" // IWYU pragma: keep -#include "zix/path.h" // IWYU pragma: keep -#include "zix/ring.h" // IWYU pragma: keep -#include "zix/sem.h" // IWYU pragma: keep -#include "zix/status.h" // IWYU pragma: keep -#include "zix/string_view.h" // IWYU pragma: keep -#include "zix/thread.h" // IWYU pragma: keep -#include "zix/tree.h" // IWYU pragma: keep -#include "zix/zix.h" // IWYU pragma: keep +#include <zix/allocator.h> // IWYU pragma: keep +#include <zix/attributes.h> // IWYU pragma: keep +#include <zix/btree.h> // IWYU pragma: keep +#include <zix/bump_allocator.h> // IWYU pragma: keep +#include <zix/digest.h> // IWYU pragma: keep +#include <zix/environment.h> // IWYU pragma: keep +#include <zix/filesystem.h> // IWYU pragma: keep +#include <zix/hash.h> // IWYU pragma: keep +#include <zix/path.h> // IWYU pragma: keep +#include <zix/ring.h> // IWYU pragma: keep +#include <zix/sem.h> // IWYU pragma: keep +#include <zix/status.h> // IWYU pragma: keep +#include <zix/string_view.h> // IWYU pragma: keep +#include <zix/thread.h> // IWYU pragma: keep +#include <zix/tree.h> // IWYU pragma: keep +#include <zix/zix.h> // IWYU pragma: keep #if defined(__GNUC__) __attribute__((const)) diff --git a/test/cpp/test_path_std.cpp b/test/cpp/test_path_std.cpp index cabf3da..b0850a9 100644 --- a/test/cpp/test_path_std.cpp +++ b/test/cpp/test_path_std.cpp @@ -10,15 +10,16 @@ #undef NDEBUG -#include "zix/path.h" -#include "zix/string_view.h" +#include <zix/path.h> +#include <zix/string_view.h> #include <cassert> #include <cstdlib> #include <filesystem> -#include <sstream> #include <string> +// IWYU pragma: no_include <version> + namespace { struct BinaryCase { @@ -483,8 +484,11 @@ run() } for (const auto& relatives : lexical_relatives) { - const Path l = relatives.lhs ? Path{relatives.lhs} : Path{}; - const Path r = relatives.rhs ? Path{relatives.rhs} : Path{}; + assert(relatives.lhs); + assert(relatives.rhs); + + const Path l = Path{relatives.lhs}; + const Path r = Path{relatives.rhs}; assert(match( l.lexically_relative(r), diff --git a/test/failing_allocator.c b/test/failing_allocator.c index 684a8ec..6cffc7b 100644 --- a/test/failing_allocator.c +++ b/test/failing_allocator.c @@ -1,10 +1,10 @@ -// Copyright 2021 David Robillard <d@drobilla.net> +// Copyright 2021-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #include "failing_allocator.h" -#include "zix/allocator.h" -#include "zix/attributes.h" +#include <zix/allocator.h> +#include <zix/attributes.h> #include <stdbool.h> #include <stddef.h> @@ -23,8 +23,7 @@ attempt(ZixFailingAllocator* const allocator) return true; } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_failing_malloc(ZixAllocator* const allocator, const size_t size) { ZixFailingAllocator* const state = (ZixFailingAllocator*)allocator; @@ -33,8 +32,7 @@ zix_failing_malloc(ZixAllocator* const allocator, const size_t size) return attempt(state) ? base->malloc(base, size) : NULL; } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_failing_calloc(ZixAllocator* const allocator, const size_t nmemb, const size_t size) @@ -66,8 +64,7 @@ zix_failing_free(ZixAllocator* const allocator, void* const ptr) base->free(base, ptr); } -ZIX_MALLOC_FUNC -static void* +ZIX_MALLOC_FUNC static void* zix_failing_aligned_alloc(ZixAllocator* const allocator, const size_t alignment, const size_t size) @@ -88,8 +85,7 @@ zix_failing_aligned_free(ZixAllocator* const allocator, void* const ptr) base->aligned_free(base, ptr); } -ZIX_CONST_FUNC -ZixFailingAllocator +ZIX_CONST_FUNC ZixFailingAllocator zix_failing_allocator(void) { ZixFailingAllocator failing_allocator = { @@ -107,3 +103,15 @@ zix_failing_allocator(void) return failing_allocator; } + +size_t +zix_failing_allocator_reset(ZixFailingAllocator* const allocator, + const size_t n_allowed) +{ + const size_t n_allocations = allocator->n_allocations; + + allocator->n_allocations = 0U; + allocator->n_remaining = n_allowed; + + return n_allocations; +} diff --git a/test/failing_allocator.h b/test/failing_allocator.h index 982874d..215f2b6 100644 --- a/test/failing_allocator.h +++ b/test/failing_allocator.h @@ -1,10 +1,10 @@ -// Copyright 2021 David Robillard <d@drobilla.net> +// Copyright 2021-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#ifndef ZIX_FAILING_ALLOCATOR_H -#define ZIX_FAILING_ALLOCATOR_H +#ifndef ZIX_TEST_FAILING_ALLOCATOR_H +#define ZIX_TEST_FAILING_ALLOCATOR_H -#include "zix/allocator.h" +#include <zix/allocator.h> #include <stddef.h> @@ -15,7 +15,12 @@ typedef struct { size_t n_remaining; ///< Number of remaining successful allocations } ZixFailingAllocator; +/// Return an allocator configured by default to succeed ZixFailingAllocator zix_failing_allocator(void); -#endif // ZIX_FAILING_ALLOCATOR_H +/// Reset an allocator to fail after some number of "allowed" allocations +size_t +zix_failing_allocator_reset(ZixFailingAllocator* allocator, size_t n_allowed); + +#endif // ZIX_TEST_FAILING_ALLOCATOR_H diff --git a/test/headers/test_headers.c b/test/headers/test_headers.c index a610600..f10d05f 100644 --- a/test/headers/test_headers.c +++ b/test/headers/test_headers.c @@ -1,21 +1,22 @@ // Copyright 2022 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC -#include "zix/allocator.h" // IWYU pragma: keep -#include "zix/attributes.h" // IWYU pragma: keep -#include "zix/btree.h" // IWYU pragma: keep -#include "zix/bump_allocator.h" // IWYU pragma: keep -#include "zix/digest.h" // IWYU pragma: keep -#include "zix/filesystem.h" // IWYU pragma: keep -#include "zix/hash.h" // IWYU pragma: keep -#include "zix/path.h" // IWYU pragma: keep -#include "zix/ring.h" // IWYU pragma: keep -#include "zix/sem.h" // IWYU pragma: keep -#include "zix/status.h" // IWYU pragma: keep -#include "zix/string_view.h" // IWYU pragma: keep -#include "zix/thread.h" // IWYU pragma: keep -#include "zix/tree.h" // IWYU pragma: keep -#include "zix/zix.h" // IWYU pragma: keep +#include <zix/allocator.h> // IWYU pragma: keep +#include <zix/attributes.h> // IWYU pragma: keep +#include <zix/btree.h> // IWYU pragma: keep +#include <zix/bump_allocator.h> // IWYU pragma: keep +#include <zix/digest.h> // IWYU pragma: keep +#include <zix/environment.h> // IWYU pragma: keep +#include <zix/filesystem.h> // IWYU pragma: keep +#include <zix/hash.h> // IWYU pragma: keep +#include <zix/path.h> // IWYU pragma: keep +#include <zix/ring.h> // IWYU pragma: keep +#include <zix/sem.h> // IWYU pragma: keep +#include <zix/status.h> // IWYU pragma: keep +#include <zix/string_view.h> // IWYU pragma: keep +#include <zix/thread.h> // IWYU pragma: keep +#include <zix/tree.h> // IWYU pragma: keep +#include <zix/zix.h> // IWYU pragma: keep #if defined(__GNUC__) __attribute__((const)) diff --git a/test/lint/meson.build b/test/lint/meson.build new file mode 100644 index 0000000..13966cf --- /dev/null +++ b/test/lint/meson.build @@ -0,0 +1,42 @@ +# Copyright 2024 David Robillard <d@drobilla.net> +# SPDX-License-Identifier: 0BSD OR ISC + +if not meson.is_subproject() + # Check release metadata + autoship = find_program('autoship', required: get_option('tests')) + if autoship.found() + test('autoship', autoship, args: ['test', zix_src_root], suite: 'data') + endif + + # Check code with cppcheck + cppcheck = find_program('cppcheck', required: false) + if cppcheck.found() + compdb_path = join_paths(zix_build_root, 'compile_commands.json') + cppcheck_args = [ + '--cppcheck-build-dir=' + meson.current_build_dir(), + '--enable=warning,style,performance,portability', + '--error-exitcode=1', + '--project=' + compdb_path, + '--suppress=constParameterCallback', + '--suppress=constParameterPointer', + '--suppress=normalCheckLevelMaxBranches', + '--suppress=unreadVariable', + '-q', + ] + test('cppcheck', cppcheck, args: cppcheck_args, suite: 'code') + endif +endif + +# Check licensing metadata +reuse = find_program('reuse', required: get_option('tests')) +if reuse.found() + reuse_args = ['--root', zix_src_root, 'lint'] + test('REUSE', reuse, args: reuse_args, suite: 'data') +endif + +# Check code formatting +clang_format = find_program('clang-format', required: false) +if clang_format.found() + clang_format_args = ['--Werror', '--dry-run'] + c_headers + sources + test('format', clang_format, args: clang_format_args, suite: 'code') +endif diff --git a/test/meson.build b/test/meson.build index 96fe455..0db182c 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,72 +1,34 @@ -# Copyright 2020-2024 David Robillard <d@drobilla.net> +# Copyright 2020-2025 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC -if not meson.is_subproject() and get_option('lint') - # Check release metadata - autoship = find_program('autoship', required: get_option('tests')) - if autoship.found() - test( - 'autoship', - autoship, - args: ['test', zix_src_root], - suite: 'data', - ) - endif +######## +# Lint # +######## - # Check licensing metadata - reuse = find_program('reuse', required: get_option('tests')) - if reuse.found() - test( - 'REUSE', - reuse, - args: ['--root', zix_src_root, 'lint'], - suite: 'data', - ) - endif +if get_option('lint') + subdir('lint') +endif - # Check code formatting - clang_format = find_program('clang-format', required: false) - if clang_format.found() - test( - 'format', - clang_format, - args: ['--Werror', '--dry-run'] + c_headers + sources, - suite: 'code', - ) - endif +############## +# Unit Tests # +############## - # Check code with cppcheck - if not meson.is_subproject() - cppcheck = find_program('cppcheck', required: false) - if cppcheck.found() - compdb_path = join_paths(zix_build_root, 'compile_commands.json') - test( - 'cppcheck', - cppcheck, - args: [ - '--check-level=exhaustive', - '--enable=warning,style,performance,portability', - '--error-exitcode=1', - '--project=' + compdb_path, - '--suppress=constParameterCallback', - '--suppress=constParameterPointer', - '--suppress=unreadVariable', - '-q', - ], - suite: 'code', - ) +# Set warning suppression flags specific to tests +test_suppressions = [] +if cc.get_id() in ['clang', 'emscripten'] + if warning_level == 'everything' + test_suppressions += ['-Wno-bad-function-cast'] + + if host_machine.system() == 'windows' + if cc.get_id() in ['clang', 'emscripten'] + test_suppressions += ['-Wno-format-nonliteral'] + endif endif endif -endif +elif cc.get_id() == 'gcc' + test_suppressions += ['-Wno-bad-function-cast'] -# Set warning suppression flags specific to tests -test_suppressions = [] -if host_machine.system() == 'windows' - if cc.get_id() in ['clang', 'emscripten'] - test_suppressions += [ - '-Wno-format-nonliteral', - ] - elif cc.get_id() == 'gcc' + if host_machine.system() == 'windows' test_suppressions += ['-Wno-format'] endif endif @@ -80,8 +42,9 @@ sequential_tests = { '': [], '_small': ['4'], }, - 'filesystem': {'': files('../README.md')}, 'digest': {'': []}, + 'environment': {'': []}, + 'filesystem': {'': files('../README.md')}, 'hash': {'': []}, 'path': {'': []}, 'status': {'': []}, @@ -128,6 +91,7 @@ foreach test, cases : sequential_tests sources, c_args: c_suppressions + program_c_args + test_suppressions, dependencies: [zix_dep], + implicit_include_directories: false, include_directories: include_dirs, link_args: program_link_args, ) @@ -147,6 +111,7 @@ if thread_dep.found() sources, c_args: c_suppressions + program_c_args + test_suppressions, dependencies: [zix_dep, thread_dep], + implicit_include_directories: false, include_directories: include_dirs, link_args: program_link_args, ) @@ -182,15 +147,11 @@ if cc.get_id() != 'emscripten' ] if not meson.is_cross_build() - header_suppressions += [ - '-Wno-poison-system-directories', - ] + header_suppressions += ['-Wno-poison-system-directories'] endif if host_machine.system() == 'windows' - header_suppressions += [ - '-Wno-nonportable-system-include-path', - ] + header_suppressions += ['-Wno-nonportable-system-include-path'] endif elif cc.get_id() == 'gcc' @@ -216,6 +177,7 @@ if cc.get_id() != 'emscripten' files('headers/test_headers.c'), c_args: header_suppressions + program_c_args, dependencies: zix_dep, + implicit_include_directories: false, include_directories: include_dirs, ), suite: 'build', @@ -233,19 +195,16 @@ if add_languages(['cpp'], native: false, required: get_option('tests_cpp')) '-Wno-c++98-compat-pedantic', '-Wno-nullability-extension', '-Wno-padded', + '-Wno-unsafe-buffer-usage-in-libc-call', '-Wno-zero-as-null-pointer-constant', ] if not meson.is_cross_build() - cpp_test_args += [ - '-Wno-poison-system-directories', - ] + cpp_test_args += ['-Wno-poison-system-directories'] endif if host_machine.system() == 'windows' - cpp_test_args += [ - '-Wno-nonportable-system-include-path', - ] + cpp_test_args += ['-Wno-nonportable-system-include-path'] endif elif cpp.get_id() == 'gcc' @@ -267,11 +226,14 @@ if add_languages(['cpp'], native: false, required: get_option('tests_cpp')) '/wd4711', # function selected for automatic inline expansion '/wd4820', # padding added after construct '/wd5039', # throwing function passed to C (winbase.h) + '/wd5045', # will insert Spectre mitigation for memory load '/wd5262', # implicit fall-through '/wd5264', # const variable is not used ] endif + cpp_test_args = cpp.get_supported_arguments(cpp_test_args) + test( 'headers_cpp', executable( @@ -279,6 +241,7 @@ if add_languages(['cpp'], native: false, required: get_option('tests_cpp')) files('cpp/test_headers_cpp.cpp'), cpp_args: cpp_test_args + program_c_args, dependencies: [zix_dep], + implicit_include_directories: false, include_directories: include_dirs, link_args: program_link_args, ), @@ -296,6 +259,7 @@ int main(void) { return 0; }''' files('cpp/test_path_std.cpp'), cpp_args: cpp_test_args + program_c_args, dependencies: [zix_dep], + implicit_include_directories: false, include_directories: include_dirs, link_args: program_link_args, ), diff --git a/test/test_allocator.c b/test/test_allocator.c index 9ecbfa0..e9be7ea 100644 --- a/test/test_allocator.c +++ b/test/test_allocator.c @@ -5,8 +5,8 @@ #include "failing_allocator.h" -#include "zix/allocator.h" -#include "zix/bump_allocator.h" +#include <zix/allocator.h> +#include <zix/bump_allocator.h> #include <assert.h> #include <stddef.h> @@ -122,7 +122,7 @@ static void test_failing_allocator(void) { ZixFailingAllocator allocator = zix_failing_allocator(); - allocator.n_remaining = 0; + zix_failing_allocator_reset(&allocator, 0); assert(!zix_malloc(&allocator.base, 16U)); assert(!zix_calloc(&allocator.base, 16U, 1U)); diff --git a/test/test_btree.c b/test/test_btree.c index e918799..a87e26e 100644 --- a/test/test_btree.c +++ b/test/test_btree.c @@ -3,16 +3,15 @@ #undef NDEBUG -#include "zix/btree.h" - #include "ensure.h" #include "failing_allocator.h" #include "test_args.h" #include "test_data.h" -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/btree.h> +#include <zix/status.h> #include <assert.h> #include <inttypes.h> @@ -20,8 +19,7 @@ #include <stdio.h> #include <stdlib.h> -ZIX_PURE_FUNC -static int +ZIX_PURE_FUNC static int int_cmp(const void* a, const void* b, const void* ZIX_UNUSED(user_data)) { const uintptr_t ia = (uintptr_t)a; @@ -103,6 +101,23 @@ destroy(void* const ptr, const void* const user_data) } static void +test_empty(void) +{ + ZixBTree* const t = zix_btree_new(NULL, int_cmp, NULL); + assert(t); + + // Check that reading functions work properly with an empty (rootless) tree + const int e = 42; + ZixBTreeIter ti = zix_btree_end(t); + zix_btree_clear(t, NULL, NULL); + assert(!zix_btree_size(t)); + assert(zix_btree_find(t, &e, &ti) == ZIX_STATUS_NOT_FOUND); + assert(!zix_btree_lower_bound(t, int_cmp, NULL, &e, &ti)); + + zix_btree_free(t, destroy, NULL); +} + +static void test_clear(void) { ZixBTree* t = zix_btree_new(NULL, int_cmp, NULL); @@ -419,7 +434,7 @@ stress(ZixAllocator* const allocator, uintptr_t removed = 0; ENSUREV(t, zix_btree_remove(t, (void*)r, (void**)&removed, &next), - "Removal of non-existant %" PRIuPTR " succeeded\n", + "Removal of non-existent %" PRIuPTR " succeeded\n", r); } @@ -563,9 +578,9 @@ test_failed_alloc(void) assert(!stress(&allocator.base, 0, 4096)); // Test that each allocation failing is handled gracefully - const size_t n_new_allocs = allocator.n_allocations; + const size_t n_new_allocs = zix_failing_allocator_reset(&allocator, 0); for (size_t i = 0U; i < n_new_allocs; ++i) { - allocator.n_remaining = i; + zix_failing_allocator_reset(&allocator, i); assert(stress(&allocator.base, 0, 4096)); } } @@ -578,6 +593,7 @@ main(int argc, char** argv) return EXIT_FAILURE; } + test_empty(); test_clear(); test_free(); test_iter_comparison(); diff --git a/test/test_digest.c b/test/test_digest.c index 7228f86..65a0e41 100644 --- a/test/test_digest.c +++ b/test/test_digest.c @@ -3,8 +3,8 @@ #undef NDEBUG -#include "zix/attributes.h" -#include "zix/digest.h" +#include <zix/attributes.h> +#include <zix/digest.h> #include <assert.h> #include <stddef.h> @@ -116,8 +116,7 @@ test_digest_aligned(void) } } -ZIX_PURE_FUNC -int +ZIX_PURE_FUNC int main(void) { test_digest32(); diff --git a/test/test_environment.c b/test/test_environment.c new file mode 100644 index 0000000..8756441 --- /dev/null +++ b/test/test_environment.c @@ -0,0 +1,143 @@ +// Copyright 2012-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#undef NDEBUG + +#include "failing_allocator.h" + +#include <zix/allocator.h> +#include <zix/environment.h> +#include <zix/path.h> + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#ifdef _WIN32 + +# define HOME_NAME "USERPROFILE" +# define HOME_VAR "%USERPROFILE%" + +#else + +# define HOME_NAME "HOME" +# define HOME_VAR "$HOME" + +static char* +concat(ZixAllocator* const allocator, + const char* const prefix, + const char* const suffix) +{ + const size_t prefix_len = strlen(prefix); + const size_t suffix_len = strlen(suffix); + const size_t result_len = prefix_len + suffix_len; + + char* const result = (char*)zix_calloc(allocator, 1U, result_len + 1U); + assert(result); + memcpy(result, prefix, prefix_len + 1U); + memcpy(result + prefix_len, suffix, suffix_len + 1U); + return result; +} + +#endif + +static void +check_expansion(const char* const path, const char* const expected) +{ + char* const expanded = zix_expand_environment_strings(NULL, path); + assert(expanded); + assert(!strcmp(expanded, expected)); + zix_free(NULL, expanded); +} + +static void +test_expansion(void) +{ + // Check non-expansion of hopefully unset variables + check_expansion("$ZIX_UNSET0", "$ZIX_UNSET0"); + check_expansion("$ZIX_unset0", "$ZIX_unset0"); + check_expansion("%ZIX_UNSET0%", "%ZIX_UNSET0%"); + check_expansion("%ZIX_unset0%", "%ZIX_unset0%"); + + // Check non-expansion of invalid variable names + check_expansion("$%INVALID", "$%INVALID"); + check_expansion("$<INVALID>", "$<INVALID>"); + check_expansion("$[INVALID]", "$[INVALID]"); + check_expansion("$invalid", "$invalid"); + check_expansion("${INVALID}", "${INVALID}"); + + const char* const home = getenv(HOME_NAME); + if (home) { + char* const var_foo = zix_path_join(NULL, HOME_VAR, "foo"); + char* const home_foo = zix_path_join(NULL, home, "foo"); + + check_expansion(var_foo, home_foo); + +#ifndef _WIN32 + char* const tilde_foo = zix_path_join(NULL, "~", "foo"); + char* const home_and_other = concat(NULL, home, ":/other"); + char* const other_and_home = concat(NULL, "/other:", home); + check_expansion("~other", "~other"); + check_expansion("~", home); + check_expansion("~/foo", home_foo); + check_expansion("~:/other", home_and_other); + check_expansion("/other:~", other_and_home); + check_expansion("$HO", "$HO"); + check_expansion("$HOMEZIX", "$HOMEZIX"); + zix_free(NULL, other_and_home); + zix_free(NULL, home_and_other); + zix_free(NULL, tilde_foo); +#endif + + zix_free(NULL, home_foo); + zix_free(NULL, var_foo); + } +} + +static void +test_failed_alloc(void) +{ + ZixFailingAllocator allocator = zix_failing_allocator(); + + zix_failing_allocator_reset(&allocator, 0U); + assert(!zix_expand_environment_strings(&allocator.base, "/one:~")); + assert(!zix_expand_environment_strings(&allocator.base, "/only")); + assert(!zix_expand_environment_strings(&allocator.base, "~")); + +#ifndef _WIN32 + zix_failing_allocator_reset(&allocator, 1U); + assert(!zix_expand_environment_strings(&allocator.base, "/one:~")); + assert(!zix_expand_environment_strings(&allocator.base, "/one:$HOME/two")); + + zix_failing_allocator_reset(&allocator, 2U); + assert(!zix_expand_environment_strings(&allocator.base, "/one:$HOME/two")); + + zix_failing_allocator_reset(&allocator, 1U); + assert(!zix_expand_environment_strings(&allocator.base, "/one:$UNSET/two")); +#endif +} + +#ifndef _WIN32 + +// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) +extern char** environ; + +static void +test_null_environ(void) +{ + environ = NULL; + check_expansion(HOME_VAR, HOME_VAR); +} + +#endif + +int +main(void) +{ + test_expansion(); + test_failed_alloc(); +#ifndef _WIN32 + test_null_environ(); +#endif + return 0; +} diff --git a/test/test_filesystem.c b/test/test_filesystem.c index 87a3d15..40fc9a2 100644 --- a/test/test_filesystem.c +++ b/test/test_filesystem.c @@ -1,13 +1,13 @@ -// Copyright 2020-2023 David Robillard <d@drobilla.net> +// Copyright 2020-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #undef NDEBUG -#include "zix/allocator.h" -#include "zix/filesystem.h" -#include "zix/path.h" -#include "zix/status.h" -#include "zix/string_view.h" +#include <zix/allocator.h> +#include <zix/filesystem.h> +#include <zix/path.h> +#include <zix/status.h> +#include <zix/string_view.h> #ifndef _WIN32 # include <unistd.h> @@ -64,6 +64,10 @@ test_canonical_path(void) char* const temp_dir = create_temp_dir("zixXXXXXX"); assert(temp_dir); + char* const sub_dir = zix_path_join(NULL, temp_dir, "sub"); + assert(!zix_create_directory(sub_dir)); + assert(zix_file_type(temp_dir) == ZIX_FILE_TYPE_DIRECTORY); + char* const file_path = zix_path_join(NULL, temp_dir, "zix_test_file"); assert(file_path); @@ -96,10 +100,10 @@ test_canonical_path(void) // Test dot segment resolution - char* const parent_dir_1 = zix_path_join(NULL, temp_dir, ".."); + char* const parent_dir_1 = zix_path_join(NULL, sub_dir, ".."); assert(parent_dir_1); - const ZixStringView parent_view = zix_path_parent_path(temp_dir); + const ZixStringView parent_view = zix_path_parent_path(sub_dir); char* const parent_dir_2 = zix_string_view_copy(NULL, parent_view); assert(parent_dir_2); assert(parent_dir_2[0]); @@ -120,6 +124,7 @@ test_canonical_path(void) // Clean everything up assert(!zix_remove(file_path)); + assert(!zix_remove(sub_dir)); assert(!zix_remove(temp_dir)); free(real_parent_dir_2); @@ -127,6 +132,7 @@ test_canonical_path(void) free(parent_dir_2); free(parent_dir_1); free(file_path); + free(sub_dir); free(temp_dir); } @@ -161,6 +167,7 @@ test_file_type(void) if (sock >= 0) { const socklen_t addr_len = sizeof(struct sockaddr_un); struct sockaddr_un* const addr = (struct sockaddr_un*)calloc(1, addr_len); + assert(addr); if (strlen(file_path) < sizeof(addr->sun_path)) { addr->sun_family = AF_UNIX; @@ -172,9 +179,7 @@ test_file_type(void) assert(!zix_remove(file_path)); close(fd); } - } else { - fprintf(stderr, "warning: Skipped socket test with oddly long TMPDIR\n"); - } + } // otherwise, TMPDIR is oddly long, skip test close(sock); free(addr); @@ -245,7 +250,8 @@ write_to_path(const char* const path, const char* const contents) const size_t len = strlen(contents); fwrite(contents, 1, len, f); - ret = fflush(f) ? errno : ferror(f) ? EBADF : fclose(f) ? errno : 0; + ret = fflush(f) ? errno : ferror(f) ? EBADF : 0; + ret = (fclose(f) && !ret) ? errno : ret; } return ret; @@ -261,9 +267,7 @@ test_copy_file(const char* data_file_path) assert(tmp_file_path); assert(copy_path); - if (!data_file_path) { - data_file_path = tmp_file_path; - } + data_file_path = data_file_path ? data_file_path : tmp_file_path; assert(!write_to_path(tmp_file_path, "test\n")); @@ -395,6 +399,7 @@ visit(const char* const path, const char* const name, void* const data) if (new_names) { char* const name_copy = (char*)calloc(name_len + 1, 1); + assert(name_copy); memcpy(name_copy, name, name_len + 1); file_list->names = new_names; @@ -695,10 +700,11 @@ int main(const int argc, char** const argv) { // Try to find some existing data file that's ideally not on a tmpfs - const char* data_file_path = (argc > 1) ? argv[1] : "build.ninja"; - if (zix_file_type(data_file_path) != ZIX_FILE_TYPE_REGULAR) { - data_file_path = NULL; - } + const char* const default_file_path = (argc > 1) ? argv[1] : "build.ninja"; + const char* const data_file_path = + (zix_file_type(default_file_path) == ZIX_FILE_TYPE_REGULAR) + ? default_file_path + : NULL; test_temp_directory_path(); test_current_path(); diff --git a/test/test_hash.c b/test/test_hash.c index 3d3ca95..2ff286c 100644 --- a/test/test_hash.c +++ b/test/test_hash.c @@ -10,11 +10,11 @@ #include "failing_allocator.h" #include "test_data.h" -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/digest.h" -#include "zix/hash.h" -#include "zix/status.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/digest.h> +#include <zix/hash.h> +#include <zix/status.h> #include <assert.h> #include <inttypes.h> @@ -47,24 +47,21 @@ test_fail(TestState* const state, const char* fmt, ...) return EXIT_FAILURE; } -ZIX_PURE_FUNC -static const char* +ZIX_PURE_FUNC static const char* identity(const char* record) { return record; } /// Decent hash function using zix_digest (murmur2) -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t decent_string_hash(const char* const str) { return zix_digest(0U, str, strlen(str)); } /// Terrible hash function from K&R first edition -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t terrible_string_hash(const char* str) { size_t hash = 0U; @@ -77,8 +74,7 @@ terrible_string_hash(const char* str) return hash; } -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t string_hash_aligned(const char* const str) { size_t length = strlen(str); @@ -87,22 +83,19 @@ string_hash_aligned(const char* const str) return zix_digest_aligned(0U, str, length); } -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t string_hash32(const char* const str) { return (size_t)zix_digest32(0U, str, strlen(str)); } -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t string_hash64(const char* const str) { return (size_t)zix_digest64(0U, str, strlen(str)); } -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t string_hash32_aligned(const char* const str) { size_t length = strlen(str); @@ -113,8 +106,7 @@ string_hash32_aligned(const char* const str) #if UINTPTR_MAX >= UINT64_MAX -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t string_hash64_aligned(const char* const str) { size_t length = strlen(str); @@ -201,8 +193,8 @@ stress_with(ZixAllocator* const allocator, if (not_indexed) { memcpy(not_indexed, not_indexed_string, strlen(not_indexed_string) + 1); const char* match = (const char*)zix_hash_find_record(hash, not_indexed); - ENSUREV(&state, !match, "Unexpectedly found `%s'\n", not_indexed); free(not_indexed); + ENSUREV(&state, !match, "Unexpectedly found `%s'\n", not_indexed_string); } // Remove strings @@ -298,8 +290,7 @@ stress(ZixAllocator* const allocator, const size_t n_elems) } /// Identity hash function for numeric strings for explicitly hitting cases -ZIX_PURE_FUNC -static size_t +ZIX_PURE_FUNC static size_t identity_index_hash(const char* const str) { return strtoul(str, NULL, 10); @@ -366,9 +357,9 @@ test_failed_alloc(void) assert(!stress(&allocator.base, 16)); // Test that each allocation failing is handled gracefully - const size_t n_new_allocs = allocator.n_allocations; + const size_t n_new_allocs = zix_failing_allocator_reset(&allocator, 0); for (size_t i = 0U; i < n_new_allocs; ++i) { - allocator.n_remaining = i; + zix_failing_allocator_reset(&allocator, i); assert(stress(&allocator.base, 16)); } } diff --git a/test/test_path.c b/test/test_path.c index 5c98c32..3a8353d 100644 --- a/test/test_path.c +++ b/test/test_path.c @@ -3,8 +3,8 @@ #undef NDEBUG -#include "zix/path.h" -#include "zix/string_view.h" +#include <zix/path.h> +#include <zix/string_view.h> #include <assert.h> #include <stdbool.h> diff --git a/test/test_ring.c b/test/test_ring.c index b1845ce..06ac183 100644 --- a/test/test_ring.c +++ b/test/test_ring.c @@ -6,10 +6,10 @@ #include "failing_allocator.h" #include "test_args.h" -#include "zix/attributes.h" -#include "zix/ring.h" -#include "zix/status.h" -#include "zix/thread.h" +#include <zix/attributes.h> +#include <zix/ring.h> +#include <zix/status.h> +#include <zix/thread.h> #include <assert.h> #include <limits.h> @@ -34,8 +34,7 @@ gen_msg(int* const msg, int start) return start; } -ZIX_PURE_FUNC -static int +ZIX_PURE_FUNC static int cmp_msg(const int* const msg1, const int* const msg2) { for (unsigned i = 0U; i < MSG_SIZE; ++i) { @@ -170,9 +169,9 @@ test_failed_alloc(void) assert(ring); // Test that each allocation failing is handled gracefully - const size_t n_new_allocs = allocator.n_allocations; + const size_t n_new_allocs = zix_failing_allocator_reset(&allocator, 0); for (size_t i = 0U; i < n_new_allocs; ++i) { - allocator.n_remaining = i; + zix_failing_allocator_reset(&allocator, i); assert(!zix_ring_new(&allocator.base, 512)); } diff --git a/test/test_sem.c b/test/test_sem.c index 0118f50..ad2ea6d 100644 --- a/test/test_sem.c +++ b/test/test_sem.c @@ -3,10 +3,10 @@ #undef NDEBUG -#include "zix/attributes.h" -#include "zix/sem.h" -#include "zix/status.h" -#include "zix/thread.h" +#include <zix/attributes.h> +#include <zix/sem.h> +#include <zix/status.h> +#include <zix/thread.h> #include <assert.h> #include <stdio.h> diff --git a/test/test_status.c b/test/test_status.c index 6857928..b047fe8 100644 --- a/test/test_status.c +++ b/test/test_status.c @@ -3,7 +3,7 @@ #undef NDEBUG -#include "zix/status.h" +#include <zix/status.h> #include <assert.h> #include <stdio.h> diff --git a/test/test_string_view.c b/test/test_string_view.c index 52b824d..9aeb826 100644 --- a/test/test_string_view.c +++ b/test/test_string_view.c @@ -5,7 +5,8 @@ #include "failing_allocator.h" -#include "zix/string_view.h" +#include <zix/allocator.h> +#include <zix/string_view.h> #include <assert.h> #include <string.h> @@ -94,7 +95,7 @@ test_copy(void) ZixFailingAllocator allocator = zix_failing_allocator(); // Copying a string takes exactly one allocation - allocator.n_remaining = 1U; + zix_failing_allocator_reset(&allocator, 1U); char* const copy = zix_string_view_copy(&allocator.base, orig); assert(copy); @@ -102,7 +103,7 @@ test_copy(void) zix_free(&allocator.base, copy); // Check that allocation failure is handled gracefully - allocator.n_remaining = 0U; + zix_failing_allocator_reset(&allocator, 0U); assert(!zix_string_view_copy(&allocator.base, orig)); } diff --git a/test/test_thread.c b/test/test_thread.c index 09edde6..ede0f31 100644 --- a/test/test_thread.c +++ b/test/test_thread.c @@ -3,8 +3,8 @@ #undef NDEBUG -#include "zix/status.h" -#include "zix/thread.h" +#include <zix/status.h> +#include <zix/thread.h> #include <assert.h> #include <string.h> diff --git a/test/test_tree.c b/test/test_tree.c index fae6d85..61be9be 100644 --- a/test/test_tree.c +++ b/test/test_tree.c @@ -8,10 +8,10 @@ #include "test_args.h" #include "test_data.h" -#include "zix/allocator.h" -#include "zix/attributes.h" -#include "zix/status.h" -#include "zix/tree.h" +#include <zix/allocator.h> +#include <zix/attributes.h> +#include <zix/status.h> +#include <zix/tree.h> #include <assert.h> #include <inttypes.h> @@ -211,9 +211,9 @@ test_failed_alloc(void) assert(!stress(&allocator.base, 0, 16)); // Test that each allocation failing is handled gracefully - const size_t n_new_allocs = allocator.n_allocations; + const size_t n_new_allocs = zix_failing_allocator_reset(&allocator, 0); for (size_t i = 0U; i < n_new_allocs; ++i) { - allocator.n_remaining = i; + zix_failing_allocator_reset(&allocator, i); assert(stress(&allocator.base, 0, 16)); } } |