diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index bf1db8030c..39c0420333 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -236,7 +236,7 @@ CSMDoc::CollectionReferencesStage::CollectionReferencesStage(Document& document, int CSMDoc::CollectionReferencesStage::setup() { - mState.getSubRecords().clear(); + mState.clearSubRecords(); int size = mDocument.getData().getReferences().getSize(); @@ -260,7 +260,7 @@ void CSMDoc::CollectionReferencesStage::perform(int stage, Messages& messages) const ESM::RefId& cellId = record.get().mOriginalCell.empty() ? record.get().mCell : record.get().mOriginalCell; - std::deque& indices = mState.getSubRecords()[cellId.getRefIdString()]; + std::deque& indices = mState.getOrInsertSubRecord(cellId); // collect moved references at the end of the container const bool interior = !cellId.startsWith("#"); @@ -360,11 +360,9 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) std::deque tempRefs; std::deque persistentRefs; - std::map>::const_iterator references - = mState.getSubRecords().find(cell.get().mId.getRefIdString()); + const std::deque* references = mState.findSubRecord(cell.get().mId); - if (cell.isModified() || cell.mState == CSMWorld::RecordBase::State_Deleted - || references != mState.getSubRecords().end()) + if (cell.isModified() || cell.mState == CSMWorld::RecordBase::State_Deleted || references != nullptr) { CSMWorld::Cell cellRecord = cell.get(); const bool interior = !cellRecord.mId.startsWith("#"); @@ -372,10 +370,9 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) // count new references and adjust RefNumCount accordingsly unsigned int newRefNum = cellRecord.mRefNumCounter; - if (references != mState.getSubRecords().end()) + if (references != nullptr) { - for (std::deque::const_iterator iter(references->second.begin()); iter != references->second.end(); - ++iter) + for (std::deque::const_iterator iter(references->begin()); iter != references->end(); ++iter) { const CSMWorld::Record& ref = mDocument.getData().getReferences().getRecord(*iter); @@ -421,10 +418,10 @@ void CSMDoc::WriteCellCollectionStage::perform(int stage, Messages& messages) cellRecord.save(writer, cell.mState == CSMWorld::RecordBase::State_Deleted); // write references - if (references != mState.getSubRecords().end()) + if (references != nullptr) { writeReferences(persistentRefs, interior, newRefNum); - cellRecord.saveTempMarker(writer, int(references->second.size()) - persistentRefs.size()); + cellRecord.saveTempMarker(writer, static_cast(references->size()) - persistentRefs.size()); writeReferences(tempRefs, interior, newRefNum); } diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index 169f572154..aec2d0fd9b 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -68,7 +68,20 @@ bool CSMDoc::SavingState::isProjectFile() const return mProjectFile; } -std::map, Misc::StringUtils::CiComp>& CSMDoc::SavingState::getSubRecords() +const std::deque* CSMDoc::SavingState::findSubRecord(const ESM::RefId& refId) const { - return mSubRecords; + const auto it = mSubRecords.find(refId); + if (it == mSubRecords.end()) + return nullptr; + return &it->second; +} + +std::deque& CSMDoc::SavingState::getOrInsertSubRecord(const ESM::RefId& refId) +{ + return mSubRecords[refId]; +} + +void CSMDoc::SavingState::clearSubRecords() +{ + mSubRecords.clear(); } diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index daa65ef815..c42dff0366 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -8,9 +8,9 @@ #include #include - #include #include + namespace CSMDoc { class Operation; @@ -26,7 +26,7 @@ namespace CSMDoc ESM::ESMWriter mWriter; std::filesystem::path mProjectPath; bool mProjectFile; - std::map, Misc::StringUtils::CiComp> mSubRecords; // record ID, list of subrecords + std::map> mSubRecords; // record ID, list of subrecords public: SavingState(Operation& operation, std::filesystem::path projectPath, ToUTF8::FromType encoding); @@ -47,7 +47,11 @@ namespace CSMDoc bool isProjectFile() const; ///< Currently saving project file? (instead of content file) - std::map, Misc::StringUtils::CiComp>& getSubRecords(); + const std::deque* findSubRecord(const ESM::RefId& refId) const; + + std::deque& getOrInsertSubRecord(const ESM::RefId& refId); + + void clearSubRecords(); }; } diff --git a/apps/opencs/model/tools/mergestages.cpp b/apps/opencs/model/tools/mergestages.cpp index e11be72d4e..5a7fa6c1b9 100644 --- a/apps/opencs/model/tools/mergestages.cpp +++ b/apps/opencs/model/tools/mergestages.cpp @@ -15,7 +15,6 @@ #include #include -#include #include "mergestate.hpp" @@ -117,7 +116,7 @@ void CSMTools::MergeReferencesStage::perform(int stage, CSMDoc::Messages& messag ref.mOriginalCell = ref.mCell; - ref.mRefNum.mIndex = mIndex[Misc::StringUtils::lowerCase(ref.mCell.getRefIdString())]++; + ref.mRefNum.mIndex = mIndex[ref.mCell]++; ref.mRefNum.mContentFile = 0; ref.mNew = false; diff --git a/apps/opencs/model/tools/mergestages.hpp b/apps/opencs/model/tools/mergestages.hpp index 0763398e9d..42f06858b1 100644 --- a/apps/opencs/model/tools/mergestages.hpp +++ b/apps/opencs/model/tools/mergestages.hpp @@ -113,7 +113,7 @@ namespace CSMTools class MergeReferencesStage : public CSMDoc::Stage { MergeState& mState; - std::map mIndex; + std::map mIndex; public: MergeReferencesStage(MergeState& state); diff --git a/apps/opencs/model/world/actoradapter.cpp b/apps/opencs/model/world/actoradapter.cpp index 31b94d4e0b..4dabe1caae 100644 --- a/apps/opencs/model/world/actoradapter.cpp +++ b/apps/opencs/model/world/actoradapter.cpp @@ -136,7 +136,7 @@ namespace CSMWorld return SceneUtil::getActorSkeleton(firstPerson, mFemale, beast, werewolf); } - std::string_view ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const + ESM::RefId ActorAdapter::ActorData::getPart(ESM::PartReferenceType index) const { auto it = mParts.find(index); if (it == mParts.end()) @@ -146,12 +146,11 @@ namespace CSMWorld if (mFemale) { // Note: we should use male parts for females as fallback - const std::string& femalePart = mRaceData->getFemalePart(index).getRefIdString(); - if (!femalePart.empty()) + if (const ESM::RefId femalePart = mRaceData->getFemalePart(index); !femalePart.empty()) return femalePart; } - return mRaceData->getMalePart(index).getRefIdString(); + return mRaceData->getMalePart(index); } return {}; @@ -174,7 +173,7 @@ namespace CSMWorld return; } - mParts[index] = std::make_pair(partId.getRefIdString(), priority); + mParts[index] = std::make_pair(partId, priority); addOtherDependency(partId); } diff --git a/apps/opencs/model/world/actoradapter.hpp b/apps/opencs/model/world/actoradapter.hpp index 81bef223e3..8d703a6bae 100644 --- a/apps/opencs/model/world/actoradapter.hpp +++ b/apps/opencs/model/world/actoradapter.hpp @@ -35,7 +35,7 @@ namespace CSMWorld Q_OBJECT public: /// A list indexed by ESM::PartReferenceType - using ActorPartList = std::map>; + using ActorPartList = std::map>; /// A list indexed by ESM::BodyPart::MeshPart using RacePartList = std::array; /// Tracks unique strings @@ -95,7 +95,7 @@ namespace CSMWorld /// Returns the skeleton the actor should use for attaching parts to std::string getSkeleton() const; /// Retrieves the associated actor part - std::string_view getPart(ESM::PartReferenceType index) const; + ESM::RefId getPart(ESM::PartReferenceType index) const; /// Checks if the actor has a data dependency bool hasDependency(const ESM::RefId& id) const; diff --git a/apps/opencs/model/world/cellcoordinates.cpp b/apps/opencs/model/world/cellcoordinates.cpp index a82d4649a6..bb5c73fc54 100644 --- a/apps/opencs/model/world/cellcoordinates.cpp +++ b/apps/opencs/model/world/cellcoordinates.cpp @@ -6,6 +6,7 @@ #include +#include #include #include @@ -59,6 +60,11 @@ bool CSMWorld::CellCoordinates::isExteriorCell(const std::string& id) return (!id.empty() && id[0] == '#'); } +bool CSMWorld::CellCoordinates::isExteriorCell(const ESM::RefId& id) +{ + return id.startsWith("#"); +} + std::pair CSMWorld::CellCoordinates::fromId(const std::string& id) { // no worldspace for now, needs to be changed for 1.1 diff --git a/apps/opencs/model/world/cellcoordinates.hpp b/apps/opencs/model/world/cellcoordinates.hpp index 0be856e7f0..d6359ffa52 100644 --- a/apps/opencs/model/world/cellcoordinates.hpp +++ b/apps/opencs/model/world/cellcoordinates.hpp @@ -12,6 +12,11 @@ namespace osg class Vec3d; } +namespace ESM +{ + class RefId; +} + namespace CSMWorld { class CellCoordinates @@ -41,6 +46,8 @@ namespace CSMWorld static bool isExteriorCell(const std::string& id); + static bool isExteriorCell(const ESM::RefId& id); + /// \return first: CellCoordinates (or 0, 0 if cell does not have coordinates), /// second: is cell paged? /// diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index ad8d3690b8..0ec7142d2a 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -91,10 +91,10 @@ namespace CSMWorld /// /// \return Success? - int cloneRecordImp(const std::string& origin, const std::string& dest, UniversalId::Type type); + int cloneRecordImp(const ESM::RefId& origin, const ESM::RefId& dest, UniversalId::Type type); ///< Returns the index of the clone. - int touchRecordImp(const std::string& id); + int touchRecordImp(const ESM::RefId& id); ///< Returns the index of the record on success, -1 on failure. public: @@ -227,29 +227,28 @@ namespace CSMWorld template int Collection::cloneRecordImp( - const std::string& origin, const std::string& destination, UniversalId::Type type) + const ESM::RefId& origin, const ESM::RefId& destination, UniversalId::Type type) { auto copy = std::make_unique>(); - copy->mModified = getRecord(ESM::RefId::stringRefId(origin)).get(); + copy->mModified = getRecord(origin).get(); copy->mState = RecordBase::State_ModifiedOnly; - setRecordId(ESM::RefId::stringRefId(destination), copy->get()); + setRecordId(destination, copy->get()); if (type == UniversalId::Type_Reference) { CSMWorld::CellRef* ptr = (CSMWorld::CellRef*)©->mModified; ptr->mRefNum.mIndex = 0; } - ESM::RefId destinationRefId = ESM::RefId::stringRefId(destination); - int index = getAppendIndex(destinationRefId, type); - insertRecord(std::move(copy), getAppendIndex(destinationRefId, type)); + const int index = getAppendIndex(destination, type); + insertRecord(std::move(copy), getAppendIndex(destination, type)); return index; } template - int Collection::touchRecordImp(const std::string& id) + int Collection::touchRecordImp(const ESM::RefId& id) { - int index = getIndex(ESM::RefId::stringRefId(id)); + const int index = getIndex(id); Record& record = *mRecords.at(index); if (record.isDeleted()) { @@ -269,27 +268,27 @@ namespace CSMWorld void Collection::cloneRecord( const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) { - cloneRecordImp(origin.getRefIdString(), destination.getRefIdString(), type); + cloneRecordImp(origin, destination, type); } template <> inline void Collection::cloneRecord( const ESM::RefId& origin, const ESM::RefId& destination, const UniversalId::Type type) { - int index = cloneRecordImp(origin.getRefIdString(), destination.getRefIdString(), type); + const int index = cloneRecordImp(origin, destination, type); mRecords.at(index)->get().setPlugin(0); } template bool Collection::touchRecord(const ESM::RefId& id) { - return touchRecordImp(id.getRefIdString()) != -1; + return touchRecordImp(id) != -1; } template <> inline bool Collection::touchRecord(const ESM::RefId& id) { - int index = touchRecordImp(id.getRefIdString()); + const int index = touchRecordImp(id); if (index >= 0) { mRecords.at(index)->get().setPlugin(0); diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index 71b27cc812..b211477caf 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -113,11 +113,7 @@ void CSMWorld::CommandDispatcher::setEditLock(bool locked) void CSMWorld::CommandDispatcher::setSelection(const std::vector& selection) { mSelection = selection; - for (auto& sel : mSelection) - { - Misc::StringUtils::lowerCaseInPlace(sel); - } - std::sort(mSelection.begin(), mSelection.end()); + std::sort(mSelection.begin(), mSelection.end(), Misc::StringUtils::CiComp{}); } void CSMWorld::CommandDispatcher::setExtendedTypes(const std::vector& types) @@ -290,8 +286,7 @@ void CSMWorld::CommandDispatcher::executeExtendedDelete() if (record.mState == RecordBase::State_Deleted) continue; - if (!std::binary_search(mSelection.begin(), mSelection.end(), - Misc::StringUtils::lowerCase(record.get().mCell.getRefIdString()))) + if (!std::binary_search(mSelection.begin(), mSelection.end(), record.get().mCell)) continue; macro.push(new CSMWorld::DeleteCommand(model, record.get().mId.getRefIdString())); @@ -321,8 +316,7 @@ void CSMWorld::CommandDispatcher::executeExtendedRevert() { const Record& record = collection.getRecord(i); - if (!std::binary_search(mSelection.begin(), mSelection.end(), - Misc::StringUtils::lowerCase(record.get().mCell.getRefIdString()))) + if (!std::binary_search(mSelection.begin(), mSelection.end(), record.get().mCell)) continue; macro.push(new CSMWorld::RevertCommand(model, record.get().mId.getRefIdString())); diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 09296b55d6..a6268c1a48 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -1209,9 +1209,8 @@ bool CSMWorld::Data::continueLoading(CSMDoc::Messages& messages) messages.add(id, "Logic error: cell index out of bounds", "", CSMDoc::Message::Severity_Error); index = mCells.getSize() - 1; } - const std::string cellId = mCells.getId(index).getRefIdString(); - mRefs.load(*mReader, index, mBase, mRefLoadCache[cellId], messages); + mRefs.load(*mReader, index, mBase, mRefLoadCache[mCells.getId(index)], messages); break; } diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 6db992cca6..42c5309723 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -123,7 +123,7 @@ namespace CSMWorld const ESM::Dialogue* mDialogue; // last loaded dialogue bool mBase; bool mProject; - std::map, Misc::StringUtils::CiComp> mRefLoadCache; + std::map> mRefLoadCache; int mReaderIndex; bool mFsStrict; diff --git a/apps/opencs/model/world/scriptcontext.cpp b/apps/opencs/model/world/scriptcontext.cpp index 7f554a75af..ba70127ad3 100644 --- a/apps/opencs/model/world/scriptcontext.cpp +++ b/apps/opencs/model/world/scriptcontext.cpp @@ -85,7 +85,7 @@ std::pair CSMWorld::ScriptContext::getMemberType(const std::string& if (index == -1) return std::make_pair(' ', false); - std::map::iterator iter = mLocals.find(id2.getRefIdString()); + auto iter = mLocals.find(id2); if (iter == mLocals.end()) { @@ -97,7 +97,7 @@ std::pair CSMWorld::ScriptContext::getMemberType(const std::string& Compiler::Scanner scanner(errorHandler, stream, getExtensions()); scanner.scan(parser); - iter = mLocals.insert(std::make_pair(id2.getRefIdString(), locals)).first; + iter = mLocals.emplace(id2, std::move(locals)).first; } return std::make_pair(iter->second.getType(Misc::StringUtils::lowerCase(name)), reference); @@ -131,7 +131,7 @@ void CSMWorld::ScriptContext::clear() bool CSMWorld::ScriptContext::clearLocals(const std::string& script) { - std::map::iterator iter = mLocals.find(script); + const auto iter = mLocals.find(script); if (iter != mLocals.end()) { diff --git a/apps/opencs/model/world/scriptcontext.hpp b/apps/opencs/model/world/scriptcontext.hpp index 523a2bf3ac..c2dbadfc6f 100644 --- a/apps/opencs/model/world/scriptcontext.hpp +++ b/apps/opencs/model/world/scriptcontext.hpp @@ -19,7 +19,7 @@ namespace CSMWorld const Data& mData; mutable std::vector mIds; mutable bool mIdsUpdated; - mutable std::map mLocals; + mutable std::map> mLocals; public: ScriptContext(const Data& data); diff --git a/apps/opencs/view/render/actor.cpp b/apps/opencs/view/render/actor.cpp index 803299bd25..d1bfac0ec6 100644 --- a/apps/opencs/view/render/actor.cpp +++ b/apps/opencs/view/render/actor.cpp @@ -104,9 +104,8 @@ namespace CSVRender { for (int i = 0; i < ESM::PRT_Count; ++i) { - auto type = (ESM::PartReferenceType)i; - const std::string_view partId = mActorData->getPart(type); - attachBodyPart(type, getBodyPartMesh(partId)); + const auto type = static_cast(i); + attachBodyPart(type, getBodyPartMesh(mActorData->getPart(type))); } } @@ -124,11 +123,11 @@ namespace CSVRender } } - std::string Actor::getBodyPartMesh(std::string_view bodyPartId) + std::string Actor::getBodyPartMesh(const ESM::RefId& bodyPartId) { const auto& bodyParts = mData.getBodyParts(); - const int index = bodyParts.searchId(ESM::RefId::stringRefId(bodyPartId)); + const int index = bodyParts.searchId(bodyPartId); if (index != -1 && !bodyParts.getRecord(index).isDeleted()) return MeshPrefix + bodyParts.getRecord(index).get().mModel; else diff --git a/apps/opencs/view/render/actor.hpp b/apps/opencs/view/render/actor.hpp index 7fe14e4a20..86c7e7ff2d 100644 --- a/apps/opencs/view/render/actor.hpp +++ b/apps/opencs/view/render/actor.hpp @@ -51,7 +51,7 @@ namespace CSVRender void loadBodyParts(); void attachBodyPart(ESM::PartReferenceType, const std::string& mesh); - std::string getBodyPartMesh(std::string_view bodyPartId); + std::string getBodyPartMesh(const ESM::RefId& bodyPartId); static const std::string MeshPrefix; diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp index 4887d0ef3d..7782ce36c9 100644 --- a/apps/opencs/view/render/object.cpp +++ b/apps/opencs/view/render/object.cpp @@ -737,7 +737,7 @@ void CSVRender::Object::apply(CSMWorld::CommandMacro& commands) // Do cell check first so positions can be compared const CSMWorld::CellRef& ref = collection.getRecord(recordIndex).get(); - if (CSMWorld::CellCoordinates::isExteriorCell(ref.mCell.getRefIdString())) + if (CSMWorld::CellCoordinates::isExteriorCell(ref.mCell)) { // Find cell index at new position std::pair cellIndex diff --git a/apps/openmw/mwworld/globals.cpp b/apps/openmw/mwworld/globals.cpp index 733270b06c..264109d17f 100644 --- a/apps/openmw/mwworld/globals.cpp +++ b/apps/openmw/mwworld/globals.cpp @@ -37,7 +37,7 @@ namespace MWWorld for (const ESM::Global& esmGlobal : globals) { - mVariables.emplace(esmGlobal.mId.getRefIdString(), esmGlobal); + mVariables.emplace(esmGlobal.mId, esmGlobal); } } @@ -98,8 +98,7 @@ namespace MWWorld // Deleted globals can't appear there, so isDeleted will be ignored here. global.load(reader, isDeleted); - Collection::iterator iter = mVariables.find(global.mId.getRefIdString()); - if (iter != mVariables.end()) + if (const auto iter = mVariables.find(global.mId); iter != mVariables.end()) iter->second = global; return true; diff --git a/apps/openmw/mwworld/globals.hpp b/apps/openmw/mwworld/globals.hpp index f53f71e845..ab94f0f8dd 100644 --- a/apps/openmw/mwworld/globals.hpp +++ b/apps/openmw/mwworld/globals.hpp @@ -2,8 +2,8 @@ #define GAME_MWWORLD_GLOBALS_H #include +#include #include -#include #include #include @@ -29,8 +29,7 @@ namespace MWWorld class Globals { private: - using Collection - = std::unordered_map; + using Collection = std::map>; Collection mVariables; // type, value diff --git a/apps/openmw_test_suite/CMakeLists.txt b/apps/openmw_test_suite/CMakeLists.txt index 4fe4cde9d0..ae5723cd25 100644 --- a/apps/openmw_test_suite/CMakeLists.txt +++ b/apps/openmw_test_suite/CMakeLists.txt @@ -23,6 +23,7 @@ file(GLOB UNITTEST_SRC_FILES esm/test_fixed_string.cpp esm/variant.cpp + esm/testrefid.cpp lua/test_lua.cpp lua/test_scriptscontainer.cpp diff --git a/apps/openmw_test_suite/esm/testrefid.cpp b/apps/openmw_test_suite/esm/testrefid.cpp new file mode 100644 index 0000000000..4083046d15 --- /dev/null +++ b/apps/openmw_test_suite/esm/testrefid.cpp @@ -0,0 +1,148 @@ +#include + +#include + +#include +#include +#include + +namespace ESM +{ + namespace + { + using namespace ::testing; + + TEST(ESMRefIdTest, defaultConstructedIsEmpty) + { + const RefId refId; + EXPECT_TRUE(refId.empty()); + } + + TEST(ESMRefIdTest, stringRefIdIsNotEmpty) + { + const RefId refId = RefId::stringRefId("ref_id"); + EXPECT_FALSE(refId.empty()); + } + + TEST(ESMRefIdTest, formIdRefIdIsNotEmpty) + { + const RefId refId = RefId::formIdRefId(42); + EXPECT_FALSE(refId.empty()); + } + + TEST(ESMRefIdTest, defaultConstructedIsEqualToItself) + { + const RefId refId; + EXPECT_EQ(refId, refId); + } + + TEST(ESMRefIdTest, defaultConstructedIsEqualToDefaultConstructed) + { + const RefId a; + const RefId b; + EXPECT_EQ(a, b); + } + + TEST(ESMRefIdTest, defaultConstructedIsNotEqualToDebugStringRefId) + { + const RefId a; + const RefId b = RefId::stringRefId("b"); + EXPECT_NE(a, b); + } + + TEST(ESMRefIdTest, defaultConstructedIsNotEqualToFormIdRefId) + { + const RefId a; + const RefId b = RefId::formIdRefId(42); + EXPECT_NE(a, b); + } + + TEST(ESMRefIdTest, defaultConstructedIsNotEqualToDebugStringLiteral) + { + const RefId a; + EXPECT_NE(a, "foo"); + } + + TEST(ESMRefIdTest, stringRefIdIsEqualToTheSameStringLiteralValue) + { + const RefId refId = RefId::stringRefId("ref_id"); + EXPECT_EQ(refId, "ref_id"); + } + + TEST(ESMRefIdTest, stringRefIdIsCaseInsensitiveEqualToTheSameStringLiteralValue) + { + const RefId refId = RefId::stringRefId("ref_id"); + EXPECT_EQ(refId, "REF_ID"); + } + + TEST(ESMRefIdTest, stringRefIdIsEqualToTheSameStringRefId) + { + const RefId a = RefId::stringRefId("ref_id"); + const RefId b = RefId::stringRefId("ref_id"); + EXPECT_EQ(a, b); + } + + TEST(ESMRefIdTest, stringRefIdIsCaseInsensitiveEqualToTheSameStringRefId) + { + const RefId lower = RefId::stringRefId("ref_id"); + const RefId upper = RefId::stringRefId("REF_ID"); + EXPECT_EQ(lower, upper); + } + + TEST(ESMRefIdTest, stringRefIdIsEqualToItself) + { + const RefId refId = RefId::stringRefId("ref_id"); + EXPECT_EQ(refId, refId); + } + + TEST(ESMRefIdTest, stringRefIdIsCaseInsensitiveLessByContent) + { + const RefId a = RefId::stringRefId("a"); + const RefId b = RefId::stringRefId("B"); + EXPECT_LT(a, b); + } + + TEST(ESMRefIdTest, stringRefIdHasCaseInsensitiveHash) + { + const RefId lower = RefId::stringRefId("a"); + const RefId upper = RefId::stringRefId("A"); + const std::hash hash; + EXPECT_EQ(hash(lower), hash(upper)); + } + + TEST(ESMRefIdTest, hasCaseInsensitiveEqualityWithStringView) + { + const RefId a = RefId::stringRefId("a"); + const std::string_view b = "A"; + EXPECT_EQ(a, b); + } + + TEST(ESMRefIdTest, hasCaseInsensitiveLessWithStringView) + { + const RefId a = RefId::stringRefId("a"); + const std::string_view b = "B"; + EXPECT_LT(a, b); + } + + TEST(ESMRefIdTest, hasCaseInsensitiveStrongOrderWithStringView) + { + const RefId a = RefId::stringRefId("a"); + const std::string_view b = "B"; + const RefId c = RefId::stringRefId("c"); + EXPECT_LT(a, b); + EXPECT_LT(b, c); + } + + TEST(ESMRefIdTest, canBeUsedAsMapKeyWithLookupByStringView) + { + const std::map> map({ { ESM::RefId::stringRefId("a"), 42 } }); + EXPECT_EQ(map.count("A"), 1); + } + + TEST(ESMRefIdTest, canBeUsedAsLookupKeyForMapWithStringKey) + { + const std::map> map({ { "a", 42 } }); + EXPECT_EQ(map.count(ESM::RefId::stringRefId("A")), 1); + } + } +} diff --git a/apps/openmw_test_suite/mwscript/test_utils.hpp b/apps/openmw_test_suite/mwscript/test_utils.hpp index 6f930ba7b3..2680bfbb66 100644 --- a/apps/openmw_test_suite/mwscript/test_utils.hpp +++ b/apps/openmw_test_suite/mwscript/test_utils.hpp @@ -144,7 +144,7 @@ namespace class TestInterpreterContext : public Interpreter::Context { LocalVariables mLocals; - std::map mMembers; + std::map mMembers; public: const ESM::RefId& getTarget() const override { return ESM::RefId::sEmpty; } @@ -209,7 +209,7 @@ namespace int getMemberShort(const ESM::RefId& id, std::string_view name, bool global) const override { - auto it = mMembers.find(id.getRefIdString()); + auto it = mMembers.find(id); if (it != mMembers.end()) return it->second.getShort(name); return {}; @@ -217,7 +217,7 @@ namespace int getMemberLong(const ESM::RefId& id, std::string_view name, bool global) const override { - auto it = mMembers.find(id.getRefIdString()); + auto it = mMembers.find(id); if (it != mMembers.end()) return it->second.getLong(name); return {}; @@ -225,7 +225,7 @@ namespace float getMemberFloat(const ESM::RefId& id, std::string_view name, bool global) const override { - auto it = mMembers.find(id.getRefIdString()); + auto it = mMembers.find(id); if (it != mMembers.end()) return it->second.getFloat(name); return {}; @@ -233,17 +233,17 @@ namespace void setMemberShort(const ESM::RefId& id, std::string_view name, int value, bool global) override { - mMembers[id.getRefIdString()].setShort(name, value); + mMembers[id].setShort(name, value); } void setMemberLong(const ESM::RefId& id, std::string_view name, int value, bool global) override { - mMembers[id.getRefIdString()].setLong(name, value); + mMembers[id].setLong(name, value); } void setMemberFloat(const ESM::RefId& id, std::string_view name, float value, bool global) override { - mMembers[id.getRefIdString()].setFloat(name, value); + mMembers[id].setFloat(name, value); } }; diff --git a/components/esm/refid.cpp b/components/esm/refid.cpp index 27d3bb6d9d..a866ecb86e 100644 --- a/components/esm/refid.cpp +++ b/components/esm/refid.cpp @@ -16,9 +16,14 @@ namespace ESM return Misc::StringUtils::ciLess(mId, rhs.mId); } - bool RefId::operator<(std::string_view rhs) const + bool operator<(const RefId& lhs, std::string_view rhs) { - return Misc::StringUtils::ciLess(mId, rhs); + return Misc::StringUtils::ciLess(lhs.mId, rhs); + } + + bool operator<(std::string_view lhs, const RefId& rhs) + { + return Misc::StringUtils::ciLess(lhs, rhs.mId); } std::ostream& operator<<(std::ostream& os, const RefId& refId) diff --git a/components/esm/refid.hpp b/components/esm/refid.hpp index 0d297cfb0f..e7385e9c1b 100644 --- a/components/esm/refid.hpp +++ b/components/esm/refid.hpp @@ -39,7 +39,9 @@ namespace ESM bool operator<(const RefId& rhs) const; - bool operator<(std::string_view rhs) const; + friend bool operator<(const RefId& lhs, std::string_view rhs); + + friend bool operator<(std::string_view lhs, const RefId& rhs); friend std::ostream& operator<<(std::ostream& os, const RefId& dt);