Add EnumMap container for bidirectional key, enum mapping

This commit is contained in:
ZehMatt 2021-07-22 19:54:15 +03:00
parent 1ea9bbbb1b
commit a36e0a32f7
No known key found for this signature in database
GPG Key ID: 18CE582C71A225B0
3 changed files with 162 additions and 0 deletions

View File

@ -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 = "<group>";
@ -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;
};

View File

@ -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 <algorithm>
#include <array>
#include <string_view>
#include <vector>
/**
* Bi-directional map for converting between strings and enums / numbers.
*/
template<typename T> class EnumMap
{
private:
std::vector<std::pair<std::string_view, T>> _map;
bool _continiousValueIndex{ false };
static constexpr size_t BucketSize = 43;
std::array<std::vector<int32_t>, BucketSize> _buckets;
static constexpr bool ValueIndexable()
{
if constexpr (std::is_enum_v<T>)
return true;
else if constexpr (std::is_integral_v<T>)
return true;
return false;
}
static constexpr auto ValueDistance(T a, T b)
{
if constexpr (std::is_enum_v<T>)
return static_cast<std::underlying_type_t<T>>(b) - static_cast<std::underlying_type_t<T>>(a);
else if constexpr (std::is_integral_v<T>)
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<std::pair<std::string_view, T>>&& 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<size_t>(k);
return _map.begin() + index;
}
else
{
return binarySearchValue();
}
}
else
{
return binarySearchValue();
}
}
auto begin() const
{
return _map.begin();
}
auto end() const
{
return _map.end();
}
};

View File

@ -157,6 +157,7 @@
<ClInclude Include="core\DataSerialiserTraits.h" />
<ClInclude Include="core\Diagnostics.hpp" />
<ClInclude Include="core\Endianness.h" />
<ClInclude Include="core\EnumMap.hpp" />
<ClInclude Include="core\File.h" />
<ClInclude Include="core\FileIndex.hpp" />
<ClInclude Include="core\FileScanner.h" />