string: replace AsciiCaseInsensitiveKeyEqual with CaseInsensitiveEqual

This reverts commit eea38787b9 from PR #34242

We do not need comparators for HTTPHeaders since it is not using unordered_map anymore.
We only need a simple, locale-independent, ascii-only compare function for
a vector of key-value pairs of strings.

We have CaseInsensitiveEqual already in test utils, this commit moves
it to the strencodings module for use in the application code.
This commit is contained in:
Matthew Zipkin
2026-01-31 12:20:32 -05:00
parent 8172099293
commit b0ca400612
8 changed files with 39 additions and 69 deletions

View File

@@ -4,7 +4,7 @@
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <bech32.h>
#include <test/util/str.h>
#include <util/strencodings.h>
#include <boost/test/unit_test.hpp>

View File

@@ -5,7 +5,6 @@
#include <bech32.h>
#include <test/fuzz/fuzz.h>
#include <test/fuzz/FuzzedDataProvider.h>
#include <test/util/str.h>
#include <util/strencodings.h>
#include <cassert>

View File

@@ -13,7 +13,6 @@ add_library(test_util STATIC EXCLUDE_FROM_ALL
random.cpp
script.cpp
setup_common.cpp
str.cpp
time.cpp
transaction_utils.cpp
txmempool.cpp

View File

@@ -1,21 +0,0 @@
// Copyright (c) 2019-present The Bitcoin Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
#include <test/util/str.h>
#include <cstdint>
#include <string>
bool CaseInsensitiveEqual(const std::string& s1, const std::string& s2)
{
if (s1.size() != s2.size()) return false;
for (size_t i = 0; i < s1.size(); ++i) {
char c1 = s1[i];
if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
char c2 = s2[i];
if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
if (c1 != c2) return false;
}
return true;
}

View File

@@ -7,8 +7,6 @@
#include <string>
bool CaseInsensitiveEqual(const std::string& s1, const std::string& s2);
/**
* Increment a string. Useful to enumerate all fixed length strings with
* characters in [min_char, max_char].

View File

@@ -4,7 +4,6 @@
#include <util/strencodings.h>
#include <util/string.h>
#include <vector>
#include <boost/test/unit_test.hpp>
#include <test/util/common.h>
@@ -155,38 +154,27 @@ BOOST_AUTO_TEST_CASE(ConstevalFormatString_NumSpec)
HasReason{"tinyformat: Too many conversion specifiers in format string"});
}
BOOST_AUTO_TEST_CASE(ascii_case_insensitive_key_equal_test)
BOOST_AUTO_TEST_CASE(case_insensitive_equal_test)
{
AsciiCaseInsensitiveKeyEqual cmp;
BOOST_CHECK(!cmp("A", "B"));
BOOST_CHECK(!cmp("A", "b"));
BOOST_CHECK(!cmp("a", "B"));
BOOST_CHECK(!cmp("B", "A"));
BOOST_CHECK(!cmp("B", "a"));
BOOST_CHECK(!cmp("b", "A"));
BOOST_CHECK(!cmp("A", "AA"));
BOOST_CHECK(cmp("A-A", "a-a"));
BOOST_CHECK(cmp("A", "A"));
BOOST_CHECK(cmp("A", "a"));
BOOST_CHECK(cmp("a", "a"));
BOOST_CHECK(cmp("B", "b"));
BOOST_CHECK(cmp("ab", "aB"));
BOOST_CHECK(cmp("Ab", "aB"));
BOOST_CHECK(cmp("AB", "ab"));
BOOST_CHECK(!CaseInsensitiveEqual("A", "B"));
BOOST_CHECK(!CaseInsensitiveEqual("A", "b"));
BOOST_CHECK(!CaseInsensitiveEqual("a", "B"));
BOOST_CHECK(!CaseInsensitiveEqual("B", "A"));
BOOST_CHECK(!CaseInsensitiveEqual("B", "a"));
BOOST_CHECK(!CaseInsensitiveEqual("b", "A"));
BOOST_CHECK(!CaseInsensitiveEqual("A", "AA"));
BOOST_CHECK(CaseInsensitiveEqual("A-A", "a-a"));
BOOST_CHECK(CaseInsensitiveEqual("A", "A"));
BOOST_CHECK(CaseInsensitiveEqual("A", "a"));
BOOST_CHECK(CaseInsensitiveEqual("a", "a"));
BOOST_CHECK(CaseInsensitiveEqual("B", "b"));
BOOST_CHECK(CaseInsensitiveEqual("ab", "aB"));
BOOST_CHECK(CaseInsensitiveEqual("Ab", "aB"));
BOOST_CHECK(CaseInsensitiveEqual("AB", "ab"));
// Use a character with value > 127
// to ensure we don't trigger implicit-integer-sign-change
BOOST_CHECK(!cmp("a", "\xe4"));
}
BOOST_AUTO_TEST_CASE(ascii_case_insensitive_hash_test)
{
AsciiCaseInsensitiveHash hsh;
BOOST_CHECK_NE(hsh("A"), hsh("B"));
BOOST_CHECK_NE(hsh("AA"), hsh("A"));
BOOST_CHECK_EQUAL(hsh("A"), hsh("a"));
BOOST_CHECK_EQUAL(hsh("Ab"), hsh("aB"));
BOOST_CHECK_EQUAL(hsh("A\xfe"), hsh("a\xfe"));
BOOST_CHECK(!CaseInsensitiveEqual("a", "\xe4"));
}
BOOST_AUTO_TEST_CASE(line_reader_test)

View File

@@ -427,3 +427,16 @@ std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_mu
}
return *parsed_num * unit_amount;
}
bool CaseInsensitiveEqual(std::string_view s1, std::string_view s2)
{
if (s1.size() != s2.size()) return false;
for (size_t i = 0; i < s1.size(); ++i) {
char c1 = s1[i];
if (c1 >= 'A' && c1 <= 'Z') c1 -= ('A' - 'a');
char c2 = s2[i];
if (c2 >= 'A' && c2 <= 'Z') c2 -= ('A' - 'a');
if (c1 != c2) return false;
}
return true;
}

View File

@@ -325,6 +325,14 @@ std::string Capitalize(std::string str);
*/
std::optional<uint64_t> ParseByteUnits(std::string_view str, ByteUnit default_multiplier);
/**
* Locale-independent, ASCII-only comparator
* @param[in] s1 a string to compare
* @param[in] s2 another string to compare
* @returns true if s1 == s2 when both strings are converted to lowercase
*/
bool CaseInsensitiveEqual(std::string_view s1, std::string_view s2);
namespace util {
/** consteval version of HexDigit() without the lookup table. */
consteval uint8_t ConstevalHexDigit(const char c)
@@ -353,20 +361,6 @@ struct Hex {
};
} // namespace detail
struct AsciiCaseInsensitiveKeyEqual {
bool operator()(std::string_view s1, std::string_view s2) const
{
return ToLower(s1) == ToLower(s2);
}
};
struct AsciiCaseInsensitiveHash {
size_t operator()(std::string_view s) const
{
return std::hash<std::string>{}(ToLower(s));
}
};
/**
* ""_hex is a compile-time user-defined literal returning a
* `std::array<std::byte>`, equivalent to ParseHex(). Variants provided: