#include "enum_runtime.h" #include <util/generic/algorithm.h> #include <util/generic/map.h> #include <util/generic/yexception.h> #include <util/stream/output.h> namespace NEnumSerializationRuntime { template <typename TEnumRepresentationType> [[noreturn]] static void ThrowUndefinedValueException(const TEnumRepresentationType key, const TStringBuf className) { throw yexception() << "Undefined value " << key << " in " << className << ". "; } template <typename TEnumRepresentationType> const TString& TEnumDescriptionBase<TEnumRepresentationType>::ToString(TRepresentationType key) const { const auto it = Names.find(key); if (Y_LIKELY(it != Names.end())) { return it->second; } ThrowUndefinedValueException(key, ClassName); } template <typename TEnumRepresentationType> std::pair<bool, TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::TryFromString(const TStringBuf name) const { const auto it = Values.find(name); if (it != Values.end()) { return {true, it->second}; } return {false, TRepresentationType()}; } template <class TContainer, class TNeedle, class TGetKey> static typename TContainer::value_type const* FindPtrInSortedContainer(const TContainer& vec, const TNeedle& needle, TGetKey&& getKey) { const auto it = LowerBoundBy(vec.begin(), vec.end(), needle, getKey); if (it == vec.end()) { return nullptr; } if (getKey(*it) != needle) { return nullptr; } return std::addressof(*it); } template <typename TEnumRepresentationType> std::pair<bool, TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::TryFromStringSorted(const TStringBuf name, const TInitializationData& enumInitData) { const auto& vec = enumInitData.ValuesInitializer; const auto* ptr = FindPtrInSortedContainer(vec, name, std::mem_fn(&TEnumStringPair::Name)); if (ptr) { return {true, ptr->Key}; } return {false, TRepresentationType()}; } template <typename TEnumRepresentationType> std::pair<bool, TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::TryFromStringFullScan(const TStringBuf name, const TInitializationData& enumInitData) { const auto& vec = enumInitData.ValuesInitializer; const auto* ptr = FindIfPtr(vec, [&](const auto& item) { return item.Name == name; }); if (ptr) { return {true, ptr->Key}; } return {false, TRepresentationType()}; } [[noreturn]] static void ThrowUndefinedNameException(const TStringBuf name, const TStringBuf className, const TStringBuf allEnumNames) { ythrow yexception() << "Key '" << name << "' not found in enum " << className << ". Valid options are: " << allEnumNames << ". "; } template <typename TEnumRepresentationType> [[noreturn]] static void ThrowUndefinedNameException(const TStringBuf name, const typename TEnumDescriptionBase<TEnumRepresentationType>::TInitializationData& enumInitData) { auto exc = __LOCATION__ + yexception() << "Key '" << name << "' not found in enum " << enumInitData.ClassName << ". Valid options are: "; const auto& vec = enumInitData.NamesInitializer; for (size_t i = 0; i < vec.size(); ++i) { if (i != 0) { exc << ", "; } exc << '\'' << vec[i].Name << '\''; } exc << ". "; throw exc; } template <typename TEnumRepresentationType> auto TEnumDescriptionBase<TEnumRepresentationType>::FromString(const TStringBuf name) const -> TRepresentationType { const auto findResult = TryFromString(name); if (Y_LIKELY(findResult.first)) { return findResult.second; } ThrowUndefinedNameException(name, ClassName, AllEnumNames()); } template <typename TEnumRepresentationType> TEnumRepresentationType TEnumDescriptionBase<TEnumRepresentationType>::FromStringFullScan(const TStringBuf name, const TInitializationData& enumInitData) { const auto findResult = TryFromStringFullScan(name, enumInitData); if (Y_LIKELY(findResult.first)) { return findResult.second; } ThrowUndefinedNameException<TEnumRepresentationType>(name, enumInitData); } template <typename TEnumRepresentationType> TEnumRepresentationType TEnumDescriptionBase<TEnumRepresentationType>::FromStringSorted(const TStringBuf name, const TInitializationData& enumInitData) { const auto findResult = TryFromStringSorted(name, enumInitData); if (Y_LIKELY(findResult.first)) { return findResult.second; } ThrowUndefinedNameException<TEnumRepresentationType>(name, enumInitData); } template <typename TEnumRepresentationType> TStringBuf TEnumDescriptionBase<TEnumRepresentationType>::ToStringBuf(TRepresentationType key) const { return this->ToString(key); } template <typename TEnumRepresentationType> TStringBuf TEnumDescriptionBase<TEnumRepresentationType>::ToStringBufFullScan(const TRepresentationType key, const TInitializationData& enumInitData) { const auto& vec = enumInitData.NamesInitializer; const auto* ptr = FindIfPtr(vec, [&](const auto& item) { return item.Key == key; }); if (Y_UNLIKELY(!ptr)) { ThrowUndefinedValueException(key, enumInitData.ClassName); } return ptr->Name; } template <typename TEnumRepresentationType> TStringBuf TEnumDescriptionBase<TEnumRepresentationType>::ToStringBufSorted(const TRepresentationType key, const TInitializationData& enumInitData) { const auto& vec = enumInitData.NamesInitializer; const auto* ptr = FindPtrInSortedContainer(vec, key, std::mem_fn(&TEnumStringPair::Key)); if (Y_UNLIKELY(!ptr)) { ThrowUndefinedValueException(key, enumInitData.ClassName); } return ptr->Name; } template <typename TEnumRepresentationType> TStringBuf TEnumDescriptionBase<TEnumRepresentationType>::ToStringBufDirect(const TRepresentationType key, const TInitializationData& enumInitData) { const auto& vec = enumInitData.NamesInitializer; if (Y_UNLIKELY(vec.empty() || key < vec.front().Key)) { ThrowUndefinedValueException(key, enumInitData.ClassName); } const size_t index = static_cast<size_t>(key - vec.front().Key); if (Y_UNLIKELY(index >= vec.size())) { ThrowUndefinedValueException(key, enumInitData.ClassName); } return vec[index].Name; } template <typename TEnumRepresentationType> void TEnumDescriptionBase<TEnumRepresentationType>::Out(IOutputStream* os, const TRepresentationType key) const { (*os) << this->ToStringBuf(key); } template <typename TEnumRepresentationType> void TEnumDescriptionBase<TEnumRepresentationType>::OutFullScan(IOutputStream* os, const TRepresentationType key, const TInitializationData& enumInitData) { (*os) << ToStringBufFullScan(key, enumInitData); } template <typename TEnumRepresentationType> void TEnumDescriptionBase<TEnumRepresentationType>::OutSorted(IOutputStream* os, const TRepresentationType key, const TInitializationData& enumInitData) { (*os) << ToStringBufSorted(key, enumInitData); } template <typename TEnumRepresentationType> void TEnumDescriptionBase<TEnumRepresentationType>::OutDirect(IOutputStream* os, const TRepresentationType key, const TInitializationData& enumInitData) { (*os) << ToStringBufDirect(key, enumInitData); } template <typename TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::TEnumDescriptionBase(const TInitializationData& enumInitData) : ClassName(enumInitData.ClassName) { const TArrayRef<const TEnumStringPair>& namesInitializer = enumInitData.NamesInitializer; const TArrayRef<const TEnumStringPair>& valuesInitializer = enumInitData.ValuesInitializer; const TArrayRef<const TStringBuf>& cppNamesInitializer = enumInitData.CppNamesInitializer; TMap<TRepresentationType, TString> mapValueToName; TMap<TStringBuf, TRepresentationType> mapNameToValue; for (const TEnumStringPair& it : namesInitializer) { mapValueToName.emplace(it.Key, TString(it.Name)); } for (const TEnumStringPair& it : valuesInitializer) { mapNameToValue.emplace(it.Name, it.Key); } Names = std::move(mapValueToName); Values = std::move(mapNameToValue); AllValues.reserve(Names.size()); for (const auto& it : Names) { if (!AllNames.empty()) { AllNames += ", "; } AllNames += TString::Join('\'', it.second, '\''); AllValues.push_back(it.first); } AllCppNames.reserve(cppNamesInitializer.size()); for (const auto& cn : cppNamesInitializer) { AllCppNames.push_back(TString::Join(enumInitData.CppNamesPrefix, cn)); } } template <typename TEnumRepresentationType> TEnumDescriptionBase<TEnumRepresentationType>::~TEnumDescriptionBase() = default; // explicit instantiation template class TEnumDescriptionBase<int>; template class TEnumDescriptionBase<unsigned>; template class TEnumDescriptionBase<long long>; template class TEnumDescriptionBase<unsigned long long>; }