From c12f913eba338678ed30382aa2e5235025b6d90b Mon Sep 17 00:00:00 2001 From: lihuiba Date: Wed, 25 Sep 2024 16:41:23 +0800 Subject: [PATCH] case-insensitive map_string_key --- common/hash_combine.h | 30 ++++++++++++ common/string-keyed.h | 72 +++++++++++++++++++++++++++- common/test/test.cpp | 17 +++++++ include/photon/common/hash_combine.h | 1 + 4 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 common/hash_combine.h create mode 120000 include/photon/common/hash_combine.h diff --git a/common/hash_combine.h b/common/hash_combine.h new file mode 100644 index 00000000..dafedafc --- /dev/null +++ b/common/hash_combine.h @@ -0,0 +1,30 @@ +/* +Copyright 2022 The Photon Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +#pragma once +#include + +namespace photon { + +inline size_t hash_combine(size_t a) { return a; } + +template inline +size_t hash_combine(size_t a, size_t b, Ts...xs) { + auto x = (a<<6) + (a>>2) + 0x9e3779b9 + b; + return hash_combine(x, xs...); +} + +} // namespace photon diff --git a/common/string-keyed.h b/common/string-keyed.h index 9aea4c9e..0f90ddd4 100644 --- a/common/string-keyed.h +++ b/common/string-keyed.h @@ -17,6 +17,7 @@ limitations under the License. #pragma once #include +#include #include #include #include @@ -156,6 +157,43 @@ template>; +class Hasher_CaseInsensitive { + const size_t BUF_CAP = 64; + size_t partial_hash(std::string_view sv, size_t i, size_t n) const { + char buf[BUF_CAP]; + sv = sv.substr(i, n); + assert(sv.size() <= BUF_CAP); + for (size_t j = 0; j < sv.size(); ++j) + buf[j] = (char)tolower(sv[j]); + return std::hash()({buf, n}); + } +public: + size_t operator()(std::string_view sv) const { + size_t h = 0; + for (size_t i = 0; i < sv.size(); i += BUF_CAP) { + auto len = std::min(BUF_CAP, sv.size() - i); + auto ph = partial_hash(sv, i, len); + h = photon::hash_combine(h, ph); + } + return h; + } +}; + +class Equal_CaseInsensitive { +public: + bool operator()(std::string_view a, std::string_view b) const { + return a.size() == b.size() && strncasecmp( + a.begin(), b.begin(), a.size()) == 0; + } +}; + +template>> +using unordered_map_string_key_case_insensitive = basic_map_string_key< + std::unordered_map>; + template, class Alloc = std::allocator>> @@ -189,6 +227,23 @@ class map_string_key : public basic_map_string_key< } }; +class Less_CaseInsensitive { +public: + bool operator()(std::string_view a, std::string_view b) const { + auto len = std::min(a.size(), b.size()); + auto cmp = strncasecmp(a.begin(), b.begin(), len); + if (cmp < 0) return true; + if (cmp > 0) return false; + return a.size() < b.size(); + } +}; + +template>> +using map_string_key_case_insensitive = basic_map_string_key< + std::map>; + // the String Key-Value (Mutable), stored together // in a consecutive area, so as to save one allocation class skvm : public string_key { @@ -418,9 +473,16 @@ class basic_map_string_kv : public M using unordered_map_string_kv = basic_map_string_kv>>; -class map_string_kv : public basic_map_string_kv> { +using unordered_map_string_kv_case_insensitive = basic_map_string_kv< + std::unordered_map>; + +template, class Allocator = + std::allocator>> +class __basic_map_string_kv : public basic_map_string_kv< + std::map> { public: - using base = basic_map_string_kv>; + using base = basic_map_string_kv>; using typename base::key_type; using typename base::const_iterator; using typename base::iterator; @@ -445,3 +507,9 @@ class map_string_kv : public basic_map_string_kv> { return {base::upper_bound((const skvm&)k)}; } }; + +using map_string_kv = __basic_map_string_kv<>; + +using map_string_kv_case_insensitive = + __basic_map_string_kv; + diff --git a/common/test/test.cpp b/common/test/test.cpp index 59c33b61..ea5f6779 100644 --- a/common/test/test.cpp +++ b/common/test/test.cpp @@ -1204,6 +1204,23 @@ TEST(string_key, unordered_map_string_kv_perf) { basic_map_test(test_map); } +template static +void test_map_case_insensitive() { + M m; + m.emplace("asdf", "jkl;"); + auto it = m.find("ASDF"); + EXPECT_NE(it, m.end()); + EXPECT_EQ(it->second, "jkl;"); + EXPECT_EQ(m.count("kuherqf"), 0); +} + +TEST(string_key, case_insensitive) { + test_map_case_insensitive>(); + test_map_case_insensitive(); + test_map_case_insensitive>(); + test_map_case_insensitive(); +} + TEST(RangeLock, Basic) { RangeLock m; diff --git a/include/photon/common/hash_combine.h b/include/photon/common/hash_combine.h new file mode 120000 index 00000000..afb24037 --- /dev/null +++ b/include/photon/common/hash_combine.h @@ -0,0 +1 @@ +../../../common/hash_combine.h \ No newline at end of file