Repository: ashvardanian/StringZilla (language: rust)

Issue:
Bug: Missing `string_view_less `, `string_view_hash`, `string_view_equal`
### Describe the bug

There are compilation errors on [Standard C++ Containers with String Keys](https://github.com/ashvardanian/StringZilla?tab=readme-ov-file#standard-c-containers-with-string-keys) on README.md

`sz::string_view_less`, `sz::string_view_hash` and `sz::string_view_equal` seem not to be defined.


### Steps to reproduce

```
#include <map>
#include <unordered_map>

#include "stringzilla/stringzilla.hpp"

int main() {
  auto a = std::map<ashvardanian::stringzilla::string, int>{};
  auto b = std::unordered_map<ashvardanian::stringzilla::string, int>{};
}
```

### Expected behavior

There are no compilation error.

### StringZilla version

3.9.6

### Operating System

Fedora Linux 40

### Hardware architecture

x86

### Which interface are you using?

C++ bindings

### Contact Details

_No response_

### Are you open to being tagged as a contributor?

- [X] I am open to being mentioned in the project `.git` history as a contributor

### Is there an existing issue for this?

- [X] I have searched the existing issues

### Code of Conduct

- [X] I agree to follow this project's Code of Conduct


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml
index 5d9fe720..8f8b7803 100644
--- a/.github/workflows/prerelease.yml
+++ b/.github/workflows/prerelease.yml
@@ -28,7 +28,7 @@ jobs:
           fetch-depth: 0
           persist-credentials: false
       - name: Run TinySemVer
-        uses: ashvardanian/tinysemver@v2.0.7
+        uses: ashvardanian/tinysemver@v2.1.1
         with:
           verbose: "true"
           version-file: "VERSION"
@@ -465,7 +465,7 @@ jobs:
         if: matrix.os == 'ubuntu-24.04'
         uses: docker/setup-qemu-action@v3
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel
+        run: python -m pip install cibuildwheel==2.21.3
       - name: Build wheels
         run: cibuildwheel --output-dir wheelhouse
         env:
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 79aa61fe..80a8f989 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -27,7 +27,7 @@ jobs:
           fetch-depth: 0
           persist-credentials: false
       - name: Run TinySemVer
-        uses: ashvardanian/tinysemver@v2.0.7
+        uses: ashvardanian/tinysemver@v2.1.1
         with:
           verbose: "true"
           version-file: "VERSION"
@@ -93,7 +93,7 @@ jobs:
         if: matrix.os == 'ubuntu-24.04' # We only need QEMU for Linux builds
         uses: docker/setup-qemu-action@v3
       - name: Install cibuildwheel
-        run: python -m pip install cibuildwheel
+        run: python -m pip install cibuildwheel==2.21.3
       - name: Build wheels
         run: cibuildwheel --output-dir wheelhouse
         env:
diff --git a/include/stringzilla/stringzilla.h b/include/stringzilla/stringzilla.h
index 3d43a71c..3721c5b0 100644
--- a/include/stringzilla/stringzilla.h
+++ b/include/stringzilla/stringzilla.h
@@ -5818,7 +5818,7 @@ SZ_PUBLIC sz_bool_t sz_equal_neon(sz_cptr_t a, sz_cptr_t b, sz_size_t length) {
         a_vec.u8x16 = vld1q_u8((sz_u8_t const *)a);
         b_vec.u8x16 = vld1q_u8((sz_u8_t const *)b);
         uint8x16_t cmp = vceqq_u8(a_vec.u8x16, b_vec.u8x16);
-        if (vmaxvq_u8(cmp) != 255) { return sz_false_k; } // Check if all bytes match
+        if (vminvq_u8(cmp) != 255) { return sz_false_k; } // Check if all bytes match
     }
 
     // Handle remaining bytes
diff --git a/include/stringzilla/stringzilla.hpp b/include/stringzilla/stringzilla.hpp
index 736877df..89620860 100644
--- a/include/stringzilla/stringzilla.hpp
+++ b/include/stringzilla/stringzilla.hpp
@@ -3681,6 +3681,41 @@ bool basic_string<char_type_, allocator_>::try_preparing_replacement(size_type o
     }
 }
 
+/**
+ *  @brief  Helper function-like object to order string-view convertible objects with StringZilla.
+ *  @see    Similar to `std::less<std::string_view>`: https://en.cppreference.com/w/cpp/utility/functional/less
+ *
+ *  Unlike the STL analog, doesn't require C++14 or including the heavy `<functional>` header.
+ *  Can be used to combine STL classes with StringZilla logic, like: `std::map<std::string, int, sz::string_view_less>`.
+ */
+struct string_view_less {
+    bool operator()(string_view a, string_view b) const noexcept { return a < b; }
+};
+
+/**
+ *  @brief  Helper function-like object to check equality between string-view convertible objects with StringZilla.
+ *  @see    Similar to `std::equal_to<std::string_view>`: https://en.cppreference.com/w/cpp/utility/functional/equal_to
+ *
+ *  Unlike the STL analog, doesn't require C++14 or including the heavy `<functional>` header.
+ *  Can be used to combine STL classes with StringZilla logic, like:
+ *      `std::unordered_map<std::string, int, sz::string_view_hash, sz::string_view_equal_to>`.
+ */
+struct string_view_equal_to {
+    bool operator()(string_view a, string_view b) const noexcept { return a == b; }
+};
+
+/**
+ *  @brief  Helper function-like object to hash string-view convertible objects with StringZilla.
+ *  @see    Similar to `std::hash<std::string_view>`: https://en.cppreference.com/w/cpp/utility/functional/hash
+ *
+ *  Unlike the STL analog, doesn't require C++14 or including the heavy `<functional>` header.
+ *  Can be used to combine STL classes with StringZilla logic, like:
+ *      `std::unordered_map<std::string, int, sz::string_view_hash, sz::string_view_equal_to>`.
+ */
+struct string_view_hash {
+    std::size_t operator()(string_view str) const noexcept { return str.hash(); }
+};
+
 /**  @brief  SFINAE-type used to infer the resulting type of concatenating multiple string together. */
 template <typename... args_types>
 struct concatenation_result {};


Test patch:
diff --git a/scripts/test.cpp b/scripts/test.cpp
index 8c1186ea..c32cc92d 100644
--- a/scripts/test.cpp
+++ b/scripts/test.cpp
@@ -24,14 +24,16 @@
 #include <sanitizer/asan_interface.h> // ASAN
 #endif
 
-#include <algorithm> // `std::transform`
-#include <cstdio>    // `std::printf`
-#include <cstring>   // `std::memcpy`
-#include <iterator>  // `std::distance`
-#include <memory>    // `std::allocator`
-#include <random>    // `std::random_device`
-#include <sstream>   // `std::ostringstream`
-#include <vector>    // `std::vector`
+#include <algorithm>     // `std::transform`
+#include <cstdio>        // `std::printf`
+#include <cstring>       // `std::memcpy`
+#include <iterator>      // `std::distance`
+#include <map>           // `std::map`
+#include <memory>        // `std::allocator`
+#include <random>        // `std::random_device`
+#include <sstream>       // `std::ostringstream`
+#include <unordered_map> // `std::unordered_map`
+#include <vector>        // `std::vector`
 
 #include <string>      // Baseline
 #include <string_view> // Baseline
@@ -58,6 +60,14 @@ template class std::basic_string<char>;
 template class sz::basic_string<char>;
 template class sz::basic_charset<char>;
 
+template class std::vector<sz::string>;
+template class std::map<sz::string, int>;
+template class std::unordered_map<sz::string, int>;
+
+template class std::vector<sz::string_view>;
+template class std::map<sz::string_view, int>;
+template class std::unordered_map<sz::string_view, int>;
+
 /**
  *  @brief  Several string processing operations rely on computing integer logarithms.
  *          Failures in such operations will result in wrong `resize` outcomes and heap corruption.
@@ -394,6 +404,10 @@ static void test_stl_compatibility_for_reads() {
     assert(str("12341234") <= str("12341234"));
     assert(str("12341234") > str("12241224"));
     assert(str("12341234") < str("13241324"));
+    assert(str("0123456789012345678901234567890123456789012345678901234567890123") ==
+           str("0123456789012345678901234567890123456789012345678901234567890123"));
+    assert(str("0123456789012345678901234567890123456789012345678901234567890123") !=
+           str("0223456789012345678901234567890123456789012345678901234567890123"));
 
     // Comparisons.
     assert(str("a") != str("b"));
@@ -1489,6 +1503,21 @@ static void test_sequence_algorithms() {
     }
 }
 
+/**
+ *  @brief  Tests constructing STL containers with StringZilla strings.
+ */
+static void test_stl_containers() {
+    std::map<sz::string, int> sorted_words_sz;
+    std::unordered_map<sz::string, int> words_sz;
+    assert(sorted_words_sz.empty());
+    assert(words_sz.empty());
+
+    std::map<std::string, int, sz::string_view_less> sorted_words_stl;
+    std::unordered_map<std::string, int, sz::string_view_hash, sz::string_view_equal_to> words_stl;
+    assert(sorted_words_stl.empty());
+    assert(words_stl.empty());
+}
+
 int main(int argc, char const **argv) {
 
     // Let's greet the user nicely
@@ -1539,6 +1568,7 @@ int main(int argc, char const **argv) {
 
     // Sequences of strings
     test_sequence_algorithms();
+    test_stl_containers();
 
     std::printf("All tests passed... Unbelievable!\n");
     return 0;