mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-28 20:39:41 +00:00
Make ESM::RefId to be fixed size cheap to copy
Use std::variant. Store refId strings in unordered_set and use pointer to an item there. Inserts to unordered_set do not invalidate pointers to values so the pointer is always valid. Elements are not removed. Assume there is finite number of string refIds.
This commit is contained in:
parent
3a0443c472
commit
069d4255b9
30 changed files with 571 additions and 106 deletions
|
@ -25,5 +25,5 @@ int CSMTools::MandatoryIdStage::setup()
|
|||
void CSMTools::MandatoryIdStage::perform(int stage, CSMDoc::Messages& messages)
|
||||
{
|
||||
if (mIdCollection.searchId(mIds.at(stage)) == -1 || mIdCollection.getRecord(mIds.at(stage)).isDeleted())
|
||||
messages.add(mCollectionId, "Missing mandatory record: " + mIds.at(stage).getRefIdString());
|
||||
messages.add(mCollectionId, "Missing mandatory record: " + mIds.at(stage).toDebugString());
|
||||
}
|
||||
|
|
|
@ -658,7 +658,7 @@ int CSMWorld::RefIdCollection::getIndex(const ESM::RefId& id) const
|
|||
int index = searchId(id);
|
||||
|
||||
if (index == -1)
|
||||
throw std::runtime_error("invalid ID: " + id.getRefIdString());
|
||||
throw std::runtime_error("invalid ID: " + id.toDebugString());
|
||||
|
||||
return index;
|
||||
}
|
||||
|
|
|
@ -40,8 +40,7 @@ namespace MWDialogue
|
|||
return;
|
||||
}
|
||||
|
||||
throw std::runtime_error(
|
||||
"unknown info ID " + mInfoId.getRefIdString() + " for topic " + topic.getRefIdString());
|
||||
throw std::runtime_error("unknown info ID " + mInfoId.toDebugString() + " for topic " + topic.toDebugString());
|
||||
}
|
||||
|
||||
Entry::Entry(const ESM::JournalEntry& record)
|
||||
|
@ -98,7 +97,7 @@ namespace MWDialogue
|
|||
return iter->mId;
|
||||
}
|
||||
|
||||
throw std::runtime_error("unknown journal index for topic " + topic.getRefIdString());
|
||||
throw std::runtime_error("unknown journal index for topic " + topic.toDebugString());
|
||||
}
|
||||
|
||||
StampedJournalEntry::StampedJournalEntry()
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace MWDialogue
|
|||
[&](const auto& info) { return info.mId == entry.mInfoId; });
|
||||
|
||||
if (info == dialogue->mInfo.end() || info->mData.mJournalIndex == -1)
|
||||
throw std::runtime_error("unknown journal entry for topic " + mTopic.getRefIdString());
|
||||
throw std::runtime_error("unknown journal entry for topic " + mTopic.toDebugString());
|
||||
|
||||
if (info->mQuestStatus == ESM::DialInfo::QS_Finished || info->mQuestStatus == ESM::DialInfo::QS_Restart)
|
||||
mFinished = info->mQuestStatus == ESM::DialInfo::QS_Finished;
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace MWDialogue
|
|||
bool Topic::addEntry(const JournalEntry& entry)
|
||||
{
|
||||
if (entry.mTopic != mTopic)
|
||||
throw std::runtime_error("topic does not match: " + mTopic.getRefIdString());
|
||||
throw std::runtime_error("topic does not match: " + mTopic.toDebugString());
|
||||
|
||||
// bail out if we already have heard this
|
||||
for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)
|
||||
|
|
|
@ -896,7 +896,7 @@ namespace MWGui
|
|||
= Misc::ResourceHelpers::correctTexturePath("textures\\levelup\\" + classId.getRefIdString() + ".dds", vfs);
|
||||
if (!vfs->exists(classImage))
|
||||
{
|
||||
Log(Debug::Warning) << "No class image for " << classId.getRefIdString() << ", falling back to default";
|
||||
Log(Debug::Warning) << "No class image for " << classId << ", falling back to default";
|
||||
classImage = "textures\\levelup\\warrior.dds";
|
||||
}
|
||||
imageBox->setImageTexture(classImage);
|
||||
|
|
|
@ -683,7 +683,7 @@ namespace MWGui
|
|||
if (!mConsoleMode.empty())
|
||||
title = mConsoleMode + " " + title;
|
||||
if (!mPtr.isEmpty())
|
||||
title.append(" (" + mPtr.getCellRef().getRefId().getRefIdString() + ")");
|
||||
title.append(" (" + mPtr.getCellRef().getRefId().toDebugString() + ")");
|
||||
setTitle(title);
|
||||
}
|
||||
|
||||
|
|
|
@ -160,12 +160,7 @@ namespace
|
|||
if (result != 0)
|
||||
return result > 0;
|
||||
|
||||
// compare items by Id
|
||||
leftName = left.mBase.getCellRef().getRefId().getRefIdString();
|
||||
rightName = right.mBase.getCellRef().getRefId().getRefIdString();
|
||||
|
||||
result = leftName.compare(rightName);
|
||||
return result < 0;
|
||||
return left.mBase.getCellRef().getRefId() < right.mBase.getCellRef().getRefId();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ namespace MWGui
|
|||
ToolTipInfo info;
|
||||
info.caption = mFocusObject.getClass().getName(mFocusObject);
|
||||
if (info.caption.empty())
|
||||
info.caption = mFocusObject.getCellRef().getRefId().getRefIdString();
|
||||
info.caption = mFocusObject.getCellRef().getRefId().toDebugString();
|
||||
info.icon.clear();
|
||||
tooltipSize = createToolTip(info, checkOwned());
|
||||
}
|
||||
|
@ -686,7 +686,7 @@ namespace MWGui
|
|||
if (!creature)
|
||||
return {};
|
||||
if (creature->mName.empty())
|
||||
return " (" + creature->mId.getRefIdString() + ")";
|
||||
return " (" + creature->mId.toDebugString() + ")";
|
||||
return " (" + creature->mName + ")";
|
||||
}
|
||||
|
||||
|
@ -720,10 +720,10 @@ namespace MWGui
|
|||
for (std::pair<ESM::RefId, int>& owner : itemOwners)
|
||||
{
|
||||
if (owner.second == std::numeric_limits<int>::max())
|
||||
ret += std::string("\nStolen from ") + owner.first.getRefIdString(); // for legacy (ESS) savegames
|
||||
ret += std::string("\nStolen from ") + owner.first.toDebugString(); // for legacy (ESS) savegames
|
||||
else
|
||||
ret += std::string("\nStolen ") + MyGUI::utility::toString(owner.second) + " from "
|
||||
+ owner.first.getRefIdString();
|
||||
+ owner.first.toDebugString();
|
||||
}
|
||||
|
||||
ret += getMiscString(cellref.getGlobalVariable(), "Global");
|
||||
|
|
|
@ -167,7 +167,7 @@ namespace MWLua
|
|||
{
|
||||
objectT["isValid"] = [](const ObjectT& o) { return !o.ptrOrNull().isEmpty(); };
|
||||
objectT["recordId"] = sol::readonly_property(
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().getRefIdString(); });
|
||||
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().toDebugString(); });
|
||||
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
|
||||
const MWWorld::Ptr& ptr = o.ptr();
|
||||
if (ptr.isInCell())
|
||||
|
|
|
@ -49,7 +49,7 @@ namespace MWMechanics
|
|||
case ESM::REC_NPC_:
|
||||
return ::withBaseRecord<ESM::NPC>(mId, function);
|
||||
default:
|
||||
throw std::logic_error("failed to update base record for " + mId.getRefIdString());
|
||||
throw std::logic_error("failed to update base record for " + mId.toDebugString());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ namespace MWMechanics
|
|||
case ESM::REC_NPC_:
|
||||
return getSpellList<ESM::NPC>(mId);
|
||||
default:
|
||||
throw std::logic_error("failed to get spell list for " + mId.getRefIdString());
|
||||
throw std::logic_error("failed to get spell list for " + mId.toDebugString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -208,7 +208,7 @@ namespace MWScript
|
|||
return iter->second;
|
||||
}
|
||||
|
||||
throw std::logic_error("script " + name.getRefIdString() + " does not exist");
|
||||
throw std::logic_error("script " + name.toDebugString() + " does not exist");
|
||||
}
|
||||
|
||||
GlobalScripts& ScriptManager::getGlobalScripts()
|
||||
|
|
|
@ -65,8 +65,8 @@ namespace MWScript
|
|||
from = container;
|
||||
else
|
||||
{
|
||||
std::string error = "Failed to find the container of object '"
|
||||
+ from.getCellRef().getRefId().getRefIdString() + "'";
|
||||
const std::string error
|
||||
= "Failed to find the container of object " + from.getCellRef().getRefId().toDebugString();
|
||||
runtime.getContext().report(error);
|
||||
Log(Debug::Error) << error;
|
||||
runtime.push(0.f);
|
||||
|
@ -77,7 +77,7 @@ namespace MWScript
|
|||
const MWWorld::Ptr to = MWBase::Environment::get().getWorld()->searchPtr(name, false);
|
||||
if (to.isEmpty())
|
||||
{
|
||||
std::string error = "Failed to find an instance of object '" + name.getRefIdString() + "'";
|
||||
const std::string error = "Failed to find an instance of object " + name.toDebugString();
|
||||
runtime.getContext().report(error);
|
||||
Log(Debug::Error) << error;
|
||||
runtime.push(0.f);
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace MWWorld
|
|||
}
|
||||
|
||||
if (it == invStore.end())
|
||||
throw std::runtime_error("ActionEquip can't find item " + object.getCellRef().getRefId().getRefIdString());
|
||||
throw std::runtime_error("ActionEquip can't find item " + object.getCellRef().getRefId().toDebugString());
|
||||
|
||||
// equip the item in the first free slot
|
||||
std::vector<int>::const_iterator slot = slots_.first.begin();
|
||||
|
|
|
@ -950,13 +950,13 @@ namespace MWWorld
|
|||
}
|
||||
else
|
||||
{
|
||||
Log(Debug::Error) << "Cell reference '" + ref.mRefID.getRefIdString() + "' not found!";
|
||||
Log(Debug::Error) << "Cell reference " << ref.mRefID << " is not found!";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!handledType)
|
||||
{
|
||||
Log(Debug::Error) << "Error: Ignoring reference '" << ref.mRefID.getRefIdString() << "' of unhandled type";
|
||||
Log(Debug::Error) << "Error: Ignoring reference " << ref.mRefID << " of unhandled type";
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -783,7 +783,7 @@ int MWWorld::ContainerStore::getType(const ConstPtr& ptr)
|
|||
if (ptr.getType() == ESM::Weapon::sRecordId)
|
||||
return Type_Weapon;
|
||||
|
||||
throw std::runtime_error("Object '" + ptr.getCellRef().getRefId().getRefIdString() + "' of type "
|
||||
throw std::runtime_error("Object " + ptr.getCellRef().getRefId().toDebugString() + " of type "
|
||||
+ std::string(ptr.getTypeDescription()) + " can not be placed into a container");
|
||||
}
|
||||
|
||||
|
|
|
@ -731,10 +731,7 @@ namespace MWWorld
|
|||
}
|
||||
const ESM::RefId id = ESM::RefId::stringRefId("$dynamic" + std::to_string(mDynamicCount++));
|
||||
if (npcs.search(id) != nullptr)
|
||||
{
|
||||
const std::string msg = "Try to override existing record '" + id.getRefIdString() + "'";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
throw std::runtime_error("Try to override existing record: " + id.toDebugString());
|
||||
ESM::NPC record = npc;
|
||||
|
||||
record.mId = id;
|
||||
|
|
|
@ -189,10 +189,7 @@ namespace MWWorld
|
|||
|
||||
Store<T>& store = getWritable<T>();
|
||||
if (store.search(id) != nullptr)
|
||||
{
|
||||
const std::string msg = "Try to override existing record '" + id.getRefIdString() + "'";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
throw std::runtime_error("Try to override existing record: " + id.toDebugString());
|
||||
T record = x;
|
||||
|
||||
record.mId = id;
|
||||
|
|
|
@ -94,10 +94,10 @@ MWWorld::ManualRef::ManualRef(const MWWorld::ESMStore& store, const ESM::RefId&
|
|||
create(store.get<ESM4::Static>(), name, mRef, mPtr);
|
||||
break;
|
||||
case 0:
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown ID)");
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.toDebugString() + " (unknown ID)");
|
||||
|
||||
default:
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.getRefIdString() + " (unknown type)");
|
||||
throw std::logic_error("failed to create manual cell ref for " + name.toDebugString() + " (unknown type)");
|
||||
}
|
||||
|
||||
mPtr.getRefData().setCount(count);
|
||||
|
|
|
@ -16,7 +16,7 @@ namespace MWWorld
|
|||
res.append(" (");
|
||||
res.append(getTypeDescription());
|
||||
res.append(", ");
|
||||
res.append(getCellRef().getRefId().getRefIdString());
|
||||
res.append(getCellRef().getRefId().toDebugString());
|
||||
res.append(")");
|
||||
return res;
|
||||
}
|
||||
|
|
|
@ -950,11 +950,8 @@ namespace MWWorld
|
|||
const ESM::Pathgrid* Store<ESM::Pathgrid>::find(const ESM::RefId& name) const
|
||||
{
|
||||
const ESM::Pathgrid* pathgrid = search(name);
|
||||
if (!pathgrid)
|
||||
{
|
||||
const std::string msg = "Pathgrid in cell '" + name.getRefIdString() + "' not found";
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
if (pathgrid == nullptr)
|
||||
throw std::runtime_error("Pathgrid in cell " + name.toDebugString() + " is not found");
|
||||
return pathgrid;
|
||||
}
|
||||
const ESM::Pathgrid* Store<ESM::Pathgrid>::search(const ESM::Cell& cell) const
|
||||
|
|
|
@ -729,7 +729,7 @@ namespace MWWorld
|
|||
Ptr ret = searchPtr(name, activeOnly);
|
||||
if (!ret.isEmpty())
|
||||
return ret;
|
||||
std::string error = "failed to find an instance of object '" + name.getRefIdString() + "'";
|
||||
std::string error = "Failed to find an instance of object " + name.toDebugString();
|
||||
if (activeOnly)
|
||||
error += " in active cells";
|
||||
throw std::runtime_error(error);
|
||||
|
@ -1146,12 +1146,12 @@ namespace MWWorld
|
|||
MWWorld::Ptr newPtr = ptr;
|
||||
|
||||
if (!isPlayer && !currCell)
|
||||
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
||||
+ "\" to another cell: current cell is nullptr");
|
||||
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
|
||||
+ " to another cell: current cell is nullptr");
|
||||
|
||||
if (!newCell)
|
||||
throw std::runtime_error("Can not move actor \"" + ptr.getCellRef().getRefId().getRefIdString()
|
||||
+ "\" to another cell: new cell is nullptr");
|
||||
throw std::runtime_error("Can not move actor " + ptr.getCellRef().getRefId().toDebugString()
|
||||
+ " to another cell: new cell is nullptr");
|
||||
|
||||
if (currCell != newCell)
|
||||
{
|
||||
|
|
|
@ -89,6 +89,20 @@ namespace ESM
|
|||
EXPECT_EQ(lower, upper);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, equalityIsDefinedForStringRefIdAndRefId)
|
||||
{
|
||||
const StringRefId stringRefId("ref_id");
|
||||
const RefId refId = RefId::stringRefId("REF_ID");
|
||||
EXPECT_EQ(stringRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, equalityIsDefinedForFormRefIdAndRefId)
|
||||
{
|
||||
const FormIdRefId formIdRefId(42);
|
||||
const RefId refId = RefId::formIdRefId(42);
|
||||
EXPECT_EQ(formIdRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdIsEqualToItself)
|
||||
{
|
||||
const RefId refId = RefId::stringRefId("ref_id");
|
||||
|
@ -102,6 +116,20 @@ namespace ESM
|
|||
EXPECT_LT(a, b);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, lessThanIsDefinedForStringRefIdAndRefId)
|
||||
{
|
||||
const StringRefId stringRefId("a");
|
||||
const RefId refId = RefId::stringRefId("B");
|
||||
EXPECT_LT(stringRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, lessThanIsDefinedForFormRefIdAndRefId)
|
||||
{
|
||||
const FormIdRefId formIdRefId(13);
|
||||
const RefId refId = RefId::formIdRefId(42);
|
||||
EXPECT_LT(formIdRefId, refId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdHasCaseInsensitiveHash)
|
||||
{
|
||||
const RefId lower = RefId::stringRefId("a");
|
||||
|
@ -133,6 +161,22 @@ namespace ESM
|
|||
EXPECT_LT(b, c);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdHasStrongOrderWithFormId)
|
||||
{
|
||||
const RefId stringRefId = RefId::stringRefId("a");
|
||||
const RefId formIdRefId = RefId::formIdRefId(42);
|
||||
EXPECT_TRUE(stringRefId < formIdRefId);
|
||||
EXPECT_FALSE(formIdRefId < stringRefId);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, formIdRefIdHasStrongOrderWithStringView)
|
||||
{
|
||||
const RefId formIdRefId = RefId::formIdRefId(42);
|
||||
const std::string_view stringView = "42";
|
||||
EXPECT_TRUE(stringView < formIdRefId);
|
||||
EXPECT_FALSE(formIdRefId < stringView);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, canBeUsedAsMapKeyWithLookupByStringView)
|
||||
{
|
||||
const std::map<ESM::RefId, int, std::less<>> map({ { ESM::RefId::stringRefId("a"), 42 } });
|
||||
|
@ -144,5 +188,61 @@ namespace ESM
|
|||
const std::map<std::string, int, std::less<>> map({ { "a", 42 } });
|
||||
EXPECT_EQ(map.count(ESM::RefId::stringRefId("A")), 1);
|
||||
}
|
||||
|
||||
TEST(ESMRefIdTest, stringRefIdIsNotEqualToFormId)
|
||||
{
|
||||
const RefId stringRefId = RefId::stringRefId("\0");
|
||||
const RefId formIdRefId = RefId::formIdRefId(0);
|
||||
EXPECT_NE(stringRefId, formIdRefId);
|
||||
}
|
||||
|
||||
struct ESMRefIdToStringTest : TestWithParam<std::pair<ESM::RefId, std::string>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ESMRefIdToStringTest, toString)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& string = GetParam().second;
|
||||
EXPECT_EQ(refId.toString(), string);
|
||||
}
|
||||
|
||||
const std::vector<std::pair<RefId, std::string>> toStringParams = {
|
||||
{ RefId(), std::string() },
|
||||
{ RefId::stringRefId("foo"), "foo" },
|
||||
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), { 'a', 0, -1, '\n', '\t' } },
|
||||
{ RefId::formIdRefId(42), "42" },
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ESMRefIdToString, ESMRefIdToStringTest, ValuesIn(toStringParams));
|
||||
|
||||
struct ESMRefIdToDebugStringTest : TestWithParam<std::pair<ESM::RefId, std::string>>
|
||||
{
|
||||
};
|
||||
|
||||
TEST_P(ESMRefIdToDebugStringTest, toDebugString)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& debugString = GetParam().second;
|
||||
EXPECT_EQ(refId.toDebugString(), debugString);
|
||||
}
|
||||
|
||||
TEST_P(ESMRefIdToDebugStringTest, toStream)
|
||||
{
|
||||
const RefId& refId = GetParam().first;
|
||||
const std::string& debugString = GetParam().second;
|
||||
std::ostringstream stream;
|
||||
stream << refId;
|
||||
EXPECT_EQ(stream.str(), debugString);
|
||||
}
|
||||
|
||||
const std::vector<std::pair<RefId, std::string>> toDebugStringParams = {
|
||||
{ RefId(), "Empty{}" },
|
||||
{ RefId::stringRefId("foo"), "String{foo}" },
|
||||
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), "String{a\\x0\\xFF\\xA\\x9}" },
|
||||
{ RefId::formIdRefId(42), "FormId{42}" },
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(ESMRefIdToDebugString, ESMRefIdToDebugStringTest, ValuesIn(toDebugStringParams));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,10 @@ add_component_dir (to_utf8
|
|||
to_utf8
|
||||
)
|
||||
|
||||
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge)
|
||||
add_component_dir(esm attr common defs esmcommon records util luascripts format refid esmbridge
|
||||
formidrefid
|
||||
stringrefid
|
||||
)
|
||||
|
||||
add_component_dir(fx pass technique lexer widgets stateupdater)
|
||||
|
||||
|
|
24
components/esm/formidrefid.cpp
Normal file
24
components/esm/formidrefid.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#include "formidrefid.hpp"
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
std::string FormIdRefId::toString() const
|
||||
{
|
||||
return std::to_string(mValue);
|
||||
}
|
||||
|
||||
std::string FormIdRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, FormIdRefId value)
|
||||
{
|
||||
return stream << "FormId{" << value.mValue << '}';
|
||||
}
|
||||
}
|
52
components/esm/formidrefid.hpp
Normal file
52
components/esm/formidrefid.hpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_FORMIDREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_FORMIDREFID_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
|
||||
#include <components/esm4/formid.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class FormIdRefId
|
||||
{
|
||||
public:
|
||||
constexpr FormIdRefId() = default;
|
||||
|
||||
constexpr explicit FormIdRefId(ESM4::FormId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
ESM4::FormId getValue() const { return mValue; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
constexpr bool operator==(FormIdRefId rhs) const noexcept { return mValue == rhs.mValue; }
|
||||
|
||||
constexpr bool operator<(FormIdRefId rhs) const noexcept { return mValue < rhs.mValue; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, FormIdRefId value);
|
||||
|
||||
friend struct std::hash<FormIdRefId>;
|
||||
|
||||
private:
|
||||
ESM4::FormId mValue = 0;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::FormIdRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::FormIdRefId value) const noexcept
|
||||
{
|
||||
return std::hash<ESM4::FormId>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,68 +1,162 @@
|
|||
#include "refid.hpp"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "components/misc/strings/algorithm.hpp"
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
bool RefId::operator==(const RefId& rhs) const
|
||||
namespace
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(mId, rhs.mId);
|
||||
const std::string emptyString;
|
||||
|
||||
struct GetRefString
|
||||
{
|
||||
const std::string& operator()(EmptyRefId /*v*/) const { return emptyString; }
|
||||
|
||||
const std::string& operator()(StringRefId v) const { return v.getValue(); }
|
||||
|
||||
template <class T>
|
||||
const std::string& operator()(const T& v) const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "RefId is not a string: " << v;
|
||||
throw std::runtime_error(stream.str());
|
||||
}
|
||||
};
|
||||
|
||||
struct IsEqualToString
|
||||
{
|
||||
const std::string_view mRhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return v == mRhs; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct IsLessThanString
|
||||
{
|
||||
const std::string_view mRhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return v < mRhs; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct IsGreaterThanString
|
||||
{
|
||||
const std::string_view mLhs;
|
||||
|
||||
bool operator()(StringRefId v) const noexcept { return mLhs < v; }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct StartsWith
|
||||
{
|
||||
const std::string_view mPrefix;
|
||||
|
||||
bool operator()(StringRefId v) const { return v.startsWith(mPrefix); }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Contains
|
||||
{
|
||||
const std::string_view mSubString;
|
||||
|
||||
bool operator()(StringRefId v) const { return v.contains(mSubString); }
|
||||
|
||||
template <class T>
|
||||
bool operator()(const T& /*v*/) const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
bool RefId::operator<(const RefId& rhs) const
|
||||
const RefId RefId::sEmpty = {};
|
||||
|
||||
std::string EmptyRefId::toString() const
|
||||
{
|
||||
return Misc::StringUtils::ciLess(mId, rhs.mId);
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool operator<(const RefId& lhs, std::string_view rhs)
|
||||
std::string EmptyRefId::toDebugString() const
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs.mId, rhs);
|
||||
return "Empty{}";
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, const RefId& rhs)
|
||||
std::ostream& operator<<(std::ostream& stream, EmptyRefId value)
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs, rhs.mId);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const RefId& refId)
|
||||
{
|
||||
os << refId.getRefIdString();
|
||||
return os;
|
||||
}
|
||||
|
||||
RefId RefId::stringRefId(std::string_view id)
|
||||
{
|
||||
RefId newRefId;
|
||||
newRefId.mId = id;
|
||||
return newRefId;
|
||||
}
|
||||
|
||||
RefId RefId::formIdRefId(const ESM4::FormId id)
|
||||
{
|
||||
return ESM::RefId::stringRefId(ESM4::formIdToString(id));
|
||||
return stream << value.toDebugString();
|
||||
}
|
||||
|
||||
bool RefId::operator==(std::string_view rhs) const
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(mId, rhs);
|
||||
return std::visit(IsEqualToString{ rhs }, mValue);
|
||||
}
|
||||
|
||||
bool operator<(RefId lhs, std::string_view rhs)
|
||||
{
|
||||
return std::visit(IsLessThanString{ rhs }, lhs.mValue);
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, RefId rhs)
|
||||
{
|
||||
return std::visit(IsGreaterThanString{ lhs }, rhs.mValue);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, RefId value)
|
||||
{
|
||||
return std::visit([&](auto v) -> std::ostream& { return stream << v; }, value.mValue);
|
||||
}
|
||||
|
||||
RefId RefId::stringRefId(std::string_view value)
|
||||
{
|
||||
if (value.empty())
|
||||
return RefId();
|
||||
return RefId(StringRefId(value));
|
||||
}
|
||||
|
||||
const std::string& RefId::getRefIdString() const
|
||||
{
|
||||
return std::visit(GetRefString{}, mValue);
|
||||
}
|
||||
|
||||
std::string RefId::toString() const
|
||||
{
|
||||
return std::visit([](auto v) { return v.toString(); }, mValue);
|
||||
}
|
||||
|
||||
std::string RefId::toDebugString() const
|
||||
{
|
||||
return std::visit([](auto v) { return v.toDebugString(); }, mValue);
|
||||
}
|
||||
|
||||
bool RefId::startsWith(std::string_view prefix) const
|
||||
{
|
||||
return Misc::StringUtils::ciStartsWith(mId, prefix);
|
||||
return std::visit(StartsWith{ prefix }, mValue);
|
||||
}
|
||||
|
||||
bool RefId::contains(std::string_view subString) const
|
||||
{
|
||||
return Misc::StringUtils::ciFind(mId, subString) != std::string_view::npos;
|
||||
return std::visit(Contains{ subString }, mValue);
|
||||
}
|
||||
|
||||
const RefId RefId::sEmpty = {};
|
||||
}
|
||||
|
||||
std::size_t std::hash<ESM::RefId>::operator()(const ESM::RefId& k) const
|
||||
{
|
||||
return Misc::StringUtils::CiHash()(k.getRefIdString());
|
||||
}
|
||||
|
|
|
@ -5,11 +5,28 @@
|
|||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <components/esm4/formid.hpp>
|
||||
#include <components/misc/notnullptr.hpp>
|
||||
|
||||
#include "formidrefid.hpp"
|
||||
#include "stringrefid.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
struct EmptyRefId
|
||||
{
|
||||
constexpr bool operator==(EmptyRefId /*rhs*/) const { return true; }
|
||||
|
||||
constexpr bool operator<(EmptyRefId /*rhs*/) const { return false; }
|
||||
|
||||
std::string toString() const;
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, EmptyRefId value);
|
||||
};
|
||||
|
||||
// RefId is used to represent an Id that identifies an ESM record. These Ids can then be used in
|
||||
// ESM::Stores to find the actual record. These Ids can be serialized/de-serialized, stored on disk and remain
|
||||
// valid. They are used by ESM files, by records to reference other ESM records.
|
||||
|
@ -18,45 +35,83 @@ namespace ESM
|
|||
public:
|
||||
const static RefId sEmpty;
|
||||
|
||||
// The 2 following functions are used to move back and forth between string and RefID. Used for hard coded
|
||||
// RefIds that are as string in the code. For serialization, and display. Using explicit conversions make it
|
||||
// very clear where in the code we need to convert from string to RefId and Vice versa.
|
||||
static RefId stringRefId(std::string_view id);
|
||||
// Constructs RefId from a string using a pointer to a static set of strings.
|
||||
static RefId stringRefId(std::string_view value);
|
||||
|
||||
static RefId formIdRefId(const ESM4::FormId id);
|
||||
// Constructs RefId from ESM4 FormId storing the value in-place.
|
||||
static RefId formIdRefId(ESM4::FormId value) noexcept { return RefId(FormIdRefId(value)); }
|
||||
|
||||
const std::string& getRefIdString() const { return mId; }
|
||||
constexpr RefId() = default;
|
||||
|
||||
bool empty() const { return mId.empty(); }
|
||||
constexpr RefId(EmptyRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
RefId(StringRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr RefId(FormIdRefId value) noexcept
|
||||
: mValue(value)
|
||||
{
|
||||
}
|
||||
|
||||
// Returns a reference to the value of StringRefId if it's the underlying value or throws an exception.
|
||||
const std::string& getRefIdString() const;
|
||||
|
||||
// Returns a string with serialized underlying value.
|
||||
std::string toString() const;
|
||||
|
||||
// Returns a string with serialized underlying value with information about underlying type.
|
||||
std::string toDebugString() const;
|
||||
|
||||
constexpr bool empty() const noexcept { return std::holds_alternative<EmptyRefId>(mValue); }
|
||||
|
||||
// Returns true if underlying value is StringRefId and its underlying std::string starts with given prefix.
|
||||
// Otherwise returns false.
|
||||
bool startsWith(std::string_view prefix) const;
|
||||
|
||||
// Returns true if underlying value is StringRefId and its underlying std::string contains given subString.
|
||||
// Otherwise returns false.
|
||||
bool contains(std::string_view subString) const;
|
||||
|
||||
bool operator==(const RefId& rhs) const;
|
||||
friend constexpr bool operator==(const RefId& l, const RefId& r) { return l.mValue == r.mValue; }
|
||||
|
||||
bool operator==(std::string_view rhs) const;
|
||||
|
||||
bool operator<(const RefId& rhs) const;
|
||||
friend constexpr bool operator<(const RefId& l, const RefId& r) { return l.mValue < r.mValue; }
|
||||
|
||||
friend bool operator<(const RefId& lhs, std::string_view rhs);
|
||||
friend bool operator<(RefId lhs, std::string_view rhs);
|
||||
|
||||
friend bool operator<(std::string_view lhs, const RefId& rhs);
|
||||
friend bool operator<(std::string_view lhs, RefId rhs);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const RefId& dt);
|
||||
friend std::ostream& operator<<(std::ostream& stream, RefId value);
|
||||
|
||||
friend struct std::hash<ESM::RefId>;
|
||||
|
||||
private:
|
||||
std::string mId;
|
||||
std::variant<EmptyRefId, StringRefId, FormIdRefId> mValue{ EmptyRefId{} };
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::EmptyRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::EmptyRefId /*value*/) const noexcept { return 0; }
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<ESM::RefId>
|
||||
{
|
||||
std::size_t operator()(const ESM::RefId& k) const;
|
||||
std::size_t operator()(ESM::RefId value) const noexcept
|
||||
{
|
||||
return std::hash<decltype(value.mValue)>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
88
components/esm/stringrefid.cpp
Normal file
88
components/esm/stringrefid.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
#include "stringrefid.hpp"
|
||||
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "components/misc/guarded.hpp"
|
||||
#include "components/misc/strings/algorithm.hpp"
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using StringsSet = std::unordered_set<std::string, Misc::StringUtils::CiHash, Misc::StringUtils::CiEqual>;
|
||||
|
||||
const std::string emptyString;
|
||||
|
||||
Misc::NotNullPtr<const std::string> getOrInsertString(std::string_view id)
|
||||
{
|
||||
static Misc::ScopeGuarded<StringsSet> refIds;
|
||||
const auto locked = refIds.lock();
|
||||
auto it = locked->find(id);
|
||||
if (it == locked->end())
|
||||
it = locked->emplace(id).first;
|
||||
return &*it;
|
||||
}
|
||||
}
|
||||
|
||||
StringRefId::StringRefId()
|
||||
: mValue(&emptyString)
|
||||
{
|
||||
}
|
||||
|
||||
StringRefId::StringRefId(std::string_view value)
|
||||
: mValue(getOrInsertString(value))
|
||||
{
|
||||
}
|
||||
|
||||
bool StringRefId::operator==(std::string_view rhs) const noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciEqual(*mValue, rhs);
|
||||
}
|
||||
|
||||
bool StringRefId::operator<(StringRefId rhs) const noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(*mValue, *rhs.mValue);
|
||||
}
|
||||
|
||||
bool operator<(StringRefId lhs, std::string_view rhs) noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(*lhs.mValue, rhs);
|
||||
}
|
||||
|
||||
bool operator<(std::string_view lhs, StringRefId rhs) noexcept
|
||||
{
|
||||
return Misc::StringUtils::ciLess(lhs, *rhs.mValue);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& stream, StringRefId value)
|
||||
{
|
||||
stream << "String{";
|
||||
for (char c : *value.mValue)
|
||||
if (std::isprint(c) && c != '\t' && c != '\n' && c != '\r')
|
||||
stream << c;
|
||||
else
|
||||
stream << "\\x" << std::hex << std::uppercase << static_cast<unsigned>(static_cast<unsigned char>(c));
|
||||
return stream << '}';
|
||||
}
|
||||
|
||||
std::string StringRefId::toDebugString() const
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << *this;
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
bool StringRefId::startsWith(std::string_view prefix) const
|
||||
{
|
||||
return Misc::StringUtils::ciStartsWith(*mValue, prefix);
|
||||
}
|
||||
|
||||
bool StringRefId::contains(std::string_view subString) const
|
||||
{
|
||||
return Misc::StringUtils::ciFind(*mValue, subString) != std::string_view::npos;
|
||||
}
|
||||
}
|
64
components/esm/stringrefid.hpp
Normal file
64
components/esm/stringrefid.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef OPENMW_COMPONENTS_ESM_STRINGREFID_HPP
|
||||
#define OPENMW_COMPONENTS_ESM_STRINGREFID_HPP
|
||||
|
||||
#include <functional>
|
||||
#include <iosfwd>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <variant>
|
||||
|
||||
#include <components/misc/notnullptr.hpp>
|
||||
|
||||
namespace ESM
|
||||
{
|
||||
class StringRefId
|
||||
{
|
||||
public:
|
||||
StringRefId();
|
||||
|
||||
// Constructs StringRefId from string using pointer to a static set of strings.
|
||||
explicit StringRefId(std::string_view value);
|
||||
|
||||
const std::string& getValue() const { return *mValue; }
|
||||
|
||||
std::string toString() const { return *mValue; }
|
||||
|
||||
std::string toDebugString() const;
|
||||
|
||||
bool startsWith(std::string_view prefix) const;
|
||||
|
||||
bool contains(std::string_view subString) const;
|
||||
|
||||
bool operator==(StringRefId rhs) const noexcept { return mValue == rhs.mValue; }
|
||||
|
||||
bool operator==(std::string_view rhs) const noexcept;
|
||||
|
||||
// Compares values to provide stable order
|
||||
bool operator<(StringRefId rhs) const noexcept;
|
||||
|
||||
friend bool operator<(StringRefId lhs, std::string_view rhs) noexcept;
|
||||
|
||||
friend bool operator<(std::string_view lhs, StringRefId rhs) noexcept;
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& stream, StringRefId value);
|
||||
|
||||
friend struct std::hash<StringRefId>;
|
||||
|
||||
private:
|
||||
Misc::NotNullPtr<const std::string> mValue;
|
||||
};
|
||||
}
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<ESM::StringRefId>
|
||||
{
|
||||
std::size_t operator()(ESM::StringRefId value) const noexcept
|
||||
{
|
||||
return std::hash<const std::string*>{}(value.mValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in a new issue