1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-12-14 11:13:07 +00:00

refactors MWWorld::Store maps (#3197)

With this PR we clean up `MWWorld::Store` maps according to the approach of PR #3184.
This commit is contained in:
Bo Svensson 2021-10-30 17:27:57 +00:00 committed by GitHub
parent 7d34149adc
commit 1329f22186
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 57 additions and 107 deletions

View file

@ -61,7 +61,7 @@ namespace
} }
} }
std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions, const MWWorld::Store<ESM::Class>& classes, const std::map<std::string, ESM::NPC>& npcs) std::vector<ESM::NPC> getNPCsToReplace(const MWWorld::Store<ESM::Faction>& factions, const MWWorld::Store<ESM::Class>& classes, const std::unordered_map<std::string, ESM::NPC, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>& npcs)
{ {
// Cache first class from store - we will use it if current class is not found // Cache first class from store - we will use it if current class is not found
std::string defaultCls; std::string defaultCls;
@ -114,8 +114,8 @@ namespace
// Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no longer exists however. // Custom enchanted items can reference scripts that no longer exist, this doesn't necessarily mean the base item no longer exists however.
// So instead of removing the item altogether, we're only removing the script. // So instead of removing the item altogether, we're only removing the script.
template<class T> template<class MapT>
void removeMissingScripts(const MWWorld::Store<ESM::Script>& scripts, std::map<std::string, T>& items) void removeMissingScripts(const MWWorld::Store<ESM::Script>& scripts, MapT& items)
{ {
for(auto& [id, item] : items) for(auto& [id, item] : items)
{ {
@ -324,7 +324,6 @@ void ESMStore::countRecords()
if (value.mRefID != deletedRefID) if (value.mRefID != deletedRefID)
{ {
std::string& refId = refIDs[value.mRefID]; std::string& refId = refIDs[value.mRefID];
Misc::StringUtils::lowerCaseInPlace(refId);
++mRefCount[std::move(refId)]; ++mRefCount[std::move(refId)];
} }
}; };
@ -333,8 +332,7 @@ void ESMStore::countRecords()
int ESMStore::getRefCount(const std::string& id) const int ESMStore::getRefCount(const std::string& id) const
{ {
const std::string lowerId = Misc::StringUtils::lowerCase(id); auto it = mRefCount.find(id);
auto it = mRefCount.find(lowerId);
if(it == mRefCount.end()) if(it == mRefCount.end())
return 0; return 0;
return it->second; return it->second;
@ -533,9 +531,8 @@ void ESMStore::removeMissingObjects(Store<T>& store)
throw std::runtime_error ("Invalid player record (race or class unavailable"); throw std::runtime_error ("Invalid player record (race or class unavailable");
} }
std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& originalId) const std::pair<std::shared_ptr<MWMechanics::SpellList>, bool> ESMStore::getSpellList(const std::string& id) const
{ {
const std::string id = Misc::StringUtils::lowerCase(originalId);
auto result = mSpellListCache.find(id); auto result = mSpellListCache.find(id);
std::shared_ptr<MWMechanics::SpellList> ptr; std::shared_ptr<MWMechanics::SpellList> ptr;
if (result != mSpellListCache.end()) if (result != mSpellListCache.end())

View file

@ -75,16 +75,17 @@ namespace MWWorld
// Lookup of all IDs. Makes looking up references faster. Just // Lookup of all IDs. Makes looking up references faster. Just
// maps the id name to the record type. // maps the id name to the record type.
std::map<std::string, int> mIds; using IDMap = std::unordered_map<std::string, int, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>;
std::map<std::string, int> mStaticIds; IDMap mIds;
IDMap mStaticIds;
std::unordered_map<std::string, int> mRefCount; IDMap mRefCount;
std::map<int, StoreBase *> mStores; std::map<int, StoreBase *> mStores;
unsigned int mDynamicCount; unsigned int mDynamicCount;
mutable std::map<std::string, std::weak_ptr<MWMechanics::SpellList> > mSpellListCache; mutable std::unordered_map<std::string, std::weak_ptr<MWMechanics::SpellList>, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> mSpellListCache;
/// Validate entries in store after setup /// Validate entries in store after setup
void validate(); void validate();
@ -115,10 +116,9 @@ namespace MWWorld
} }
/// Look up the given ID in 'all'. Returns 0 if not found. /// Look up the given ID in 'all'. Returns 0 if not found.
/// \note id must be in lower case.
int find(const std::string &id) const int find(const std::string &id) const
{ {
std::map<std::string, int>::const_iterator it = mIds.find(id); IDMap::const_iterator it = mIds.find(id);
if (it == mIds.end()) { if (it == mIds.end()) {
return 0; return 0;
} }
@ -126,7 +126,7 @@ namespace MWWorld
} }
int findStatic(const std::string &id) const int findStatic(const std::string &id) const
{ {
std::map<std::string, int>::const_iterator it = mStaticIds.find(id); IDMap::const_iterator it = mStaticIds.find(id);
if (it == mStaticIds.end()) { if (it == mStaticIds.end()) {
return 0; return 0;
} }

View file

@ -38,8 +38,8 @@ namespace MWWorld
bool isDeleted = false; bool isDeleted = false;
record.load(esm, isDeleted); record.load(esm, isDeleted);
auto idx = record.mIndex;
mStatic.insert_or_assign(record.mIndex, record); mStatic.insert_or_assign(idx, std::move(record));
} }
template<typename T> template<typename T>
int IndexedStore<T>::getSize() const int IndexedStore<T>::getSize() const
@ -98,13 +98,11 @@ namespace MWWorld
template<typename T> template<typename T>
const T *Store<T>::search(const std::string &id) const const T *Store<T>::search(const std::string &id) const
{ {
std::string idLower = Misc::StringUtils::lowerCase(id); typename Dynamic::const_iterator dit = mDynamic.find(id);
typename Dynamic::const_iterator dit = mDynamic.find(idLower);
if (dit != mDynamic.end()) if (dit != mDynamic.end())
return &dit->second; return &dit->second;
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower); typename Static::const_iterator it = mStatic.find(id);
if (it != mStatic.end()) if (it != mStatic.end())
return &(it->second); return &(it->second);
@ -113,8 +111,7 @@ namespace MWWorld
template<typename T> template<typename T>
const T *Store<T>::searchStatic(const std::string &id) const const T *Store<T>::searchStatic(const std::string &id) const
{ {
std::string idLower = Misc::StringUtils::lowerCase(id); typename Static::const_iterator it = mStatic.find(id);
typename std::map<std::string, T>::const_iterator it = mStatic.find(idLower);
if (it != mStatic.end()) if (it != mStatic.end())
return &(it->second); return &(it->second);
@ -159,7 +156,7 @@ namespace MWWorld
bool isDeleted = false; bool isDeleted = false;
record.load(esm, isDeleted); record.load(esm, isDeleted);
Misc::StringUtils::lowerCaseInPlace(record.mId); Misc::StringUtils::lowerCaseInPlace(record.mId); // TODO: remove this line once we have ported our remaining code base to lowercase on lookup
std::pair<typename Static::iterator, bool> inserted = mStatic.insert_or_assign(record.mId, record); std::pair<typename Static::iterator, bool> inserted = mStatic.insert_or_assign(record.mId, record);
if (inserted.second) if (inserted.second)
@ -206,14 +203,13 @@ namespace MWWorld
template<typename T> template<typename T>
T *Store<T>::insert(const T &item, bool overrideOnly) T *Store<T>::insert(const T &item, bool overrideOnly)
{ {
std::string id = Misc::StringUtils::lowerCase(item.mId);
if(overrideOnly) if(overrideOnly)
{ {
auto it = mStatic.find(id); auto it = mStatic.find(item.mId);
if(it == mStatic.end()) if(it == mStatic.end())
return nullptr; return nullptr;
} }
std::pair<typename Dynamic::iterator, bool> result = mDynamic.insert_or_assign(id, item); std::pair<typename Dynamic::iterator, bool> result = mDynamic.insert_or_assign(item.mId, item);
T *ptr = &result.first->second; T *ptr = &result.first->second;
if (result.second) if (result.second)
mShared.push_back(ptr); mShared.push_back(ptr);
@ -222,8 +218,7 @@ namespace MWWorld
template<typename T> template<typename T>
T *Store<T>::insertStatic(const T &item) T *Store<T>::insertStatic(const T &item)
{ {
std::string id = Misc::StringUtils::lowerCase(item.mId); std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(item.mId, item);
std::pair<typename Static::iterator, bool> result = mStatic.insert_or_assign(id, item);
T *ptr = &result.first->second; T *ptr = &result.first->second;
if (result.second) if (result.second)
mShared.push_back(ptr); mShared.push_back(ptr);
@ -232,9 +227,7 @@ namespace MWWorld
template<typename T> template<typename T>
bool Store<T>::eraseStatic(const std::string &id) bool Store<T>::eraseStatic(const std::string &id)
{ {
std::string idLower = Misc::StringUtils::lowerCase(id); typename Static::iterator it = mStatic.find(id);
typename std::map<std::string, T>::iterator it = mStatic.find(idLower);
if (it != mStatic.end()) { if (it != mStatic.end()) {
// delete from the static part of mShared // delete from the static part of mShared
@ -242,7 +235,7 @@ namespace MWWorld
typename std::vector<T *>::iterator end = sharedIter + mStatic.size(); typename std::vector<T *>::iterator end = sharedIter + mStatic.size();
while (sharedIter != mShared.end() && sharedIter != end) { while (sharedIter != mShared.end() && sharedIter != end) {
if((*sharedIter)->mId == idLower) { if(Misc::StringUtils::ciEqual((*sharedIter)->mId, id)) {
mShared.erase(sharedIter); mShared.erase(sharedIter);
break; break;
} }
@ -257,17 +250,13 @@ namespace MWWorld
template<typename T> template<typename T>
bool Store<T>::erase(const std::string &id) bool Store<T>::erase(const std::string &id)
{ {
std::string key = Misc::StringUtils::lowerCase(id); if (!mDynamic.erase(id))
typename Dynamic::iterator it = mDynamic.find(key);
if (it == mDynamic.end()) {
return false; return false;
}
mDynamic.erase(it);
// have to reinit the whole shared part // have to reinit the whole shared part
assert(mShared.size() >= mStatic.size()); assert(mShared.size() >= mStatic.size());
mShared.erase(mShared.begin() + mStatic.size(), mShared.end()); mShared.erase(mShared.begin() + mStatic.size(), mShared.end());
for (it = mDynamic.begin(); it != mDynamic.end(); ++it) { for (auto it = mDynamic.begin(); it != mDynamic.end(); ++it) {
mShared.push_back(&it->second); mShared.push_back(&it->second);
} }
return true; return true;
@ -353,12 +342,8 @@ namespace MWWorld
ESM::LandTexture* tex = const_cast<ESM::LandTexture*>(search(lt.mIndex, i)); ESM::LandTexture* tex = const_cast<ESM::LandTexture*>(search(lt.mIndex, i));
if (tex) if (tex)
{ {
const std::string texId = Misc::StringUtils::lowerCase(tex->mId); if (Misc::StringUtils::ciEqual(tex->mId, lt.mId))
const std::string ltId = Misc::StringUtils::lowerCase(lt.mId);
if (texId == ltId)
{
tex->mTexture = lt.mTexture; tex->mTexture = lt.mTexture;
}
} }
} }
@ -367,9 +352,10 @@ namespace MWWorld
ltexl.resize(lt.mIndex+1); ltexl.resize(lt.mIndex+1);
// Store it // Store it
ltexl[lt.mIndex] = lt; auto idx = lt.mIndex;
ltexl[idx] = std::move(lt);
return RecordId(lt.mId, isDeleted); return RecordId(ltexl[idx].mId, isDeleted);
} }
RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm) RecordId Store<ESM::LandTexture>::load(ESM::ESMReader &esm)
{ {
@ -503,16 +489,12 @@ namespace MWWorld
} }
const ESM::Cell *Store<ESM::Cell>::search(const std::string &id) const const ESM::Cell *Store<ESM::Cell>::search(const std::string &id) const
{ {
ESM::Cell cell; DynamicInt::const_iterator it = mInt.find(id);
cell.mName = Misc::StringUtils::lowerCase(id);
std::map<std::string, ESM::Cell>::const_iterator it = mInt.find(cell.mName);
if (it != mInt.end()) { if (it != mInt.end()) {
return &(it->second); return &(it->second);
} }
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName); DynamicInt::const_iterator dit = mDynamicInt.find(id);
if (dit != mDynamicInt.end()) { if (dit != mDynamicInt.end()) {
return &dit->second; return &dit->second;
} }
@ -521,48 +503,34 @@ namespace MWWorld
} }
const ESM::Cell *Store<ESM::Cell>::search(int x, int y) const const ESM::Cell *Store<ESM::Cell>::search(int x, int y) const
{ {
ESM::Cell cell;
cell.mData.mX = x;
cell.mData.mY = y;
std::pair<int, int> key(x, y); std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key); DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) { if (it != mExt.end())
return &(it->second); return &(it->second);
}
DynamicExt::const_iterator dit = mDynamicExt.find(key); DynamicExt::const_iterator dit = mDynamicExt.find(key);
if (dit != mDynamicExt.end()) { if (dit != mDynamicExt.end())
return &dit->second; return &dit->second;
}
return nullptr; return nullptr;
} }
const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const const ESM::Cell *Store<ESM::Cell>::searchStatic(int x, int y) const
{ {
ESM::Cell cell; DynamicExt::const_iterator it = mExt.find(std::make_pair(x,y));
cell.mData.mX = x; if (it != mExt.end())
cell.mData.mY = y;
std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) {
return &(it->second); return &(it->second);
}
return nullptr; return nullptr;
} }
const ESM::Cell *Store<ESM::Cell>::searchOrCreate(int x, int y) const ESM::Cell *Store<ESM::Cell>::searchOrCreate(int x, int y)
{ {
std::pair<int, int> key(x, y); std::pair<int, int> key(x, y);
DynamicExt::const_iterator it = mExt.find(key); DynamicExt::const_iterator it = mExt.find(key);
if (it != mExt.end()) { if (it != mExt.end())
return &(it->second); return &(it->second);
}
DynamicExt::const_iterator dit = mDynamicExt.find(key); DynamicExt::const_iterator dit = mDynamicExt.find(key);
if (dit != mDynamicExt.end()) { if (dit != mDynamicExt.end())
return &dit->second; return &dit->second;
}
ESM::Cell newCell; ESM::Cell newCell;
newCell.mData.mX = x; newCell.mData.mX = x;
@ -625,12 +593,11 @@ namespace MWWorld
// Load the (x,y) coordinates of the cell, if it is an exterior cell, // Load the (x,y) coordinates of the cell, if it is an exterior cell,
// so we can find the cell we need to merge with // so we can find the cell we need to merge with
cell.loadNameAndData(esm, isDeleted); cell.loadNameAndData(esm, isDeleted);
std::string idLower = Misc::StringUtils::lowerCase(cell.mName);
if(cell.mData.mFlags & ESM::Cell::Interior) if(cell.mData.mFlags & ESM::Cell::Interior)
{ {
// Store interior cell by name, try to merge with existing parent data. // Store interior cell by name, try to merge with existing parent data.
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower)); ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell.mName));
if (oldcell) { if (oldcell) {
// merge new cell into old cell // merge new cell into old cell
// push the new references on the list of references to manage (saveContext = true) // push the new references on the list of references to manage (saveContext = true)
@ -642,7 +609,7 @@ namespace MWWorld
// spawn a new cell // spawn a new cell
cell.loadCell(esm, true); cell.loadCell(esm, true);
mInt[idLower] = cell; mInt[cell.mName] = cell;
} }
} }
else else
@ -780,27 +747,19 @@ namespace MWWorld
const std::string cellType = (cell.isExterior()) ? "exterior" : "interior"; const std::string cellType = (cell.isExterior()) ? "exterior" : "interior";
throw std::runtime_error("Failed to create " + cellType + " cell"); throw std::runtime_error("Failed to create " + cellType + " cell");
} }
ESM::Cell *ptr;
if (cell.isExterior()) { if (cell.isExterior()) {
std::pair<int, int> key(cell.getGridX(), cell.getGridY()); std::pair<int, int> key(cell.getGridX(), cell.getGridY());
// duplicate insertions are avoided by search(ESM::Cell &) // duplicate insertions are avoided by search(ESM::Cell &)
std::pair<DynamicExt::iterator, bool> result = DynamicExt::iterator result = mDynamicExt.emplace(key, cell).first;
mDynamicExt.insert(std::make_pair(key, cell)); mSharedExt.push_back(&result->second);
return &result->second;
ptr = &result.first->second;
mSharedExt.push_back(ptr);
} else { } else {
std::string key = Misc::StringUtils::lowerCase(cell.mName);
// duplicate insertions are avoided by search(ESM::Cell &) // duplicate insertions are avoided by search(ESM::Cell &)
std::pair<DynamicInt::iterator, bool> result = DynamicInt::iterator result = mDynamicInt.emplace(cell.mName, cell).first;
mDynamicInt.insert(std::make_pair(key, cell)); mSharedInt.push_back(&result->second);
return &result->second;
ptr = &result.first->second;
mSharedInt.push_back(ptr);
} }
return ptr;
} }
bool Store<ESM::Cell>::erase(const ESM::Cell &cell) bool Store<ESM::Cell>::erase(const ESM::Cell &cell)
{ {
@ -811,8 +770,7 @@ namespace MWWorld
} }
bool Store<ESM::Cell>::erase(const std::string &id) bool Store<ESM::Cell>::erase(const std::string &id)
{ {
std::string key = Misc::StringUtils::lowerCase(id); DynamicInt::iterator it = mDynamicInt.find(id);
DynamicInt::iterator it = mDynamicInt.find(key);
if (it == mDynamicInt.end()) { if (it == mDynamicInt.end()) {
return false; return false;
@ -1062,12 +1020,11 @@ namespace MWWorld
dialogue.loadId(esm); dialogue.loadId(esm);
std::string idLower = Misc::StringUtils::lowerCase(dialogue.mId); Static::iterator found = mStatic.find(dialogue.mId);
std::map<std::string, ESM::Dialogue>::iterator found = mStatic.find(idLower);
if (found == mStatic.end()) if (found == mStatic.end())
{ {
dialogue.loadData(esm, isDeleted); dialogue.loadData(esm, isDeleted);
mStatic.insert(std::make_pair(idLower, dialogue)); mStatic.emplace(dialogue.mId, dialogue);
} }
else else
{ {
@ -1081,11 +1038,7 @@ namespace MWWorld
template<> template<>
bool Store<ESM::Dialogue>::eraseStatic(const std::string &id) bool Store<ESM::Dialogue>::eraseStatic(const std::string &id)
{ {
auto it = mStatic.find(Misc::StringUtils::lowerCase(id)); mStatic.erase(id);
if (it != mStatic.end())
mStatic.erase(it);
return true; return true;
} }

View file

@ -5,6 +5,7 @@
#include <vector> #include <vector>
#include <memory> #include <memory>
#include <map> #include <map>
#include <unordered_map>
#include <set> #include <set>
#include "recordcmp.hpp" #include "recordcmp.hpp"
@ -147,14 +148,13 @@ namespace MWWorld
template <class T> template <class T>
class Store : public StoreBase class Store : public StoreBase
{ {
std::map<std::string, T> mStatic; typedef std::unordered_map<std::string, T, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Static;
std::vector<T *> mShared; // Preserves the record order as it came from the content files (this Static mStatic;
std::vector<T*> mShared; // Preserves the record order as it came from the content files (this
// is relevant for the spell autocalc code and selection order // is relevant for the spell autocalc code and selection order
// for heads/hairs in the character creation) // for heads/hairs in the character creation)
std::map<std::string, T> mDynamic; typedef std::unordered_map<std::string, T, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Dynamic;
Dynamic mDynamic;
typedef std::map<std::string, T> Dynamic;
typedef std::map<std::string, T> Static;
friend class ESMStore; friend class ESMStore;
@ -294,7 +294,7 @@ namespace MWWorld
} }
}; };
typedef std::map<std::string, ESM::Cell> DynamicInt; typedef std::unordered_map<std::string, ESM::Cell, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> DynamicInt;
typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp> DynamicExt; typedef std::map<std::pair<int, int>, ESM::Cell, DynamicExtCmp> DynamicExt;
DynamicInt mInt; DynamicInt mInt;
@ -354,7 +354,7 @@ namespace MWWorld
class Store<ESM::Pathgrid> : public StoreBase class Store<ESM::Pathgrid> : public StoreBase
{ {
private: private:
typedef std::map<std::string, ESM::Pathgrid> Interior; typedef std::unordered_map<std::string, ESM::Pathgrid, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual> Interior;
typedef std::map<std::pair<int, int>, ESM::Pathgrid> Exterior; typedef std::map<std::pair<int, int>, ESM::Pathgrid> Exterior;
Interior mInt; Interior mInt;