diff options
author | David Robillard <d@drobilla.net> | 2024-06-26 18:48:02 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2024-06-26 18:52:29 -0400 |
commit | aec0d760835219f91eb483c68ef7b2c8f9e8597e (patch) | |
tree | e732377408502d555e53cd67ddf86986da3cee2c | |
parent | c147881d999fa966ea266d6feac2a3fabeef511b (diff) | |
download | zix-aec0d760835219f91eb483c68ef7b2c8f9e8597e.tar.gz zix-aec0d760835219f91eb483c68ef7b2c8f9e8597e.tar.bz2 zix-aec0d760835219f91eb483c68ef7b2c8f9e8597e.zip |
Add zix_string_view_equals()
-rw-r--r-- | NEWS | 3 | ||||
-rw-r--r-- | include/zix/string_view.h | 14 | ||||
-rw-r--r-- | src/string_view.c | 21 | ||||
-rw-r--r-- | test/meson.build | 3 | ||||
-rw-r--r-- | test/test_string_view.c | 118 |
5 files changed, 155 insertions, 4 deletions
@@ -1,13 +1,14 @@ zix (0.5.0) unstable; urgency=medium * Add ZIX_NODISCARD attribute + * 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 - -- David Robillard <d@drobilla.net> Sun, 23 Jun 2024 12:18:29 +0000 + -- David Robillard <d@drobilla.net> Wed, 26 Jun 2024 22:21:42 +0000 zix (0.4.2) stable; urgency=medium diff --git a/include/zix/string_view.h b/include/zix/string_view.h index f463c1f..cfd478d 100644 --- a/include/zix/string_view.h +++ b/include/zix/string_view.h @@ -1,4 +1,4 @@ -// Copyright 2011-2023 David Robillard <d@drobilla.net> +// Copyright 2011-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #ifndef ZIX_STRING_VIEW_H @@ -7,6 +7,7 @@ #include "zix/allocator.h" #include "zix/attributes.h" +#include <stdbool.h> #include <stddef.h> #include <string.h> @@ -94,6 +95,17 @@ char* ZIX_ALLOCATED zix_string_view_copy(ZixAllocator* ZIX_NULLABLE allocator, ZixStringView view); /** + Return true if both string views refer to equal strings. + + This may be significantly faster than a full string comparison, because it + has fast paths for when the operands have different lengths, or point to the + same string data. +*/ +ZIX_PURE_API +bool +zix_string_view_equals(ZixStringView lhs, ZixStringView rhs); + +/** @} */ diff --git a/src/string_view.c b/src/string_view.c index 9d98258..192f918 100644 --- a/src/string_view.c +++ b/src/string_view.c @@ -1,9 +1,10 @@ -// Copyright 2007-2022 David Robillard <d@drobilla.net> +// Copyright 2007-2024 David Robillard <d@drobilla.net> // SPDX-License-Identifier: ISC #include "zix/string_view.h" #include "zix/allocator.h" +#include <stdbool.h> #include <string.h> char* @@ -16,3 +17,21 @@ zix_string_view_copy(ZixAllocator* const allocator, const ZixStringView view) } return copy; } + +bool +zix_string_view_equals(const ZixStringView lhs, const ZixStringView rhs) +{ + if (lhs.length != rhs.length) { + return false; + } + + if (lhs.data != rhs.data) { + for (size_t i = 0U; i < lhs.length; ++i) { + if (lhs.data[i] != rhs.data[i]) { + return false; + } + } + } + + return true; +} diff --git a/test/meson.build b/test/meson.build index 166de7d..96fe455 100644 --- a/test/meson.build +++ b/test/meson.build @@ -1,4 +1,4 @@ -# Copyright 2020-2023 David Robillard <d@drobilla.net> +# Copyright 2020-2024 David Robillard <d@drobilla.net> # SPDX-License-Identifier: 0BSD OR ISC if not meson.is_subproject() and get_option('lint') @@ -85,6 +85,7 @@ sequential_tests = { 'hash': {'': []}, 'path': {'': []}, 'status': {'': []}, + 'string_view': {'': []}, 'tree': { '': [], '_seed': ['8', '314159'], diff --git a/test/test_string_view.c b/test/test_string_view.c new file mode 100644 index 0000000..52b824d --- /dev/null +++ b/test/test_string_view.c @@ -0,0 +1,118 @@ +// Copyright 2021-2024 David Robillard <d@drobilla.net> +// SPDX-License-Identifier: ISC + +#undef NDEBUG + +#include "failing_allocator.h" + +#include "zix/string_view.h" + +#include <assert.h> +#include <string.h> + +static void +test_static_init(void) +{ + static const ZixStringView a = ZIX_STATIC_STRING("a"); + static const ZixStringView ab = ZIX_STATIC_STRING("ab"); + + assert(a.length == 1U); + assert(a.data); + assert(a.data[0] == 'a'); + assert(a.data[1] == '\0'); + + assert(ab.length == 2U); + assert(ab.data[0] == 'a'); + assert(ab.data[1] == 'b'); + assert(ab.data[2] == '\0'); +} + +static void +test_empty(void) +{ + const ZixStringView empty = zix_empty_string(); + + assert(!empty.length); + assert(empty.data); + assert(empty.data[0] == '\0'); +} + +static void +test_string(void) +{ + const ZixStringView nodata = zix_string(NULL); + const ZixStringView empty = zix_string(""); + + assert(!nodata.length); + assert(nodata.data); + assert(nodata.data[0] == '\0'); + + assert(!empty.length); + assert(empty.data); + assert(empty.data[0] == '\0'); +} + +static void +test_equals(void) +{ + static const char* const prefix_str = "prefix"; + + const ZixStringView prefix = zix_string(prefix_str); + const ZixStringView pre = zix_substring(prefix_str, 3U); + const ZixStringView fix = zix_substring(prefix_str + 3U, 3U); + const ZixStringView suffix1 = zix_substring("suffix_1", 6U); + const ZixStringView suffix2 = zix_substring("suffix_2", 6U); + + assert(prefix.length == 6U); + assert(pre.length == 3U); + assert(fix.length == 3U); + assert(suffix1.length == 6U); + assert(suffix2.length == 6U); + + assert(zix_string_view_equals(prefix, zix_string("prefix"))); + assert(zix_string_view_equals(pre, zix_string("pre"))); + assert(zix_string_view_equals(fix, zix_string("fix"))); + assert(zix_string_view_equals(suffix1, zix_string("suffix"))); + assert(zix_string_view_equals(suffix2, zix_string("suffix"))); + + assert(zix_string_view_equals(prefix, prefix)); + assert(zix_string_view_equals(suffix1, suffix2)); + + assert(!zix_string_view_equals(prefix, pre)); + assert(!zix_string_view_equals(pre, prefix)); + assert(!zix_string_view_equals(pre, fix)); + assert(!zix_string_view_equals(fix, prefix)); + assert(!zix_string_view_equals(suffix1, prefix)); + assert(!zix_string_view_equals(prefix, suffix1)); +} + +static void +test_copy(void) +{ + static const ZixStringView orig = ZIX_STATIC_STRING("string"); + + ZixFailingAllocator allocator = zix_failing_allocator(); + + // Copying a string takes exactly one allocation + allocator.n_remaining = 1U; + + char* const copy = zix_string_view_copy(&allocator.base, orig); + assert(copy); + assert(!strcmp(copy, "string")); + zix_free(&allocator.base, copy); + + // Check that allocation failure is handled gracefully + allocator.n_remaining = 0U; + assert(!zix_string_view_copy(&allocator.base, orig)); +} + +int +main(void) +{ + test_static_init(); + test_empty(); + test_string(); + test_equals(); + test_copy(); + return 0; +} |