diff --git a/OpenRCT2.xcodeproj/project.pbxproj b/OpenRCT2.xcodeproj/project.pbxproj index b6527ce470..82b726f9ed 100644 --- a/OpenRCT2.xcodeproj/project.pbxproj +++ b/OpenRCT2.xcodeproj/project.pbxproj @@ -767,6 +767,7 @@ F42186C5840D4196981ADD16 /* EntityTweener.h in Headers */ = {isa = PBXBuildFile; fileRef = 091352A950004312BAB18717 /* EntityTweener.h */; }; 0746674FA0794ABF86E406A1 /* Litter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9D3DD6CD73F5421880280D9D /* Litter.cpp */; }; B9B6F97CE24E4A559C7BBA0A /* RideConstruction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6BCCA2EF0F5A40D5B83A83AC /* RideConstruction.cpp */; }; + 317B766A750D4365B22A1682 /* EnumMap.hpp in Headers */ = {isa = PBXBuildFile; fileRef = BA2317BF6FB54E328DEB7055 /* EnumMap.hpp */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -1836,6 +1837,7 @@ 091352A950004312BAB18717 /* EntityTweener.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = EntityTweener.h; path = src/openrct2/world/EntityTweener.h; sourceTree = SOURCE_ROOT; }; 9D3DD6CD73F5421880280D9D /* Litter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Litter.cpp; path = src/openrct2/world/Litter.cpp; sourceTree = SOURCE_ROOT; }; 6BCCA2EF0F5A40D5B83A83AC /* RideConstruction.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RideConstruction.cpp; path = src/openrct2/ride/RideConstruction.cpp; sourceTree = SOURCE_ROOT; }; + BA2317BF6FB54E328DEB7055 /* EnumMap.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = EnumMap.hpp; path = src/openrct2/core/EnumMap.hpp; sourceTree = SOURCE_ROOT; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -2503,6 +2505,7 @@ 4C8BB67F25533D64005C8830 /* StringReader.h */, F76C83991EC4E7CC00FA49E2 /* Zip.cpp */, F76C839A1EC4E7CC00FA49E2 /* Zip.h */, + BA2317BF6FB54E328DEB7055 /* EnumMap.hpp */, ); path = core; sourceTree = ""; @@ -3468,6 +3471,7 @@ 2ADE2F342244191E002598AF /* VirtualFloor.h in Headers */, 66A10F8B257F1E1800DD651A /* LandSmoothAction.h in Headers */, F42186C5840D4196981ADD16 /* EntityTweener.h in Headers */, + 317B766A750D4365B22A1682 /* EnumMap.hpp in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/src/openrct2/core/EnumMap.hpp b/src/openrct2/core/EnumMap.hpp new file mode 100644 index 0000000000..95f539421a --- /dev/null +++ b/src/openrct2/core/EnumMap.hpp @@ -0,0 +1,157 @@ +/***************************************************************************** + * Copyright (c) 2014-2021 OpenRCT2 developers + * + * For a complete list of all authors, please refer to contributors.md + * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 + * + * OpenRCT2 is licensed under the GNU General Public License version 3. + *****************************************************************************/ +#pragma once + +#include +#include +#include +#include + +/** + * Bi-directional map for converting between strings and enums / numbers. + */ +template class EnumMap +{ +private: + std::vector> _map; + bool _continiousValueIndex{ false }; + + static constexpr size_t BucketSize = 43; + std::array, BucketSize> _buckets; + + static constexpr bool ValueIndexable() + { + if constexpr (std::is_enum_v) + return true; + else if constexpr (std::is_integral_v) + return true; + return false; + } + + static constexpr auto ValueDistance(T a, T b) + { + if constexpr (std::is_enum_v) + return static_cast>(b) - static_cast>(a); + else if constexpr (std::is_integral_v) + return (b - a); + } + + static constexpr uint32_t MakeHash(const std::string_view str) + { + uint32_t res = 0x811c9dc5; + for (auto chr : str) + { + res ^= chr; + res *= 0x01000193; + } + return res; + } + +public: + EnumMap(const std::initializer_list>&& items) + : _map{ items } + { + std::sort(_map.begin(), _map.end(), [](const auto& a, const auto& b) { return a.second < b.second; }); + + if constexpr (ValueIndexable()) + { + _continiousValueIndex = true; + T cur{}; + for (size_t i = 1; i < _map.size(); i++) + { + auto nextVal = _map[i].second; + auto dist = ValueDistance(cur, _map[i].second); + if (dist != 1) + { + _continiousValueIndex = false; + break; + } + cur = nextVal; + } + } + + int32_t index = 0; + for (auto& kv : _map) + { + auto hash = MakeHash(kv.first); + auto bucketIndex = hash % BucketSize; + auto& bucket = _buckets[bucketIndex]; + bucket.push_back(index); + index++; + } + } + + std::string_view operator[](T k) const + { + auto it = find(k); + return it->first; + } + + T operator[](std::string_view k) const + { + auto it = find(k); + return it->second; + } + + auto find(const std::string_view k) const + { + const auto hash = MakeHash(k); + const auto bucketIndex = hash % BucketSize; + const auto& bucket = _buckets[bucketIndex]; + + for (auto index : bucket) + { + auto& entry = _map[index]; + if (entry.first == k) + { + return _map.begin() + index; + } + } + + return end(); + } + + auto find(const T k) const + { + const auto binarySearchValue = [&]() { + auto it = std::lower_bound(_map.begin(), _map.end(), k, [](const auto& a, const auto& b) { return a.second < b; }); + if (it == _map.end() || it->second != k) + return end(); + + return it; + }; + + if constexpr (ValueIndexable()) + { + if (_continiousValueIndex) + { + auto index = static_cast(k); + return _map.begin() + index; + } + else + { + return binarySearchValue(); + } + } + else + { + return binarySearchValue(); + } + } + + auto begin() const + { + return _map.begin(); + } + + auto end() const + { + return _map.end(); + } +}; diff --git a/src/openrct2/libopenrct2.vcxproj b/src/openrct2/libopenrct2.vcxproj index 2af6898f4a..8a82280aee 100644 --- a/src/openrct2/libopenrct2.vcxproj +++ b/src/openrct2/libopenrct2.vcxproj @@ -157,6 +157,7 @@ +