string: add AsciiCaseInsensitive{KeyEqual, Hash} for unordered map
What changed, and why it matters
This commit adds two small helper tools for comparing text strings in a case-insensitive way, intended for use when handling HTTP headers. It only introduces the helpers and their unit tests; it does not change any live network or wallet code. There is no security issue visible in the change itself.
No action required. This is a routine utility addition with no security-relevant defect evident from the diff.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The patch introduces AsciiCaseInsensitiveKeyEqual and AsciiCaseInsensitiveHash in src/util/strencodings.h, plus unit tests in src/test/util_string_tests.cpp. These are C++ functors designed for use as key_equal and hash policies in std::unordered_map when indexing HTTP header names per RFC 9110. The implementation delegates to the existing ToLower() utility and std::hash. No callers are added and no existing behavior is modified.
Changed components
src/util/strencodings.hsrc/test/util_string_tests.cppInspect captured patch +50 / −0
diff --git a/src/test/util_string_tests.cpp b/src/test/util_string_tests.cpp
index 65ee140b..4a49e5d0 100644
--- a/src/test/util_string_tests.cpp
+++ b/src/test/util_string_tests.cpp
@@ -2,6 +2,7 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <util/strencodings.h>
#include <util/string.h>
#include <boost/test/unit_test.hpp>
@@ -146,4 +147,38 @@ 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)
+{
+ 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"));
+
+ // 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_AUTO_TEST_SUITE_END()
diff --git a/src/util/strencodings.h b/src/util/strencodings.h
index dc8493ff..97a7b268 100644
--- a/src/util/strencodings.h
+++ b/src/util/strencodings.h
@@ -13,6 +13,7 @@
#include <span.h>
#include <util/string.h>
+#include <algorithm>
#include <array>
#include <bit>
#include <charconv>
@@ -353,6 +354,20 @@ 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:
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.