summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.clang-format25
-rw-r--r--.clang-tidy3
-rw-r--r--.git-blame-ignore-revs5
-rw-r--r--.gitignore15
-rw-r--r--.gitlab-ci.yml26
-rw-r--r--.reuse/dep52
-rw-r--r--LICENSES/0BSD.txt2
-rw-r--r--[l---------]LICENSES/ISC.txt14
-rw-r--r--NEWS22
-rw-r--r--README.md10
-rw-r--r--benchmark/.clang-tidy3
-rw-r--r--benchmark/bench.h16
-rw-r--r--benchmark/dict_bench.c19
-rw-r--r--benchmark/meson.build11
-rw-r--r--benchmark/tree_bench.c77
-rw-r--r--doc/Doxyfile.in2
-rw-r--r--doc/conf.py.in4
-rw-r--r--doc/html/meson.build15
-rw-r--r--doc/meson.build3
-rw-r--r--doc/overview_code.c21
-rw-r--r--doc/singlehtml/meson.build15
-rw-r--r--doc/xml/meson.build8
-rw-r--r--include/zix/allocator.h17
-rw-r--r--include/zix/attributes.h15
-rw-r--r--include/zix/btree.h61
-rw-r--r--include/zix/bump_allocator.h7
-rw-r--r--include/zix/digest.h20
-rw-r--r--include/zix/environment.h39
-rw-r--r--include/zix/filesystem.h83
-rw-r--r--include/zix/hash.h69
-rw-r--r--include/zix/path.h78
-rw-r--r--include/zix/ring.h126
-rw-r--r--include/zix/sem.h28
-rw-r--r--include/zix/status.h5
-rw-r--r--include/zix/string_view.h22
-rw-r--r--include/zix/thread.h10
-rw-r--r--include/zix/tree.h60
-rw-r--r--include/zix/zix.h38
-rw-r--r--meson.build114
-rw-r--r--meson_options.txt27
-rwxr-xr-xscripts/benchmark.py6
-rw-r--r--src/.clang-tidy1
-rw-r--r--src/allocator.c16
-rw-r--r--src/btree.c90
-rw-r--r--src/bump_allocator.c21
-rw-r--r--src/darwin/sem_darwin.c4
-rw-r--r--src/digest.c2
-rw-r--r--src/errno_status.c4
-rw-r--r--src/errno_status.h10
-rw-r--r--src/filesystem.c7
-rw-r--r--src/hash.c7
-rw-r--r--src/path.c28
-rw-r--r--src/path_iter.h8
-rw-r--r--src/posix/environment_posix.c134
-rw-r--r--src/posix/filesystem_posix.c27
-rw-r--r--src/posix/sem_posix.c7
-rw-r--r--src/posix/thread_posix.c4
-rw-r--r--src/ring.c15
-rw-r--r--src/status.c2
-rw-r--r--src/string_view.c5
-rw-r--r--src/system.c2
-rw-r--r--src/system.h2
-rw-r--r--src/tree.c42
-rw-r--r--src/tree_debug.h139
-rw-r--r--src/win32/environment_win32.c37
-rw-r--r--src/win32/filesystem_win32.c298
-rw-r--r--src/win32/sem_win32.c4
-rw-r--r--src/win32/thread_win32.c4
-rw-r--r--src/win32/win32_util.c73
-rw-r--r--src/win32/win32_util.h31
-rw-r--r--src/zix_config.h68
-rw-r--r--subprojects/sphinxygen.wrap12
-rw-r--r--test/.clang-tidy6
-rw-r--r--test/cpp/.clang-tidy8
-rw-r--r--test/cpp/test_headers_cpp.cpp31
-rw-r--r--test/cpp/test_path_std.cpp14
-rw-r--r--test/failing_allocator.c30
-rw-r--r--test/failing_allocator.h15
-rw-r--r--test/headers/test_headers.c31
-rw-r--r--test/lint/meson.build42
-rw-r--r--test/meson.build112
-rw-r--r--test/test_allocator.c6
-rw-r--r--test/test_btree.c36
-rw-r--r--test/test_digest.c7
-rw-r--r--test/test_environment.c143
-rw-r--r--test/test_filesystem.c44
-rw-r--r--test/test_hash.c43
-rw-r--r--test/test_path.c4
-rw-r--r--test/test_ring.c15
-rw-r--r--test/test_sem.c8
-rw-r--r--test/test_status.c2
-rw-r--r--test/test_string_view.c7
-rw-r--r--test/test_thread.c4
-rw-r--r--test/test_tree.c12
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
diff --git a/.gitignore b/.gitignore
index c330864..53c3c1d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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.
diff --git a/NEWS b/NEWS
index eaf8c05..9970fcc 100644
--- a/NEWS
+++ b/NEWS
@@ -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
diff --git a/README.md b/README.md
index 654c3d4..0335acd 100644
--- a/README.md
+++ b/README.md
@@ -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>
diff --git a/src/hash.c b/src/hash.c
index 7576eba..6c98bcf 100644
--- a/src/hash.c
+++ b/src/hash.c
@@ -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);
diff --git a/src/path.c b/src/path.c
index 404557a..2240031 100644
--- a/src/path.c
+++ b/src/path.c
@@ -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>
diff --git a/src/ring.c b/src/ring.c
index ad00f75..cb8a47e 100644
--- a/src/ring.c
+++ b/src/ring.c
@@ -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>
diff --git a/src/tree.c b/src/tree.c
index 5e3aa61..4282418 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -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, &params);
+
+ 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));
}
}