Use serialized ESM::RefId for Lua records

depth-refraction
elsid 2 years ago
parent ca9c55ac26
commit b7fdca0fe6
No known key found for this signature in database
GPG Key ID: 4DE04C198CBA7625

@ -48,6 +48,40 @@ namespace
return generateSerializedRefIds(generateStringRefIds(size, random), serialize);
}
template <class Random>
std::vector<ESM::RefId> generateIndexRefIds(Random& random)
{
std::vector<ESM::RefId> result;
result.reserve(refIdsCount);
std::uniform_int_distribution<std::uint32_t> distribution(0, std::numeric_limits<std::uint32_t>::max());
std::generate_n(std::back_inserter(result), refIdsCount,
[&] { return ESM::IndexRefId(ESM::REC_ARMO, distribution(random)); });
return result;
}
template <class Random, class Serialize>
std::vector<std::string> generateSerializedIndexRefIds(Random& random, Serialize&& serialize)
{
return generateSerializedRefIds(generateIndexRefIds(random), serialize);
}
template <class Random>
std::vector<ESM::RefId> generateGeneratedRefIds(Random& random)
{
std::vector<ESM::RefId> result;
result.reserve(refIdsCount);
std::uniform_int_distribution<std::uint64_t> distribution(0, std::numeric_limits<std::uint64_t>::max());
std::generate_n(
std::back_inserter(result), refIdsCount, [&] { return ESM::GeneratedRefId(distribution(random)); });
return result;
}
template <class Random, class Serialize>
std::vector<std::string> generateSerializedGeneratedRefIds(Random& random, Serialize&& serialize)
{
return generateSerializedRefIds(generateGeneratedRefIds(random), serialize);
}
void serializeRefId(benchmark::State& state)
{
std::minstd_rand random;
@ -74,9 +108,96 @@ namespace
i = 0;
}
}
void serializeTextStringRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextStringRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
void serializeTextGeneratedRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextGeneratedRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
void serializeTextIndexRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateIndexRefIds(random);
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
i = 0;
}
}
void deserializeTextIndexRefId(benchmark::State& state)
{
std::minstd_rand random;
std::vector<std::string> serializedRefIds
= generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
i = 0;
}
}
}
BENCHMARK(serializeRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(deserializeRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(serializeTextStringRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(deserializeTextStringRefId)->RangeMultiplier(4)->Range(8, 64);
BENCHMARK(serializeTextGeneratedRefId);
BENCHMARK(deserializeTextGeneratedRefId);
BENCHMARK(serializeTextIndexRefId);
BENCHMARK(deserializeTextIndexRefId);
BENCHMARK_MAIN();

@ -40,8 +40,8 @@ namespace MWLua
};
cellT["name"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getNameId(); });
cellT["region"]
= sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getRegion().getRefIdString(); });
cellT["region"] = sol::readonly_property(
[](const CellT& c) -> std::string { return c.mStore->getCell()->getRegion().serializeText(); });
cellT["gridX"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridX(); });
cellT["gridY"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->getGridY(); });
cellT["hasWater"] = sol::readonly_property([](const CellT& c) { return c.mStore->getCell()->hasWater(); });

@ -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().toDebugString(); });
[](const ObjectT& o) -> std::string { return o.ptr().getCellRef().getRefId().serializeText(); });
objectT["cell"] = sol::readonly_property([](const ObjectT& o) -> sol::optional<Cell<ObjectT>> {
const MWWorld::Ptr& ptr = o.ptr();
if (ptr.isInCell())

@ -29,12 +29,12 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Activator& rec) { return "ESM3_Activator[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Activator& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Activator& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Activator& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Activator& rec) -> std::string { return rec.mScript.serializeText(); });
}
}

@ -36,13 +36,13 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Apparatus& rec) { return "ESM3_Apparatus[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Apparatus& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Apparatus& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Apparatus& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Apparatus& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -43,7 +43,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Armor& rec) -> std::string { return "ESM3_Armor[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -51,10 +51,10 @@ namespace MWLua
record["icon"] = sol::readonly_property([vfs](const ESM::Armor& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Armor& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
record["enchant"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Armor& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Armor& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mValue; });
record["type"] = sol::readonly_property([](const ESM::Armor& rec) -> int { return rec.mData.mType; });

@ -38,19 +38,19 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Book& rec) { return "ESM3_Book[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Book& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["text"] = sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mText; });
record["enchant"]
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
= sol::readonly_property([](const ESM::Book& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["isScroll"] = sol::readonly_property([](const ESM::Book& rec) -> bool { return rec.mData.mIsScroll; });
record["value"] = sol::readonly_property([](const ESM::Book& rec) -> int { return rec.mData.mValue; });
record["weight"] = sol::readonly_property([](const ESM::Book& rec) -> float { return rec.mData.mWeight; });

@ -42,7 +42,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Clothing& rec) -> std::string { return "ESM3_Clothing[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Clothing& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Clothing& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -51,9 +51,9 @@ namespace MWLua
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Clothing& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
[](const ESM::Clothing& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"] = sol::readonly_property(
[](const ESM::Clothing& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Clothing& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Clothing& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mValue; });
record["type"] = sol::readonly_property([](const ESM::Clothing& rec) -> int { return rec.mData.mType; });

@ -55,13 +55,13 @@ namespace MWLua
return "ESM3_Container[" + rec.mId.toDebugString() + "]";
};
record["id"]
= sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Container& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Container& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Container& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Container& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Container& rec) -> float { return rec.mWeight; });
}
}

@ -33,8 +33,8 @@ namespace MWLua
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Creature& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Creature& rec) -> std::string { return rec.mScript.serializeText(); });
record["baseCreature"] = sol::readonly_property(
[](const ESM::Creature& rec) -> std::string { return rec.mOriginal.getRefIdString(); });
[](const ESM::Creature& rec) -> std::string { return rec.mOriginal.serializeText(); });
}
}

@ -50,17 +50,17 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Door& rec) -> std::string { return "ESM3_Door[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Door& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Door& rec) -> std::string { return rec.mScript.serializeText(); });
record["openSound"] = sol::readonly_property(
[](const ESM::Door& rec) -> std::string { return rec.mOpenSound.getRefIdString(); });
[](const ESM::Door& rec) -> std::string { return rec.mOpenSound.serializeText(); });
record["closeSound"] = sol::readonly_property(
[](const ESM::Door& rec) -> std::string { return rec.mCloseSound.getRefIdString(); });
[](const ESM::Door& rec) -> std::string { return rec.mCloseSound.serializeText(); });
}
}

@ -29,14 +29,14 @@ namespace MWLua
sol::usertype<ESM::Ingredient> record = context.mLua->sol().new_usertype<ESM::Ingredient>(("ESM3_Ingredient"));
record[sol::meta_function::to_string]
= [](const ESM::Ingredient& rec) { return "ESM3_Ingredient[" + rec.mId.toDebugString() + "]"; };
record["id"] = sol::readonly_property(
[](const ESM::Ingredient& rec) -> std::string { return rec.mId.getRefIdString(); });
record["id"]
= sol::readonly_property([](const ESM::Ingredient& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Ingredient& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Ingredient& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Ingredient& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Ingredient& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -29,7 +29,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Light& rec) -> std::string { return "ESM3_Light[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Light& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -38,9 +38,9 @@ namespace MWLua
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["sound"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.getRefIdString(); });
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mSound.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Light& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Light& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mValue; });
record["duration"] = sol::readonly_property([](const ESM::Light& rec) -> int { return rec.mData.mTime; });

@ -29,13 +29,13 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Lockpick& rec) { return "ESM3_Lockpick[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Lockpick& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Lockpick& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Lockpick& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Lockpick& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -30,13 +30,13 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Miscellaneous& rec) { return "ESM3_Miscellaneous[" + rec.mId.toDebugString() + "]"; };
record["id"] = sol::readonly_property(
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mId.getRefIdString(); });
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Miscellaneous& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Miscellaneous& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Miscellaneous& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -32,15 +32,15 @@ namespace MWLua
= [](const ESM::NPC& rec) { return "ESM3_NPC[" + rec.mId.toDebugString() + "]"; };
record["name"] = sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mName; });
record["race"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mRace.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mRace.serializeText(); });
record["class"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mClass.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mClass.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mScript.serializeText(); });
record["hair"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHair.serializeText(); });
record["head"]
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.getRefIdString(); });
= sol::readonly_property([](const ESM::NPC& rec) -> std::string { return rec.mHead.serializeText(); });
// This function is game-specific, in future we should replace it with something more universal.
npc["isWerewolf"] = [](const Object& o) {

@ -28,7 +28,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Potion& rec) { return "ESM3_Potion[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -36,8 +36,8 @@ namespace MWLua
record["icon"] = sol::readonly_property([vfs](const ESM::Potion& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Potion& rec) -> std::string { return rec.mScript.getRefIdString(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Potion& rec) -> std::string { return rec.mScript.serializeText(); });
record["weight"] = sol::readonly_property([](const ESM::Potion& rec) -> float { return rec.mData.mWeight; });
record["value"] = sol::readonly_property([](const ESM::Potion& rec) -> int { return rec.mData.mValue; });
}

@ -29,13 +29,13 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Probe& rec) { return "ESM3_Probe[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"]
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mScript.getRefIdString(); });
= sol::readonly_property([](const ESM::Probe& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Probe& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -29,13 +29,13 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Repair& rec) { return "ESM3_Repair[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});
record["mwscript"] = sol::readonly_property(
[](const ESM::Repair& rec) -> std::string { return rec.mScript.getRefIdString(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Repair& rec) -> std::string { return rec.mScript.serializeText(); });
record["icon"] = sol::readonly_property([vfs](const ESM::Repair& rec) -> std::string {
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});

@ -29,7 +29,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Static& rec) -> std::string { return "ESM3_Static[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Static& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Static& rec) -> std::string { return rec.mId.serializeText(); });
record["model"] = sol::readonly_property([vfs](const ESM::Static& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
});

@ -58,7 +58,6 @@ namespace MWLua
{ ESM::REC_PROB, ObjectTypeName::Probe },
{ ESM::REC_REPA, ObjectTypeName::Repair },
};
}
unsigned int getLiveCellRefType(const MWWorld::LiveCellRefBase* ref)

@ -57,8 +57,7 @@ namespace MWLua
const MWWorld::Store<T>& store = MWBase::Environment::get().getWorld()->getStore().get<T>();
table["record"] = sol::overload([](const Object& obj) -> const T* { return obj.ptr().get<T>()->mBase; },
[&store](
const std::string& recordId) -> const T* { return store.find(ESM::RefId::stringRefId(recordId)); });
[&store](std::string_view id) -> const T* { return store.find(ESM::RefId::deserializeText(id)); });
}
}

@ -46,7 +46,7 @@ namespace MWLua
record[sol::meta_function::to_string]
= [](const ESM::Weapon& rec) -> std::string { return "ESM3_Weapon[" + rec.mId.toDebugString() + "]"; };
record["id"]
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mId.getRefIdString(); });
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mId.serializeText(); });
record["name"] = sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mName; });
record["model"] = sol::readonly_property([vfs](const ESM::Weapon& rec) -> std::string {
return Misc::ResourceHelpers::correctMeshPath(rec.mModel, vfs);
@ -55,9 +55,9 @@ namespace MWLua
return Misc::ResourceHelpers::correctIconPath(rec.mIcon, vfs);
});
record["enchant"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> std::string { return rec.mEnchant.getRefIdString(); });
record["mwscript"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> std::string { return rec.mScript.getRefIdString(); });
[](const ESM::Weapon& rec) -> std::string { return rec.mEnchant.serializeText(); });
record["mwscript"]
= sol::readonly_property([](const ESM::Weapon& rec) -> std::string { return rec.mScript.serializeText(); });
record["isMagical"] = sol::readonly_property(
[](const ESM::Weapon& rec) -> bool { return rec.mData.mFlags & ESM::Weapon::Magical; });
record["isSilver"] = sol::readonly_property(

@ -211,9 +211,9 @@ namespace ESM
{ 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" },
{ RefId::generated(42), "42" },
{ RefId::index(REC_ARMO, 42), "ARMO, 42" },
{ RefId::formIdRefId(42), "0x2a" },
{ RefId::generated(42), "0x2a" },
{ RefId::index(REC_ARMO, 42), "ARMO:0x2a" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdToString, ESMRefIdToStringTest, ValuesIn(toStringParams));
@ -240,15 +240,51 @@ namespace ESM
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}" },
{ RefId::generated(42), "Generated{42}" },
{ RefId::index(REC_ARMO, 42), "Index{ARMO, 42}" },
{ RefId::stringRefId("foo"), "\"foo\"" },
{ RefId::stringRefId("BAR"), "\"BAR\"" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), "\"a\\x0\\xFF\\xA\\x9\"" },
{ RefId::formIdRefId(42), "FormId:0x2a" },
{ RefId::generated(42), "Generated:0x2a" },
{ RefId::index(REC_ARMO, 42), "Index:ARMO:0x2a" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdToDebugString, ESMRefIdToDebugStringTest, ValuesIn(toDebugStringParams));
struct ESMRefIdTextTest : TestWithParam<std::pair<RefId, std::string>>
{
};
TEST_P(ESMRefIdTextTest, serializeTextShouldReturnString)
{
EXPECT_EQ(GetParam().first.serializeText(), GetParam().second);
}
TEST_P(ESMRefIdTextTest, deserializeTextShouldReturnRefId)
{
EXPECT_EQ(RefId::deserializeText(GetParam().second), GetParam().first);
}
const std::vector<std::pair<RefId, std::string>> serializedRefIds = {
{ RefId(), "" },
{ RefId::stringRefId("foo"), "foo" },
{ RefId::stringRefId("BAR"), "bar" },
{ RefId::stringRefId(std::string({ 'a', 0, -1, '\n', '\t' })), { 'a', 0, -1, '\n', '\t' } },
{ RefId::formIdRefId(0), "FormId:0x0" },
{ RefId::formIdRefId(1), "FormId:0x1" },
{ RefId::formIdRefId(0x1f), "FormId:0x1f" },
{ RefId::formIdRefId(std::numeric_limits<ESM4::FormId>::max()), "FormId:0xffffffff" },
{ RefId::generated(0), "Generated:0x0" },
{ RefId::generated(1), "Generated:0x1" },
{ RefId::generated(0x1f), "Generated:0x1f" },
{ RefId::generated(std::numeric_limits<std::uint64_t>::max()), "Generated:0xffffffffffffffff" },
{ RefId::index(REC_INGR, 0), "Index:INGR:0x0" },
{ RefId::index(REC_INGR, 1), "Index:INGR:0x1" },
{ RefId::index(REC_INGR, 0x1f), "Index:INGR:0x1f" },
{ RefId::index(REC_INGR, std::numeric_limits<std::uint32_t>::max()), "Index:INGR:0xffffffff" },
};
INSTANTIATE_TEST_SUITE_P(ESMRefIdText, ESMRefIdTextTest, ValuesIn(serializedRefIds));
template <class T>
RefId generateRefId();
@ -295,7 +331,14 @@ namespace ESM
EXPECT_EQ(RefId::deserialize(refId.serialize()), refId);
}
REGISTER_TYPED_TEST_SUITE_P(ESMRefIdSerializeDeserializeTest, serializeThenDeserializeShouldProduceSameValue);
TYPED_TEST_P(ESMRefIdSerializeDeserializeTest, serializeTextThenDeserializeTextShouldProduceSameValue)
{
const RefId refId = generateRefId<TypeParam>();
EXPECT_EQ(RefId::deserializeText(refId.serializeText()), refId);
}
REGISTER_TYPED_TEST_SUITE_P(ESMRefIdSerializeDeserializeTest, serializeThenDeserializeShouldProduceSameValue,
serializeTextThenDeserializeTextShouldProduceSameValue);
using RefIdTypeParams = Types<EmptyRefId, StringRefId, FormIdRefId, GeneratedRefId, IndexRefId>;

@ -90,6 +90,7 @@ add_component_dir(esm attr common defs esmcommon records util luascripts format
stringrefid
generatedrefid
indexrefid
serializerefid
)
add_component_dir(fx pass technique lexer widgets stateupdater)

@ -1,24 +1,28 @@
#include "formidrefid.hpp"
#include "serializerefid.hpp"
#include <ostream>
#include <sstream>
namespace ESM
{
std::string FormIdRefId::toString() const
{
return std::to_string(mValue);
std::string result;
result.resize(getIntegralSize(mValue) + 2, '\0');
serializeIntegral(mValue, 0, result);
return result;
}
std::string FormIdRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdValue(mValue, formIdRefIdPrefix, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, FormIdRefId value)
{
return stream << "FormId{" << value.mValue << '}';
return stream << value.toDebugString();
}
}

@ -1,24 +1,28 @@
#include "generatedrefid.hpp"
#include "serializerefid.hpp"
#include <ostream>
#include <sstream>
namespace ESM
{
std::string GeneratedRefId::toString() const
{
return std::to_string(mValue);
std::string result;
result.resize(getIntegralSize(mValue) + 2, '\0');
serializeIntegral(mValue, 0, result);
return result;
}
std::string GeneratedRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdValue(mValue, generatedRefIdPrefix, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, GeneratedRefId value)
{
return stream << "Generated{" << value.mValue << '}';
return stream << value.toDebugString();
}
}

@ -1,26 +1,33 @@
#include "indexrefid.hpp"
#include <ostream>
#include <sstream>
#include "serializerefid.hpp"
#include "esmcommon.hpp"
#include <ostream>
namespace ESM
{
std::string IndexRefId::toString() const
{
return ESM::NAME(mRecordType).toString() + ", " + std::to_string(mValue);
std::string result;
result.resize(sizeof(mRecordType) + getIntegralSize(mValue) + 3, '\0');
std::memcpy(result.data(), &mRecordType, sizeof(mRecordType));
result[sizeof(mRecordType)] = ':';
serializeIntegral(mValue, sizeof(mRecordType) + 1, result);
return result;
}
std::string IndexRefId::toDebugString() const
{
std::ostringstream stream;
stream << *this;
return stream.str();
std::string result;
serializeRefIdPrefix(sizeof(mRecordType) + getIntegralSize(mValue) + 1, indexRefIdPrefix, result);
std::memcpy(result.data() + indexRefIdPrefix.size(), &mRecordType, sizeof(mRecordType));
result[indexRefIdPrefix.size() + sizeof(mRecordType)] = ':';
serializeIntegral(mValue, indexRefIdPrefix.size() + sizeof(mRecordType) + 1, result);
return result;
}
std::ostream& operator<<(std::ostream& stream, IndexRefId value)
{
return stream << "Index{" << ESM::NAME(value.mRecordType).toStringView() << ", " << value.mValue << '}';
return stream << value.toDebugString();
}
}

@ -1,8 +1,15 @@
#include "refid.hpp"
#include "serializerefid.hpp"
#include "components/misc/strings/lower.hpp"
#include <charconv>
#include <ostream>
#include <sstream>
#include <stdexcept>
#include <system_error>
#include <variant>
namespace ESM
{
@ -102,6 +109,19 @@ namespace ESM
return false;
}
};
struct SerializeText
{
std::string operator()(ESM::EmptyRefId /*v*/) const { return std::string(); }
std::string operator()(ESM::StringRefId v) const { return Misc::StringUtils::lowerCase(v.getValue()); }
template <class T>
std::string operator()(const T& v) const
{
return v.toDebugString();
}
};
}
const RefId RefId::sEmpty = {};
@ -193,4 +213,31 @@ namespace ESM
std::memcpy(&result.mValue, value.data(), sizeof(result.mValue));
return result;
}
std::string RefId::serializeText() const
{
return std::visit(SerializeText{}, mValue);
}
ESM::RefId RefId::deserializeText(std::string_view value)
{
if (value.empty())
return ESM::RefId();
if (value.starts_with(formIdRefIdPrefix))
return ESM::RefId::formIdRefId(deserializeIntegral<ESM4::FormId>(formIdRefIdPrefix.size(), value));
if (value.starts_with(generatedRefIdPrefix))
return ESM::RefId::generated(deserializeIntegral<std::uint64_t>(generatedRefIdPrefix.size(), value));
if (value.starts_with(indexRefIdPrefix))
{
ESM::RecNameInts recordType{};
std::memcpy(&recordType, value.data() + indexRefIdPrefix.size(), sizeof(recordType));
return ESM::RefId::index(recordType,
deserializeIntegral<std::uint32_t>(indexRefIdPrefix.size() + sizeof(recordType) + 1, value));
}
return ESM::RefId::stringRefId(value);
}
}

@ -48,9 +48,12 @@ namespace ESM
public:
const static RefId sEmpty;
// Constructs RefId from a string containing byte by byte copy of RefId::mValue.
// Constructs RefId from a serialized string containing byte by byte copy of RefId::mValue.
static ESM::RefId deserialize(std::string_view value);
// Constructs RefId from a serialized text.
static ESM::RefId deserializeText(std::string_view value);
// Constructs RefId from a string using a pointer to a static set of strings.
static RefId stringRefId(std::string_view value);
@ -118,6 +121,9 @@ namespace ESM
// Copy mValue byte by byte into a string. Use result only during within the same process.
std::string serialize() const;
// Serialize into stable text format.
std::string serializeText() const;
friend constexpr bool operator==(const RefId& l, const RefId& r) { return l.mValue == r.mValue; }
bool operator==(std::string_view rhs) const;

@ -0,0 +1,64 @@
#ifndef OPENMW_COMPONENTS_ESM_SERIALIZEREFID_HPP
#define OPENMW_COMPONENTS_ESM_SERIALIZEREFID_HPP
#include <charconv>
#include <cstring>
#include <string>
#include <string_view>
#include <system_error>
namespace ESM
{
constexpr std::string_view formIdRefIdPrefix = "FormId:";
constexpr std::string_view generatedRefIdPrefix = "Generated:";
constexpr std::string_view indexRefIdPrefix = "Index:";
template <class T>
std::size_t getIntegralSize(T value)
{
std::size_t result = sizeof(T) * 2;
while (true)
{
if (result == 1 || (value & static_cast<T>(0xf) << ((result - 1) * 4)) != 0)
break;
--result;
}
return result;
}
inline void serializeRefIdPrefix(std::size_t valueSize, std::string_view prefix, std::string& out)
{
out.resize(prefix.size() + valueSize + 2, '\0');
std::memcpy(out.data(), prefix.data(), prefix.size());
}
template <class T>
void serializeIntegral(T value, std::size_t shift, std::string& out)
{
out[shift] = '0';
out[shift + 1] = 'x';
const auto r = std::to_chars(out.data() + shift + 2, out.data() + out.size(), value, 16);
if (r.ec != std::errc())
throw std::system_error(std::make_error_code(r.ec), "Failed to serialize ESM::RefId integral value");
}
template <class T>
void serializeRefIdValue(T value, std::string_view prefix, std::string& out)
{
serializeRefIdPrefix(getIntegralSize(value), prefix, out);
serializeIntegral(value, prefix.size(), out);
}
template <class T>
T deserializeIntegral(std::size_t shift, std::string_view value)
{
T result{};
const auto r = std::from_chars(value.data() + shift + 2, value.data() + value.size(), result, 16);
if (r.ec != std::errc())
throw std::system_error(std::make_error_code(r.ec),
"Failed to deserialize ESM::RefId integral value: \"" + std::string(value) + '"');
return result;
}
}
#endif

@ -60,13 +60,13 @@ namespace ESM
std::ostream& operator<<(std::ostream& stream, StringRefId value)
{
stream << "String{";
stream << '"';
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 << '}';
return stream << '"';
}
std::string StringRefId::toDebugString() const

Loading…
Cancel
Save