diff --git a/.github/actions/spelling/expect/expect.txt b/.github/actions/spelling/expect/expect.txt index b9a19437552..35da0d7949b 100644 --- a/.github/actions/spelling/expect/expect.txt +++ b/.github/actions/spelling/expect/expect.txt @@ -189,7 +189,6 @@ cacafire callee capslock CARETBLINKINGENABLED -carlos CARRIAGERETURN cascadia cassert @@ -635,7 +634,7 @@ doskey dotnet doubleclick downlevel -dpg +DPG dpi DPIAPI DPICHANGE @@ -793,6 +792,7 @@ FIXEDCONVERTED FIXEDFILEINFO Flg flyout +fmix fmodern fmtarg fmtid @@ -1471,7 +1471,6 @@ msix msrc msvcrt MSVCRTD -MSVS msys msysgit MTSM @@ -2174,7 +2173,6 @@ SHOWNOACTIVATE SHOWNORMAL SHOWWINDOW SHRT -sid sidebyside SIF SIGDN @@ -2205,7 +2203,6 @@ somefile SOURCEBRANCH sourced SOURCESDIRECTORY -SPACEBAR spammy spand sprintf @@ -2510,7 +2507,6 @@ unmark UNORM unparseable unpause -Unregister unregistering untests untextured @@ -2523,7 +2519,6 @@ upvote uri url urlencoded -Urxvt USASCII usebackq USECALLBACK @@ -2568,7 +2563,6 @@ Vcount vcpkg vcprintf vcproj -vcvarsall vcxitems vcxproj vec @@ -2825,7 +2819,6 @@ xes xff XFile XFORM -xIcon XManifest XMath XMFLOAT @@ -2858,7 +2851,6 @@ YCast YCENTER YCount YDPI -yIcon yml YOffset YPosition @@ -2866,7 +2858,6 @@ YSize YSubstantial YVIRTUALSCREEN YWalk -zamora ZCmd ZCtrl zsh diff --git a/src/buffer/out/UnicodeStorage.hpp b/src/buffer/out/UnicodeStorage.hpp index e1bd2ff3659..ac463f3ed5c 100644 --- a/src/buffer/out/UnicodeStorage.hpp +++ b/src/buffer/out/UnicodeStorage.hpp @@ -14,9 +14,11 @@ Author(s): #pragma once -#include #include -#include +#include + +#include +#include // std::unordered_map needs help to know how to hash a COORD namespace std @@ -33,10 +35,7 @@ namespace std // - the hashed coord constexpr size_t operator()(const COORD& coord) const noexcept { - size_t retVal = coord.Y; - const size_t xCoord = coord.X; - retVal |= xCoord << (sizeof(coord.Y) * CHAR_BIT); - return retVal; + return til::hash(til::bit_cast(coord)); } }; } diff --git a/src/buffer/out/textBuffer.cpp b/src/buffer/out/textBuffer.cpp index 3fba7b0829b..39f8abd328e 100644 --- a/src/buffer/out/textBuffer.cpp +++ b/src/buffer/out/textBuffer.cpp @@ -2447,7 +2447,7 @@ uint16_t TextBuffer::GetHyperlinkId(std::wstring_view uri, std::wstring_view id) // assign _currentHyperlinkId if the custom id does not already exist std::wstring newId{ id }; // hash the URL and add it to the custom ID - GH#7698 - newId += L"%" + std::to_wstring(std::hash{}(uri)); + newId += L"%" + std::to_wstring(til::hash(uri)); const auto result = _hyperlinkCustomIdMap.emplace(newId, _currentHyperlinkId); if (result.second) { diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.h b/src/cascadia/TerminalSettingsModel/ActionArgs.h index 6f7a560da75..f5cf473d5cc 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.h @@ -62,27 +62,6 @@ private: // * ActionEventArgs holds a single IActionArgs. For events that don't need // additional args, this can be nullptr. -template<> -constexpr size_t Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(const winrt::Microsoft::Terminal::Settings::Model::IActionArgs& args) -{ - return gsl::narrow_cast(args.Hash()); -} - -template<> -constexpr size_t Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& args) -{ - return gsl::narrow_cast(args.Hash()); -} - -// Retrieves the hash value for an empty-constructed object. -template -static size_t EmptyHash() -{ - // cache the value of the empty hash - static const size_t cachedHash = winrt::make_self()->Hash(); - return cachedHash; -} - //////////////////////////////////////////////////////////////////////////////// // BEGIN X-MACRO MADNESS //////////////////////////////////////////////////////////////////////////////// @@ -324,9 +303,18 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_ColorScheme = _ColorScheme; return *copy; } - size_t Hash() const + size_t Hash(uint64_t hasherState) const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(Commandline(), StartingDirectory(), TabTitle(), TabColor(), ProfileIndex(), Profile(), SuppressApplicationTitle(), ColorScheme()); + til::hasher h{ gsl::narrow_cast(hasherState) }; + h.write(Commandline()); + h.write(StartingDirectory()); + h.write(TabTitle()); + h.write(TabColor()); + h.write(ProfileIndex()); + h.write(Profile()); + h.write(SuppressApplicationTitle()); + h.write(ColorScheme()); + return h.finalize(); } }; @@ -375,9 +363,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_TerminalArgs = _TerminalArgs.Copy(); return *copy; } - size_t Hash() const + size_t Hash(uint64_t hasherState) const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TerminalArgs()); + til::hasher h{ gsl::narrow_cast(hasherState) }; + h.write(TerminalArgs()); + return h.finalize(); } }; @@ -459,9 +449,14 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_SplitSize = _SplitSize; return *copy; } - size_t Hash() const + size_t Hash(uint64_t hasherState) const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(SplitDirection(), TerminalArgs(), SplitMode(), SplitSize()); + til::hasher h{ gsl::narrow_cast(hasherState) }; + h.write(SplitDirection()); + h.write(TerminalArgs()); + h.write(SplitMode()); + h.write(SplitSize()); + return h.finalize(); } }; @@ -506,9 +501,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_TerminalArgs = _TerminalArgs.Copy(); return *copy; } - size_t Hash() const + size_t Hash(uint64_t hasherState) const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(TerminalArgs()); + til::hasher h{ gsl::narrow_cast(hasherState) }; + h.write(TerminalArgs()); + return h.finalize(); } }; @@ -625,9 +622,11 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation copy->_Actions = _Actions; return *copy; } - size_t Hash() const + size_t Hash(uint64_t hasherState) const { - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty(_Actions); + til::hasher h{ gsl::narrow_cast(hasherState) }; + h.write(winrt::get_abi(_Actions)); + return h.finalize(); } }; } diff --git a/src/cascadia/TerminalSettingsModel/ActionArgs.idl b/src/cascadia/TerminalSettingsModel/ActionArgs.idl index 457feefced7..b0edde61509 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgs.idl +++ b/src/cascadia/TerminalSettingsModel/ActionArgs.idl @@ -10,7 +10,7 @@ namespace Microsoft.Terminal.Settings.Model Boolean Equals(IActionArgs other); String GenerateName(); IActionArgs Copy(); - UInt64 Hash(); + UInt64 Hash(UInt64 hasher); }; interface IActionEventArgs @@ -128,7 +128,7 @@ namespace Microsoft.Terminal.Settings.Model Boolean Equals(NewTerminalArgs other); String GenerateName(); String ToCommandline(); - UInt64 Hash(); + UInt64 Hash(UInt64 hasher); }; [default_interface] runtimeclass ActionEventArgs : IActionEventArgs diff --git a/src/cascadia/TerminalSettingsModel/ActionArgsMagic.h b/src/cascadia/TerminalSettingsModel/ActionArgsMagic.h index 26529fb6d17..708deb93280 100644 --- a/src/cascadia/TerminalSettingsModel/ActionArgsMagic.h +++ b/src/cascadia/TerminalSettingsModel/ActionArgsMagic.h @@ -17,6 +17,8 @@ Author(s): --*/ +#pragma once + // MACRO HACKS // // We want to have code that looks like: @@ -96,7 +98,7 @@ struct InitListPlaceholder // definition of Hash() below, that's to deal with trailing commas (or in this // case, leading.) #define HASH_ARGS(type, name, jsonKey, required, ...) \ - , name() + h.write(name()); // Use ACTION_ARGS_STRUCT when you've got no other customizing to do. #define ACTION_ARGS_STRUCT(className, argsMacro) \ @@ -109,52 +111,53 @@ struct InitListPlaceholder // * NewTerminalArgs has a ToCommandline method it needs to additionally declare. // * GlobalSummonArgs has the QuakeModeFromJson helper -#define ACTION_ARG_BODY(className, argsMacro) \ - className() = default; \ - className( \ - argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \ - argsMacro(CTOR_INIT) _placeholder{} {}; \ - argsMacro(DECLARE_ARGS); \ - \ -private: \ - InitListPlaceholder _placeholder; \ - \ -public: \ - hstring GenerateName() const; \ - bool Equals(const IActionArgs& other) \ - { \ - auto otherAsUs = other.try_as(); \ - if (otherAsUs) \ - { \ - return true argsMacro(EQUALS_ARGS); \ - } \ - return false; \ - }; \ - static FromJsonResult FromJson(const Json::Value& json) \ - { \ - auto args = winrt::make_self(); \ - argsMacro(FROM_JSON_ARGS); \ - return { *args, {} }; \ - } \ - static Json::Value ToJson(const IActionArgs& val) \ - { \ - if (!val) \ - { \ - return {}; \ - } \ - Json::Value json{ Json::ValueType::objectValue }; \ - const auto args{ get_self(val) }; \ - argsMacro(TO_JSON_ARGS); \ - return json; \ - } \ - IActionArgs Copy() const \ - { \ - auto copy{ winrt::make_self() }; \ - argsMacro(COPY_ARGS); \ - return *copy; \ - } \ - size_t Hash() const \ - { \ - return ::Microsoft::Terminal::Settings::Model::HashUtils::HashProperty( \ - 0 argsMacro(HASH_ARGS)); \ +#define ACTION_ARG_BODY(className, argsMacro) \ + className() = default; \ + className( \ + argsMacro(CTOR_PARAMS) InitListPlaceholder = {}) : \ + argsMacro(CTOR_INIT) _placeholder{} {}; \ + argsMacro(DECLARE_ARGS); \ + \ +private: \ + InitListPlaceholder _placeholder; \ + \ +public: \ + hstring GenerateName() const; \ + bool Equals(const IActionArgs& other) \ + { \ + auto otherAsUs = other.try_as(); \ + if (otherAsUs) \ + { \ + return true argsMacro(EQUALS_ARGS); \ + } \ + return false; \ + }; \ + static FromJsonResult FromJson(const Json::Value& json) \ + { \ + auto args = winrt::make_self(); \ + argsMacro(FROM_JSON_ARGS); \ + return { *args, {} }; \ + } \ + static Json::Value ToJson(const IActionArgs& val) \ + { \ + if (!val) \ + { \ + return {}; \ + } \ + Json::Value json{ Json::ValueType::objectValue }; \ + const auto args{ get_self(val) }; \ + argsMacro(TO_JSON_ARGS); \ + return json; \ + } \ + IActionArgs Copy() const \ + { \ + auto copy{ winrt::make_self() }; \ + argsMacro(COPY_ARGS); \ + return *copy; \ + } \ + size_t Hash(uint64_t hasherState) const \ + { \ + til::hasher h{ gsl::narrow_cast(hasherState) }; \ + argsMacro(HASH_ARGS); \ + return h.finalize(); \ } diff --git a/src/cascadia/TerminalSettingsModel/ActionMap.cpp b/src/cascadia/TerminalSettingsModel/ActionMap.cpp index 1bd4aa39034..33d36746a3f 100644 --- a/src/cascadia/TerminalSettingsModel/ActionMap.cpp +++ b/src/cascadia/TerminalSettingsModel/ActionMap.cpp @@ -15,36 +15,42 @@ namespace winrt::Microsoft::Terminal::Settings::Model::implementation { static InternalActionID Hash(const Model::ActionAndArgs& actionAndArgs) { - size_t hashedAction{ HashUtils::HashProperty(actionAndArgs.Action()) }; + static constexpr auto initialHash = til::hasher{}.finalize(); + const auto action = actionAndArgs.Action(); + til::hasher hasher; - size_t hashedArgs{}; - if (const auto& args{ actionAndArgs.Args() }) + if (const auto args = actionAndArgs.Args()) { - // Args are defined, so hash them - hashedArgs = gsl::narrow_cast(args.Hash()); + hasher = til::hasher{ gsl::narrow_cast(args.Hash(initialHash)) }; } else { + size_t hash = 0; + // Args are not defined. // Check if the ShortcutAction supports args. switch (actionAndArgs.Action()) { -#define ON_ALL_ACTIONS_WITH_ARGS(action) \ - case ShortcutAction::action: \ - /* If it does, hash the default values for the args.*/ \ - hashedArgs = EmptyHash(); \ - break; +#define ON_ALL_ACTIONS_WITH_ARGS(action) \ + case ShortcutAction::action: \ + { \ + /* If it does, hash the default values for the args. */ \ + static const auto cachedHash = gsl::narrow_cast( \ + winrt::make_self()->Hash(initialHash)); \ + hash = cachedHash; \ + break; \ + } ALL_SHORTCUT_ACTIONS_WITH_ARGS #undef ON_ALL_ACTIONS_WITH_ARGS default: - { - // Otherwise, hash nullptr. - std::hash argsHash; - hashedArgs = argsHash(nullptr); - } + break; } + + hasher = til::hasher{ hash }; } - return hashedAction ^ hashedArgs; + + hasher.write(action); + return hasher.finalize(); } // Method Description: diff --git a/src/cascadia/TerminalSettingsModel/HashUtils.h b/src/cascadia/TerminalSettingsModel/HashUtils.h index f0b81499507..9079b5de66a 100644 --- a/src/cascadia/TerminalSettingsModel/HashUtils.h +++ b/src/cascadia/TerminalSettingsModel/HashUtils.h @@ -17,54 +17,37 @@ Revision History: #pragma once -namespace Microsoft::Terminal::Settings::Model::HashUtils -{ - // This is a helper template function for hashing multiple variables in conjunction to each other. - template - constexpr size_t HashProperty(const T& val) - { - std::hash hashFunc; - return hashFunc(val); - } - - template - constexpr size_t HashProperty(const T& val, Args&&... more) - { - // Inspired by boost::hash_combine, which causes this effect... - // seed ^= hash_value(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); - // Source: https://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html - const auto seed{ HashProperty(val) }; - return seed ^ (0x9e3779b9 + (seed << 6) + (seed >> 2) + HashProperty(std::forward(more)...)); - } +#include - template<> - constexpr size_t HashProperty(const til::color& val) - { - return HashProperty(val.a, val.r, val.g, val.b); - } - -#ifdef WINRT_Windows_Foundation_H +namespace til +{ template - constexpr size_t HashProperty(const winrt::Windows::Foundation::IReference& val) + struct hash_trait> { - return val ? HashProperty(val.Value()) : 0; - } -#endif + constexpr void operator()(hasher& h, const winrt::Windows::Foundation::IReference& v) const noexcept + { + if (v) + { + h.write(v.Value()); + } + } + }; -#ifdef WINRT_Windows_UI_H template<> - constexpr size_t HashProperty(const winrt::Windows::UI::Color& val) + struct hash_trait { - return HashProperty(til::color{ val }); - } -#endif + void operator()(hasher& h, const winrt::Microsoft::Terminal::Settings::Model::NewTerminalArgs& value) const noexcept + { + h.write(winrt::get_abi(value)); + } + }; -#ifdef WINRT_Microsoft_Terminal_Core_H template<> - constexpr size_t HashProperty(const winrt::Microsoft::Terminal::Core::Color& val) + struct hash_trait { - return HashProperty(til::color{ val }); - } -#endif - -}; + void operator()(hasher& h, const winrt::hstring& value) const noexcept + { + h.write(reinterpret_cast(value.data()), value.size() * sizeof(wchar_t)); + } + }; +} diff --git a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters index 287892c4a16..fb21fccdc31 100644 --- a/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters +++ b/src/cascadia/TerminalSettingsModel/Microsoft.Terminal.Settings.ModelLib.vcxproj.filters @@ -93,6 +93,7 @@ profileGeneration + diff --git a/src/host/alias.cpp b/src/host/alias.cpp index 01c9bda9955..232485df010 100644 --- a/src/host/alias.cpp +++ b/src/host/alias.cpp @@ -2,19 +2,14 @@ // Licensed under the MIT license. #include "precomp.h" - #include "alias.h" -#include "_output.h" +#include + #include "output.h" -#include "stream.h" -#include "_stream.h" -#include "dbcs.h" #include "handle.h" #include "misc.h" #include "../types/inc/convert.hpp" -#include "srvinit.h" -#include "resource.h" #include "ApiRoutines.h" @@ -28,10 +23,12 @@ struct case_insensitive_hash { std::size_t operator()(const std::wstring& key) const { - std::wstring lower(key); - std::transform(lower.begin(), lower.end(), lower.begin(), ::towlower); - std::hash hash; - return hash(lower); + til::hasher h; + for (const auto& ch : key) + { + h.write(::towlower(ch)); + } + return h.finalize(); } }; diff --git a/src/host/ut_host/TextBufferTests.cpp b/src/host/ut_host/TextBufferTests.cpp index 2872c9421eb..7f1da731b2f 100644 --- a/src/host/ut_host/TextBufferTests.cpp +++ b/src/host/ut_host/TextBufferTests.cpp @@ -2583,10 +2583,10 @@ void TextBufferTests::HyperlinkTrim() const TextAttribute attr{ 0x7f }; auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); - const auto url = L"test.url"; - const auto otherUrl = L"other.url"; - const auto customId = L"CustomId"; - const auto otherCustomId = L"OtherCustomId"; + static constexpr std::wstring_view url{ L"test.url" }; + static constexpr std::wstring_view otherUrl{ L"other.url" }; + static constexpr std::wstring_view customId{ L"CustomId" }; + static constexpr std::wstring_view otherCustomId{ L"OtherCustomId" }; // Set a hyperlink id in the first row and add a hyperlink to our map const COORD pos{ 70, 0 }; @@ -2606,8 +2606,8 @@ void TextBufferTests::HyperlinkTrim() // Increment the circular buffer _buffer->IncrementCircularBuffer(); - const auto finalCustomId = fmt::format(L"{}%{}", customId, std::hash{}(url)); - const auto finalOtherCustomId = fmt::format(L"{}%{}", otherCustomId, std::hash{}(otherUrl)); + const auto finalCustomId = fmt::format(L"{}%{}", customId, til::hash(url)); + const auto finalOtherCustomId = fmt::format(L"{}%{}", otherCustomId, til::hash(otherUrl)); // The hyperlink reference that was only in the first row should be deleted from the map VERIFY_ARE_EQUAL(_buffer->_hyperlinkMap.find(id), _buffer->_hyperlinkMap.end()); @@ -2629,8 +2629,8 @@ void TextBufferTests::NoHyperlinkTrim() const TextAttribute attr{ 0x7f }; auto _buffer = std::make_unique(bufferSize, attr, cursorSize, _renderTarget); - const auto url = L"test.url"; - const auto customId = L"CustomId"; + static constexpr std::wstring_view url{ L"test.url" }; + static constexpr std::wstring_view customId{ L"CustomId" }; // Set a hyperlink id in the first row and add a hyperlink to our map const COORD pos{ 70, 0 }; @@ -2647,7 +2647,7 @@ void TextBufferTests::NoHyperlinkTrim() // Increment the circular buffer _buffer->IncrementCircularBuffer(); - const auto finalCustomId = fmt::format(L"{}%{}", customId, std::hash{}(url)); + const auto finalCustomId = fmt::format(L"{}%{}", customId, til::hash(url)); // The hyperlink reference should not be deleted from the map since it is still present in the buffer VERIFY_ARE_EQUAL(_buffer->GetHyperlinkUriFromId(id), url); diff --git a/src/inc/til/hash.h b/src/inc/til/hash.h new file mode 100644 index 00000000000..c30a30e4ade --- /dev/null +++ b/src/inc/til/hash.h @@ -0,0 +1,159 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +#pragma once + +namespace til +{ + template + struct hash_trait; + + struct hasher + { + explicit constexpr hasher(size_t state = FNV_offset_basis) noexcept : + _hash{ state } {} + + template + constexpr void write(const T& v) noexcept + { + hash_trait{}(*this, v); + } + + template>> + constexpr void write(const T* data, size_t count) noexcept + { +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + write(reinterpret_cast(data), sizeof(T) * count); + } + +#pragma warning(suppress : 26429) // Symbol 'data' is never tested for nullness, it can be marked as not_null (f.23). + constexpr void write(const uint8_t* data, size_t count) noexcept + { + for (size_t i = 0; i < count; ++i) + { +#pragma warning(suppress : 26481) // Don't use pointer arithmetic. Use span instead (bounds.1). + _hash ^= static_cast(data[i]); + _hash *= FNV_prime; + } + } + + constexpr size_t finalize() const noexcept + { + return _hash; + } + + private: +#if defined(_WIN64) + static constexpr size_t FNV_offset_basis = 14695981039346656037ULL; + static constexpr size_t FNV_prime = 1099511628211ULL; +#else + static constexpr size_t FNV_offset_basis = 2166136261U; + static constexpr size_t FNV_prime = 16777619U; +#endif + + size_t _hash = FNV_offset_basis; + }; + + namespace details + { + template + struct conditionally_enabled_hash_trait + { + constexpr void operator()(hasher& h, const T& v) const noexcept + { +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + h.write(reinterpret_cast(&v), sizeof(T)); + } + }; + + template + struct conditionally_enabled_hash_trait + { + conditionally_enabled_hash_trait() = delete; + conditionally_enabled_hash_trait(const conditionally_enabled_hash_trait&) = delete; + conditionally_enabled_hash_trait(conditionally_enabled_hash_trait&&) = delete; + conditionally_enabled_hash_trait& operator=(const conditionally_enabled_hash_trait&) = delete; + conditionally_enabled_hash_trait& operator=(conditionally_enabled_hash_trait&&) = delete; + }; + } + + template + struct hash_trait : details::conditionally_enabled_hash_trait> + { + }; + + template<> + struct hash_trait + { + constexpr void operator()(hasher& h, float v) const noexcept + { + v = v == 0.0f ? 0.0f : v; // map -0 to 0 +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + h.write(reinterpret_cast(&v), sizeof(v)); + } + }; + + template<> + struct hash_trait + { + constexpr void operator()(hasher& h, double v) const noexcept + { + v = v == 0.0 ? 0.0 : v; // map -0 to 0 +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + h.write(reinterpret_cast(&v), sizeof(v)); + } + }; + + template + struct hash_trait> + { + constexpr void operator()(hasher& h, const std::basic_string& v) const noexcept + { +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + h.write(reinterpret_cast(v.data()), sizeof(T) * v.size()); + } + }; + + template + struct hash_trait> + { + constexpr void operator()(hasher& h, const std::basic_string_view& v) const noexcept + { +#pragma warning(suppress : 26490) // Don't use reinterpret_cast (type.1). + h.write(reinterpret_cast(v.data()), sizeof(T) * v.size()); + } + }; + + template || std::is_enum_v>> + constexpr size_t hash(const T v) noexcept + { + // This runs murmurhash3's finalizer (fmix32/fmix64) on a single integer. + // It's fast, public domain and produces good results. + auto h = static_cast(v); + if constexpr (sizeof(size_t) == 4) + { + h ^= h >> 16; + h *= UINT32_C(0x85ebca6b); + h ^= h >> 13; + h *= UINT32_C(0xc2b2ae35); + h ^= h >> 16; + } + else + { + h ^= h >> 33; + h *= UINT64_C(0xff51afd7ed558ccd); + h ^= h >> 33; + h *= UINT64_C(0xc4ceb9fe1a85ec53); + h ^= h >> 33; + } + return h; + } + + template || std::is_enum_v)>> + constexpr size_t hash(const T& v) noexcept + { + hasher h; + h.write(v); + return h.finalize(); + } +}