diff --git a/src/CaseInsensitiveConstString.hpp b/src/CaseInsensitiveConstString.hpp new file mode 100644 index 0000000..12f094d --- /dev/null +++ b/src/CaseInsensitiveConstString.hpp @@ -0,0 +1,66 @@ +#ifndef ARBUTILS_CASEINSENSITIVECONSTSTRING_HPP +#define ARBUTILS_CASEINSENSITIVECONSTSTRING_HPP + +#include +#include +#include +#include + +namespace Arbutils { + class CaseInsensitiveConstString { + private: + const char* _str; + size_t _length; + uint32_t _hash; + + inline static constexpr char charToLower(const char c) { return (c >= 'A' && c <= 'Z') ? c + ('a' - 'A') : c; } + inline static uint32_t constexpr Hash(char const* input) { + return charToLower(*input) ? static_cast(charToLower(*input)) + 33 * Hash(input + 1) : 5381; + } + + inline static int constexpr Length(const char* str) { return *str ? 1 + Length(str + 1) : 0; } + + public: + constexpr CaseInsensitiveConstString() : _str(""), _length(0), _hash(Hash("")){}; + constexpr explicit CaseInsensitiveConstString(const char* str) + : _str(str), _length(Length(str)), _hash(Hash(str)){}; + constexpr explicit CaseInsensitiveConstString(const std::string& str) + : _str(str.c_str()), _length(str.length()), _hash(Hash(str.c_str())){}; + constexpr explicit CaseInsensitiveConstString(const char* str, size_t size) + : _str(str), _length(size), _hash(Hash(str)){}; + + [[nodiscard]] inline constexpr const char* c_str() const noexcept { return _str; } + [[nodiscard]] inline std::string std_str() const { return std::string(_str, _length); } + + [[nodiscard]] inline constexpr size_t Length() const noexcept { return _length; } + + [[nodiscard]] inline constexpr uint32_t GetHash() const noexcept { return _hash; } + + constexpr std::size_t operator()(CaseInsensitiveConstString const& s) const noexcept { return s.GetHash(); } + inline constexpr operator uint32_t() const { return _hash; } + + inline constexpr bool operator==(const CaseInsensitiveConstString& rhs) const { return _hash == rhs._hash; } + inline constexpr bool operator!=(const CaseInsensitiveConstString& rhs) const { return _hash != rhs._hash; } + inline constexpr bool operator==(const std::string& rhs) const { return _hash == Hash(rhs.c_str()); } + inline constexpr bool operator!=(const std::string& rhs) const { return _hash != Hash(rhs.c_str()); } + inline constexpr bool operator==(const char* rhs) const { return _hash == Hash(rhs); } + inline constexpr bool operator!=(const char* rhs) const { return _hash != Hash(rhs); } + }; +} + +inline constexpr Arbutils::CaseInsensitiveConstString operator"" _const_nocase(const char* c, size_t l) { + return Arbutils::CaseInsensitiveConstString(c, l); +} +inline constexpr Arbutils::CaseInsensitiveConstString operator"" _cnc(const char* c, size_t l) { + return Arbutils::CaseInsensitiveConstString(c, l); +} + +namespace std { + template <> struct hash { + constexpr std::size_t operator()(Arbutils::CaseInsensitiveConstString const& s) const noexcept { + return s.GetHash(); + } + }; +} + +#endif // ARBUTILS_CASEINSENSITIVECONSTSTRING_HPP diff --git a/src/ConstString.hpp b/src/ConstString.hpp index 78b28e0..4a8ea2a 100644 --- a/src/ConstString.hpp +++ b/src/ConstString.hpp @@ -25,11 +25,6 @@ namespace Arbutils { inline static int constexpr Length(const char* str) { return *str ? 1 + Length(str + 1) : 0; } - constexpr explicit ConstString(const char* str, bool) - : _str(str), _length(Length(str)), _hash(LowercaseHash(str)){}; - constexpr explicit ConstString(const char* str, size_t size, bool) - : _str(str), _length(size), _hash(LowercaseHash(str)){}; - public: constexpr ConstString() : _str(""), _length(0), _hash(Hash("")){}; constexpr explicit ConstString(const char* str) : _str(str), _length(Length(str)), _hash(Hash(str)){}; @@ -37,11 +32,6 @@ namespace Arbutils { : _str(str.c_str()), _length(str.length()), _hash(Hash(str.c_str())){}; constexpr explicit ConstString(const char* str, size_t size) : _str(str), _length(size), _hash(Hash(str)){}; - static constexpr inline ConstString CaseInsensitive(const char* str) { return ConstString(str, true); } - static constexpr inline ConstString CaseInsensitive(const char* str, size_t size) { - return ConstString(str, size, true); - } - [[nodiscard]] inline constexpr const char* c_str() const noexcept { return _str; } [[nodiscard]] inline std::string std_str() const { return std::string(_str, _length); } @@ -65,12 +55,6 @@ inline constexpr Arbutils::ConstString operator"" _const(const char* c, size_t l return Arbutils::ConstString(c, l); } inline constexpr Arbutils::ConstString operator"" _c(const char* c, size_t l) { return Arbutils::ConstString(c, l); } -inline constexpr Arbutils::ConstString operator"" _const_nocase(const char* c, size_t l) { - return Arbutils::ConstString::CaseInsensitive(c, l); -} -inline constexpr Arbutils::ConstString operator"" _cnc(const char* c, size_t l) { - return Arbutils::ConstString::CaseInsensitive(c, l); -} namespace std { template <> struct hash { diff --git a/tests/ConstStringTests.cpp b/tests/ConstStringTests.cpp index 29b4435..023a04e 100644 --- a/tests/ConstStringTests.cpp +++ b/tests/ConstStringTests.cpp @@ -2,6 +2,7 @@ #include #include #include "../extern/catch.hpp" +#include "../src/CaseInsensitiveConstString.hpp" #include "../src/ConstString.hpp" TEST_CASE("Use const string in unordered_map", "[Utilities]") { @@ -24,7 +25,7 @@ TEST_CASE("Use const string in switch case", "[Utilities]") { } TEST_CASE("Use insensitive const string in unordered_map", "[Utilities]") { - std::unordered_map map; + std::unordered_map map; map.insert({"foO"_cnc, 1}); map.insert({"bAR"_cnc, 5}); @@ -33,7 +34,7 @@ TEST_CASE("Use insensitive const string in unordered_map", "[Utilities]") { } TEST_CASE("Use case insensitive const string in switch case", "[Utilities]") { - auto val = Arbutils::ConstString::CaseInsensitive("foobar"); + auto val = Arbutils::CaseInsensitiveConstString("foobar"); switch (val){ case "foo"_cnc: FAIL(); break; case "bar"_cnc: FAIL(); break;