summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS3
-rw-r--r--include/zix/string_view.h14
-rw-r--r--src/string_view.c21
-rw-r--r--test/meson.build3
-rw-r--r--test/test_string_view.c118
5 files changed, 155 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 7e95197..eaf8c05 100644
--- a/NEWS
+++ b/NEWS
@@ -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;
+}