From e9c29fa8069d41c8b592811cf0244dd605415567 Mon Sep 17 00:00:00 2001 From: Gleb Mazovetskiy Date: Sun, 4 Aug 2024 11:11:43 +0100 Subject: [PATCH] Switch to ankerl::unordered_dense --- 3rdParty/unordered_dense/CMakeLists.txt | 7 +++---- Source/debug.cpp | 4 +++- Source/debug.h | 5 +++-- Source/dvlnet/zerotier_native.cpp | 7 ++++--- Source/engine/render/dun_render.cpp | 2 +- Source/engine/render/dun_render.hpp | 4 ++-- Source/engine/render/scrollrt.cpp | 17 ++++++++++++----- Source/engine/render/text_render.cpp | 4 ++-- Source/engine/trn.cpp | 1 - Source/loadsave.cpp | 10 ++++++---- Source/lua/autocomplete.cpp | 6 +++--- Source/lua/lua.cpp | 4 ++-- Source/lua/modules/dev/towners.cpp | 3 ++- Source/monstdat.cpp | 4 ++-- Source/msg.cpp | 18 +++++++++--------- Source/options.h | 10 +++++----- Source/pfile.cpp | 8 ++++---- Source/platform/vita/CMakeLists.txt | 1 + Source/utils/language.cpp | 9 ++++++--- test/missiles_test.cpp | 6 ++++-- 20 files changed, 74 insertions(+), 56 deletions(-) diff --git a/3rdParty/unordered_dense/CMakeLists.txt b/3rdParty/unordered_dense/CMakeLists.txt index bd609327b..f80c8cfa5 100644 --- a/3rdParty/unordered_dense/CMakeLists.txt +++ b/3rdParty/unordered_dense/CMakeLists.txt @@ -1,21 +1,20 @@ include(functions/FetchContent_MakeAvailableExcludeFromAll) include(FetchContent) -find_package(Patch REQUIRED) - set(unordered_dense_URL "https://github.com/martinus/unordered_dense/archive/refs/tags/v4.4.0.tar.gz") set(unordered_dense_HASH "f33c294a010540434b272754f937decf") -if(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") +if(MINGW_CROSS) + find_package(Patch REQUIRED) FetchContent_Declare(unordered_dense URL ${unordered_dense_URL} URL_HASH MD5=${unordered_dense_HASH} + PATCH_COMMAND "${Patch_EXECUTABLE}" -p1 -N < "${CMAKE_CURRENT_LIST_DIR}/0001-Disable-PMR-support-for-mingw-std-threads-compat.patch" || true ) else() FetchContent_Declare(unordered_dense URL ${unordered_dense_URL} URL_HASH MD5=${unordered_dense_HASH} - PATCH_COMMAND "${Patch_EXECUTABLE}" -p1 -N < "${CMAKE_CURRENT_LIST_DIR}/0001-Disable-PMR-support-for-mingw-std-threads-compat.patch" || true ) endif() FetchContent_MakeAvailableExcludeFromAll(unordered_dense) diff --git a/Source/debug.cpp b/Source/debug.cpp index b5eb09a14..d485819ca 100644 --- a/Source/debug.cpp +++ b/Source/debug.cpp @@ -10,6 +10,8 @@ #include #include +#include + #include "debug.h" #include "automap.h" @@ -31,7 +33,7 @@ bool DebugGodMode = false; bool DebugVision = false; bool DebugPath = false; bool DebugGrid = false; -std::unordered_map DebugCoordsMap; +ankerl::unordered_dense::map DebugCoordsMap; bool DebugScrollViewEnabled = false; std::string debugTRN; diff --git a/Source/debug.h b/Source/debug.h index 29d994fc9..42b2931ff 100644 --- a/Source/debug.h +++ b/Source/debug.h @@ -7,7 +7,8 @@ #include #include -#include + +#include #include "diablo.h" #include "engine.h" @@ -22,7 +23,7 @@ extern bool DebugGodMode; extern bool DebugVision; extern bool DebugPath; extern bool DebugGrid; -extern std::unordered_map DebugCoordsMap; +extern ankerl::unordered_dense::map DebugCoordsMap; extern bool DebugScrollViewEnabled; extern std::string debugTRN; extern uint32_t glMid1Seed[NUMLEVELS]; diff --git a/Source/dvlnet/zerotier_native.cpp b/Source/dvlnet/zerotier_native.cpp index 479d8f2d0..aead501bd 100644 --- a/Source/dvlnet/zerotier_native.cpp +++ b/Source/dvlnet/zerotier_native.cpp @@ -1,8 +1,9 @@ #include "dvlnet/zerotier_native.h" -#include #include -#include + +#include +#include #ifdef USE_SDL1 #include "utils/sdl2_to_1_2_backports.h" @@ -46,7 +47,7 @@ std::atomic_bool zt_network_ready(false); std::atomic_bool zt_node_online(false); std::atomic_bool zt_joined(false); -std::unordered_map ztPeerEvents; +ankerl::unordered_dense::map ztPeerEvents; #ifdef DVL_ZT_SYMLINK bool HasMultiByteChars(std::string_view path) diff --git a/Source/engine/render/dun_render.cpp b/Source/engine/render/dun_render.cpp index 3bd1856de..2fda02f40 100644 --- a/Source/engine/render/dun_render.cpp +++ b/Source/engine/render/dun_render.cpp @@ -1101,7 +1101,7 @@ void RenderBlackTileFull(uint8_t *DVL_RESTRICT dst, uint16_t dstPitch) } // namespace #ifdef DUN_RENDER_STATS -std::unordered_map DunRenderStats; +ankerl::unordered_dense::map DunRenderStats; std::string_view TileTypeToString(TileType tileType) { diff --git a/Source/engine/render/dun_render.hpp b/Source/engine/render/dun_render.hpp index 88d4ed0c5..684c83220 100644 --- a/Source/engine/render/dun_render.hpp +++ b/Source/engine/render/dun_render.hpp @@ -15,7 +15,7 @@ // #define DUN_RENDER_STATS #ifdef DUN_RENDER_STATS -#include +#include #endif namespace devilution { @@ -156,7 +156,7 @@ struct DunRenderTypeHash { return std::hash {}((1 < static_cast(t.tileType)) | static_cast(t.maskType)); } }; -extern std::unordered_map DunRenderStats; +extern ankerl::unordered_dense::map DunRenderStats; std::string_view TileTypeToString(TileType tileType); diff --git a/Source/engine/render/scrollrt.cpp b/Source/engine/render/scrollrt.cpp index 3ff1b1190..d0f0e7da1 100644 --- a/Source/engine/render/scrollrt.cpp +++ b/Source/engine/render/scrollrt.cpp @@ -8,6 +8,8 @@ #include #include +#include + #include "DiabloUI/ui_flags.hpp" #include "automap.h" #include "controls/plrctrls.h" @@ -83,7 +85,7 @@ namespace { /** * @brief Contains all Missile at rendering position */ -std::unordered_multimap MissilesAtRenderingTile; +ankerl::unordered_dense::map> MissilesAtRenderingTile; /** * @brief Could the missile (at the next game tick) collide? This method is a simplified version of CheckMissileCol (for example without random). @@ -163,7 +165,7 @@ void UpdateMissilesRendererData() for (auto &m : Missiles) { UpdateMissileRendererData(m); - MissilesAtRenderingTile.insert(std::make_pair(m.position.tileForRendering, &m)); + MissilesAtRenderingTile[m.position.tileForRendering].push_back(&m); } } @@ -289,9 +291,10 @@ void DrawMissilePrivate(const Surface &out, const Missile &missile, Point target */ void DrawMissile(const Surface &out, WorldTilePosition tilePosition, Point targetBufferPosition, bool pre) { - const auto [begin, end] = MissilesAtRenderingTile.equal_range(tilePosition); - for (auto it = begin; it != end; ++it) { - DrawMissilePrivate(out, *it->second, targetBufferPosition, pre); + const auto it = MissilesAtRenderingTile.find(tilePosition); + if (it == MissilesAtRenderingTile.end()) return; + for (Missile *missile : it->second) { + DrawMissilePrivate(out, *missile, targetBufferPosition, pre); } } @@ -900,6 +903,10 @@ void DrawTileContent(const Surface &out, Point tilePosition, Point targetBufferP // Keep evaluating until MicroTiles can't affect screen rows += MicroTileLen; +#ifdef _DEBUG + DebugCoordsMap.reserve(rows * columns); +#endif + for (int i = 0; i < rows; i++) { bool skip = false; for (int j = 0; j < columns; j++) { diff --git a/Source/engine/render/text_render.cpp b/Source/engine/render/text_render.cpp index 567064533..a248945af 100644 --- a/Source/engine/render/text_render.cpp +++ b/Source/engine/render/text_render.cpp @@ -9,10 +9,10 @@ #include #include #include -#include #include #include +#include #include #include "DiabloUI/diabloui.h" @@ -39,7 +39,7 @@ namespace { constexpr char32_t ZWSP = U'\u200B'; // Zero-width space -std::unordered_map Fonts; +ankerl::unordered_dense::map Fonts; std::array FontSizes = { 12, 24, 30, 42, 46, 22 }; constexpr std::array LineHeights = { 12, 26, 38, 42, 50, 22 }; diff --git a/Source/engine/trn.cpp b/Source/engine/trn.cpp index 578bc3dbc..86be4d7ee 100644 --- a/Source/engine/trn.cpp +++ b/Source/engine/trn.cpp @@ -1,7 +1,6 @@ #include "engine/trn.hpp" #include -#include #include diff --git a/Source/loadsave.cpp b/Source/loadsave.cpp index 0c0dd1533..2672e9367 100644 --- a/Source/loadsave.cpp +++ b/Source/loadsave.cpp @@ -9,9 +9,9 @@ #include #include #include -#include #include +#include #include #include "automap.h" @@ -1733,7 +1733,7 @@ void SavePortal(SaveHelper *file, int i) * @return a map converting from runtime item indexes to the relative position in the save file, used by SaveDroppedItemLocations * @see SaveDroppedItemLocations */ -std::unordered_map SaveDroppedItems(SaveHelper &file) +ankerl::unordered_dense::map SaveDroppedItems(SaveHelper &file) { // Vanilla Diablo/Hellfire initialise the ActiveItems and AvailableItems arrays based on saved data, so write valid values for compatibility for (uint8_t i = 0; i < MAXITEMS; i++) @@ -1741,7 +1741,9 @@ std::unordered_map SaveDroppedItems(SaveHelper &file) for (uint8_t i = 0; i < MAXITEMS; i++) file.WriteLE((i + ActiveItemCount) % MAXITEMS); - std::unordered_map itemIndexes = { { 0, 0 } }; + ankerl::unordered_dense::map itemIndexes; + itemIndexes.reserve(ActiveItemCount + 1); + itemIndexes.emplace(0, 0); for (uint8_t i = 0; i < ActiveItemCount; i++) { itemIndexes[ActiveItems[i] + 1] = i + 1; SaveItem(file, Items[ActiveItems[i]]); @@ -1754,7 +1756,7 @@ std::unordered_map SaveDroppedItems(SaveHelper &file) * @param file interface to the save file * @param itemIndexes a map converting from runtime item indexes to the relative position in the save file */ -void SaveDroppedItemLocations(SaveHelper &file, const std::unordered_map &itemIndexes) +void SaveDroppedItemLocations(SaveHelper &file, const ankerl::unordered_dense::map &itemIndexes) { for (int j = 0; j < MAXDUNY; j++) { for (int i = 0; i < MAXDUNX; i++) // NOLINT(modernize-loop-convert) diff --git a/Source/lua/autocomplete.cpp b/Source/lua/autocomplete.cpp index 0a86a9e38..61279aaac 100644 --- a/Source/lua/autocomplete.cpp +++ b/Source/lua/autocomplete.cpp @@ -5,9 +5,9 @@ #include #include #include -#include #include +#include #include #include @@ -122,7 +122,7 @@ ValueInfo GetValueInfo(const sol::table &table, std::string_view key, const sol: } void SuggestionsFromTable(const sol::table &table, std::string_view prefix, - size_t maxSuggestions, std::unordered_set &out) + size_t maxSuggestions, ankerl::unordered_dense::set &out) { for (const auto &[key, value] : table) { if (key.get_type() == sol::type::string) { @@ -178,7 +178,7 @@ void GetLuaAutocompleteSuggestions(std::string_view text, const sol::environment const std::string_view prefix = token.substr(dotPos + 1); token.remove_suffix(token.size() - (dotPos == std::string_view::npos ? 0 : dotPos)); - std::unordered_set suggestions; + ankerl::unordered_dense::set suggestions; const auto addSuggestions = [&](const sol::table &table) { SuggestionsFromTable(table, prefix, maxSuggestions, suggestions); }; diff --git a/Source/lua/lua.cpp b/Source/lua/lua.cpp index 18c33b03e..f1c7626e7 100644 --- a/Source/lua/lua.cpp +++ b/Source/lua/lua.cpp @@ -2,8 +2,8 @@ #include #include -#include +#include #include #include @@ -30,7 +30,7 @@ namespace { struct LuaState { sol::state sol = {}; sol::table commonPackages = {}; - std::unordered_map compiledScripts = {}; + ankerl::unordered_dense::segmented_map compiledScripts = {}; sol::environment sandbox = {}; sol::table events = {}; }; diff --git a/Source/lua/modules/dev/towners.cpp b/Source/lua/modules/dev/towners.cpp index 87610258b..62cfe7d7a 100644 --- a/Source/lua/modules/dev/towners.cpp +++ b/Source/lua/modules/dev/towners.cpp @@ -3,6 +3,7 @@ #include +#include #include #include "lua/metadoc.hpp" @@ -14,7 +15,7 @@ namespace devilution { namespace { -std::unordered_map TownerShortNameToTownerId = { +ankerl::unordered_dense::map TownerShortNameToTownerId = { { "griswold", _talker_id::TOWN_SMITH }, { "smith", _talker_id::TOWN_SMITH }, { "pepin", _talker_id::TOWN_HEALER }, diff --git a/Source/monstdat.cpp b/Source/monstdat.cpp index 83689353f..f3a05b1cd 100644 --- a/Source/monstdat.cpp +++ b/Source/monstdat.cpp @@ -6,8 +6,8 @@ #include "monstdat.h" #include -#include +#include #include #include "data/file.hpp" @@ -475,7 +475,7 @@ void LoadMonstDat() MonstersData.clear(); MonstersData.reserve(dataFile.numRecords()); - std::unordered_map spritePathToId; + ankerl::unordered_dense::map spritePathToId; for (DataFileRecord record : dataFile) { RecordReader reader { record, filename }; MonsterData &monster = MonstersData.emplace_back(); diff --git a/Source/msg.cpp b/Source/msg.cpp index 17b09942e..27ef4e53a 100644 --- a/Source/msg.cpp +++ b/Source/msg.cpp @@ -8,8 +8,8 @@ #include #include #include -#include +#include #include #if !defined(UNPACKED_MPQS) || !defined(UNPACKED_SAVES) || !defined(NONET) @@ -218,8 +218,8 @@ struct DSpawnedMonster { struct DLevel { TCmdPItem item[MAXITEMS]; - std::unordered_map object; - std::unordered_map spawnedMonsters; + ankerl::unordered_dense::map object; + ankerl::unordered_dense::map spawnedMonsters; DMonsterStr monster[MaxMonsters]; }; @@ -260,14 +260,14 @@ constexpr size_t MAX_CHUNKS = MAX_MULTIPLAYERLEVELS + 4; uint32_t sgdwOwnerWait; uint32_t sgdwRecvOffset; int sgnCurrMegaPlayer; -std::unordered_map DeltaLevels; +ankerl::unordered_dense::map DeltaLevels; uint8_t sbLastCmd; /** * @brief buffer used to receive level deltas, size is the worst expected case assuming every object on a level was touched */ std::byte sgRecvBuf[1U + sizeof(DLevel::item) + sizeof(uint8_t) + (sizeof(WorldTilePosition) + sizeof(_cmd_id)) * MAXOBJECTS + sizeof(DLevel::monster)]; _cmd_id sgbRecvCmd; -std::unordered_map LocalLevels; +ankerl::unordered_dense::map LocalLevels; DJunk sgJunk; uint8_t sgbDeltaChunks; std::list MegaPktList; @@ -504,7 +504,7 @@ size_t DeltaImportItem(const std::byte *src, TCmdPItem *dst) return size; } -std::byte *DeltaExportObject(std::byte *dst, const std::unordered_map &src) +std::byte *DeltaExportObject(std::byte *dst, const ankerl::unordered_dense::map &src) { *dst++ = static_cast(src.size()); for (const auto &[position, obj] : src) { @@ -516,7 +516,7 @@ std::byte *DeltaExportObject(std::byte *dst, const std::unordered_map &dst) +const std::byte *DeltaImportObjects(const std::byte *src, ankerl::unordered_dense::map &dst) { dst.clear(); @@ -562,7 +562,7 @@ size_t DeltaImportMonster(const std::byte *src, DMonsterStr *dst) return size; } -std::byte *DeltaExportSpawnedMonsters(std::byte *dst, const std::unordered_map &spawnedMonsters) +std::byte *DeltaExportSpawnedMonsters(std::byte *dst, const ankerl::unordered_dense::map &spawnedMonsters) { auto &size = *reinterpret_cast(dst); size = static_cast(spawnedMonsters.size()); @@ -580,7 +580,7 @@ std::byte *DeltaExportSpawnedMonsters(std::byte *dst, const std::unordered_map &spawnedMonsters) +const std::byte *DeltaImportSpawnedMonsters(const std::byte *src, ankerl::unordered_dense::map &spawnedMonsters) { uint16_t size = *reinterpret_cast(src); src += sizeof(uint16_t); diff --git a/Source/options.h b/Source/options.h index ae3fba095..3c9125735 100644 --- a/Source/options.h +++ b/Source/options.h @@ -6,9 +6,9 @@ #include #include #include -#include #include +#include #include "controls/controller.h" #include "controls/controller_buttons.h" @@ -708,9 +708,9 @@ struct KeymapperOptions : OptionCategoryBase { private: std::forward_list actions; - std::unordered_map> keyIDToAction; - std::unordered_map keyIDToKeyName; - std::unordered_map keyNameToKeyID; + ankerl::unordered_dense::segmented_map> keyIDToAction; + ankerl::unordered_dense::segmented_map keyIDToKeyName; + ankerl::unordered_dense::segmented_map keyNameToKeyID; }; /** The Padmapper maps gamepad buttons to actions. */ @@ -782,7 +782,7 @@ private: std::forward_list actions; std::array::value> buttonToReleaseAction; std::array::value> buttonToButtonName; - std::unordered_map buttonNameToButton; + ankerl::unordered_dense::segmented_map buttonNameToButton; bool committed = false; const Action *FindAction(ControllerButton button) const; diff --git a/Source/pfile.cpp b/Source/pfile.cpp index f2baa6c75..0364f0b9e 100644 --- a/Source/pfile.cpp +++ b/Source/pfile.cpp @@ -8,8 +8,8 @@ #include #include #include -#include +#include #include #include "codec.h" @@ -273,7 +273,7 @@ inline bool string_ends_with(std::string_view value, std::string_view suffix) return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin()); } -void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile, CompareInfo &compareInfoReference, CompareInfo &compareInfoActual, std::unordered_map &foundDiffs) +void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile, CompareInfo &compareInfoReference, CompareInfo &compareInfoActual, ankerl::unordered_dense::segmented_map &foundDiffs) { // Note: Detail diffs are currently only supported in unit tests std::string memoryMapFileAssetName = StrCat(paths::BasePath(), "/test/fixtures/memory_map/", memoryMapFile, ".txt"); @@ -295,7 +295,7 @@ void CreateDetailDiffs(std::string_view prefix, std::string_view memoryMapFile, const std::string_view buffer(reinterpret_cast(memoryMapFileData.get()), readBytes); - std::unordered_map counter; + ankerl::unordered_dense::segmented_map counter; auto getCounter = [&](const std::string &counterAsString) { auto it = counter.find(counterAsString); @@ -482,7 +482,7 @@ HeroCompareResult CompareSaves(const std::string &actualSavePath, const std::str StrAppend(message, "file \"", compareTarget.fileName, "\" has different content."); if (!logDetails) continue; - std::unordered_map foundDiffs; + ankerl::unordered_dense::segmented_map foundDiffs; CompareInfo compareInfoReference = { fileDataReference, 0, fileSizeReference, compareTarget.isTownLevel, fileSizeReference != 0 }; CompareInfo compareInfoActual = { fileDataActual, 0, fileSizeActual, compareTarget.isTownLevel, fileSizeActual != 0 }; CreateDetailDiffs(compareTarget.fileName, compareTarget.memoryMapFileName, compareInfoReference, compareInfoActual, foundDiffs); diff --git a/Source/platform/vita/CMakeLists.txt b/Source/platform/vita/CMakeLists.txt index d17f60a86..d23b75167 100644 --- a/Source/platform/vita/CMakeLists.txt +++ b/Source/platform/vita/CMakeLists.txt @@ -24,4 +24,5 @@ target_link_libraries(libdevilutionx_vita PUBLIC SceNet_stub SceNetCtl_stub tl + unordered_dense::unordered_dense ) diff --git a/Source/utils/language.cpp b/Source/utils/language.cpp index 1e86f945b..b9bbc8479 100644 --- a/Source/utils/language.cpp +++ b/Source/utils/language.cpp @@ -3,9 +3,9 @@ #include #include #include -#include #include +#include #include #include "engine/assets.hpp" @@ -38,9 +38,11 @@ std::unique_ptr translationValues; using TranslationRef = uint32_t; struct StringHash { + using is_avalanching = void; + size_t operator()(const char *str) const noexcept { - return std::hash {}(str); + return ankerl::unordered_dense::detail::wyhash::hash(str, std::string_view { str }.size()); } }; @@ -51,7 +53,7 @@ struct StringEq { } }; -std::vector> translation = { {}, {} }; +std::vector> translation = { {}, {} }; constexpr uint32_t TranslationRefOffsetBits = 19; constexpr uint32_t TranslationRefSizeBits = 32 - TranslationRefOffsetBits; // 13 @@ -481,6 +483,7 @@ void LanguageInitialize() char *keyPtr = &translationKeys[0]; char *valuePtr = &translationValues[0]; + translation[0].reserve(head.nbMappings - 1); for (uint32_t i = 1; i < head.nbMappings; i++) { if (readWholeFile ? ReadEntry(data.get(), fileSize, src[i], keyPtr) && ReadEntry(data.get(), fileSize, dst[i], valuePtr) diff --git a/test/missiles_test.cpp b/test/missiles_test.cpp index d518e11fc..3841f4328 100644 --- a/test/missiles_test.cpp +++ b/test/missiles_test.cpp @@ -1,6 +1,8 @@ #include #include +#include + #include "engine/random.hpp" #include "missiles.h" @@ -13,7 +15,7 @@ using ::testing::UnorderedElementsAre; void TestArrowRotatesUniformly(Missile &missile, int startingFrame, int leftFrame, int rightFrame) { - std::unordered_map observed {}; + ankerl::unordered_dense::map observed {}; for (auto i = 0; i < 100; i++) { missile._miAnimFrame = startingFrame; TestRotateBlockedMissile(missile); @@ -25,7 +27,7 @@ void TestArrowRotatesUniformly(Missile &missile, int startingFrame, int leftFram void TestAnimatedMissileRotatesUniformly(Missile &missile, int startingDir, int leftDir, int rightDir) { - std::unordered_map observed {}; + ankerl::unordered_dense::map observed {}; for (auto i = 0; i < 100; i++) { missile._mimfnum = startingDir; TestRotateBlockedMissile(missile);