| //===- llvm/ExecutionEngine/Orc/RPCSerialization.h --------------*- C++ -*-===// |
| // |
| // The LLVM Compiler Infrastructure |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H |
| #define LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H |
| |
| #include "OrcError.h" |
| #include "llvm/Support/thread.h" |
| #include <map> |
| #include <mutex> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| namespace llvm { |
| namespace orc { |
| namespace rpc { |
| |
| template <typename T> |
| class RPCTypeName; |
| |
| /// TypeNameSequence is a utility for rendering sequences of types to a string |
| /// by rendering each type, separated by ", ". |
| template <typename... ArgTs> class RPCTypeNameSequence {}; |
| |
| /// Render an empty TypeNameSequence to an ostream. |
| template <typename OStream> |
| OStream &operator<<(OStream &OS, const RPCTypeNameSequence<> &V) { |
| return OS; |
| } |
| |
| /// Render a TypeNameSequence of a single type to an ostream. |
| template <typename OStream, typename ArgT> |
| OStream &operator<<(OStream &OS, const RPCTypeNameSequence<ArgT> &V) { |
| OS << RPCTypeName<ArgT>::getName(); |
| return OS; |
| } |
| |
| /// Render a TypeNameSequence of more than one type to an ostream. |
| template <typename OStream, typename ArgT1, typename ArgT2, typename... ArgTs> |
| OStream& |
| operator<<(OStream &OS, const RPCTypeNameSequence<ArgT1, ArgT2, ArgTs...> &V) { |
| OS << RPCTypeName<ArgT1>::getName() << ", " |
| << RPCTypeNameSequence<ArgT2, ArgTs...>(); |
| return OS; |
| } |
| |
| template <> |
| class RPCTypeName<void> { |
| public: |
| static const char* getName() { return "void"; } |
| }; |
| |
| template <> |
| class RPCTypeName<int8_t> { |
| public: |
| static const char* getName() { return "int8_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<uint8_t> { |
| public: |
| static const char* getName() { return "uint8_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<int16_t> { |
| public: |
| static const char* getName() { return "int16_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<uint16_t> { |
| public: |
| static const char* getName() { return "uint16_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<int32_t> { |
| public: |
| static const char* getName() { return "int32_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<uint32_t> { |
| public: |
| static const char* getName() { return "uint32_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<int64_t> { |
| public: |
| static const char* getName() { return "int64_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<uint64_t> { |
| public: |
| static const char* getName() { return "uint64_t"; } |
| }; |
| |
| template <> |
| class RPCTypeName<bool> { |
| public: |
| static const char* getName() { return "bool"; } |
| }; |
| |
| template <> |
| class RPCTypeName<std::string> { |
| public: |
| static const char* getName() { return "std::string"; } |
| }; |
| |
| template <> |
| class RPCTypeName<Error> { |
| public: |
| static const char* getName() { return "Error"; } |
| }; |
| |
| template <typename T> |
| class RPCTypeName<Expected<T>> { |
| public: |
| static const char* getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) << "Expected<" |
| << RPCTypeNameSequence<T>() |
| << ">"; |
| return Name.data(); |
| } |
| |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename T> |
| std::mutex RPCTypeName<Expected<T>>::NameMutex; |
| |
| template <typename T> |
| std::string RPCTypeName<Expected<T>>::Name; |
| |
| template <typename T1, typename T2> |
| class RPCTypeName<std::pair<T1, T2>> { |
| public: |
| static const char* getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) << "std::pair<" << RPCTypeNameSequence<T1, T2>() |
| << ">"; |
| return Name.data(); |
| } |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename T1, typename T2> |
| std::mutex RPCTypeName<std::pair<T1, T2>>::NameMutex; |
| template <typename T1, typename T2> |
| std::string RPCTypeName<std::pair<T1, T2>>::Name; |
| |
| template <typename... ArgTs> |
| class RPCTypeName<std::tuple<ArgTs...>> { |
| public: |
| static const char* getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) << "std::tuple<" |
| << RPCTypeNameSequence<ArgTs...>() << ">"; |
| return Name.data(); |
| } |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename... ArgTs> |
| std::mutex RPCTypeName<std::tuple<ArgTs...>>::NameMutex; |
| template <typename... ArgTs> |
| std::string RPCTypeName<std::tuple<ArgTs...>>::Name; |
| |
| template <typename T> |
| class RPCTypeName<std::vector<T>> { |
| public: |
| static const char*getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) << "std::vector<" << RPCTypeName<T>::getName() |
| << ">"; |
| return Name.data(); |
| } |
| |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename T> |
| std::mutex RPCTypeName<std::vector<T>>::NameMutex; |
| template <typename T> |
| std::string RPCTypeName<std::vector<T>>::Name; |
| |
| template <typename T> class RPCTypeName<std::set<T>> { |
| public: |
| static const char *getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) |
| << "std::set<" << RPCTypeName<T>::getName() << ">"; |
| return Name.data(); |
| } |
| |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename T> std::mutex RPCTypeName<std::set<T>>::NameMutex; |
| template <typename T> std::string RPCTypeName<std::set<T>>::Name; |
| |
| template <typename K, typename V> class RPCTypeName<std::map<K, V>> { |
| public: |
| static const char *getName() { |
| std::lock_guard<std::mutex> Lock(NameMutex); |
| if (Name.empty()) |
| raw_string_ostream(Name) |
| << "std::map<" << RPCTypeNameSequence<K, V>() << ">"; |
| return Name.data(); |
| } |
| |
| private: |
| static std::mutex NameMutex; |
| static std::string Name; |
| }; |
| |
| template <typename K, typename V> |
| std::mutex RPCTypeName<std::map<K, V>>::NameMutex; |
| template <typename K, typename V> std::string RPCTypeName<std::map<K, V>>::Name; |
| |
| /// The SerializationTraits<ChannelT, T> class describes how to serialize and |
| /// deserialize an instance of type T to/from an abstract channel of type |
| /// ChannelT. It also provides a representation of the type's name via the |
| /// getName method. |
| /// |
| /// Specializations of this class should provide the following functions: |
| /// |
| /// @code{.cpp} |
| /// |
| /// static const char* getName(); |
| /// static Error serialize(ChannelT&, const T&); |
| /// static Error deserialize(ChannelT&, T&); |
| /// |
| /// @endcode |
| /// |
| /// The third argument of SerializationTraits is intended to support SFINAE. |
| /// E.g.: |
| /// |
| /// @code{.cpp} |
| /// |
| /// class MyVirtualChannel { ... }; |
| /// |
| /// template <DerivedChannelT> |
| /// class SerializationTraits<DerivedChannelT, bool, |
| /// typename std::enable_if< |
| /// std::is_base_of<VirtChannel, DerivedChannel>::value |
| /// >::type> { |
| /// public: |
| /// static const char* getName() { ... }; |
| /// } |
| /// |
| /// @endcode |
| template <typename ChannelT, typename WireType, |
| typename ConcreteType = WireType, typename = void> |
| class SerializationTraits; |
| |
| template <typename ChannelT> |
| class SequenceTraits { |
| public: |
| static Error emitSeparator(ChannelT &C) { return Error::success(); } |
| static Error consumeSeparator(ChannelT &C) { return Error::success(); } |
| }; |
| |
| /// Utility class for serializing sequences of values of varying types. |
| /// Specializations of this class contain 'serialize' and 'deserialize' methods |
| /// for the given channel. The ArgTs... list will determine the "over-the-wire" |
| /// types to be serialized. The serialize and deserialize methods take a list |
| /// CArgTs... ("caller arg types") which must be the same length as ArgTs..., |
| /// but may be different types from ArgTs, provided that for each CArgT there |
| /// is a SerializationTraits specialization |
| /// SerializeTraits<ChannelT, ArgT, CArgT> with methods that can serialize the |
| /// caller argument to over-the-wire value. |
| template <typename ChannelT, typename... ArgTs> |
| class SequenceSerialization; |
| |
| template <typename ChannelT> |
| class SequenceSerialization<ChannelT> { |
| public: |
| static Error serialize(ChannelT &C) { return Error::success(); } |
| static Error deserialize(ChannelT &C) { return Error::success(); } |
| }; |
| |
| template <typename ChannelT, typename ArgT> |
| class SequenceSerialization<ChannelT, ArgT> { |
| public: |
| |
| template <typename CArgT> |
| static Error serialize(ChannelT &C, CArgT &&CArg) { |
| return SerializationTraits<ChannelT, ArgT, |
| typename std::decay<CArgT>::type>:: |
| serialize(C, std::forward<CArgT>(CArg)); |
| } |
| |
| template <typename CArgT> |
| static Error deserialize(ChannelT &C, CArgT &CArg) { |
| return SerializationTraits<ChannelT, ArgT, CArgT>::deserialize(C, CArg); |
| } |
| }; |
| |
| template <typename ChannelT, typename ArgT, typename... ArgTs> |
| class SequenceSerialization<ChannelT, ArgT, ArgTs...> { |
| public: |
| |
| template <typename CArgT, typename... CArgTs> |
| static Error serialize(ChannelT &C, CArgT &&CArg, |
| CArgTs &&... CArgs) { |
| if (auto Err = |
| SerializationTraits<ChannelT, ArgT, typename std::decay<CArgT>::type>:: |
| serialize(C, std::forward<CArgT>(CArg))) |
| return Err; |
| if (auto Err = SequenceTraits<ChannelT>::emitSeparator(C)) |
| return Err; |
| return SequenceSerialization<ChannelT, ArgTs...>:: |
| serialize(C, std::forward<CArgTs>(CArgs)...); |
| } |
| |
| template <typename CArgT, typename... CArgTs> |
| static Error deserialize(ChannelT &C, CArgT &CArg, |
| CArgTs &... CArgs) { |
| if (auto Err = |
| SerializationTraits<ChannelT, ArgT, CArgT>::deserialize(C, CArg)) |
| return Err; |
| if (auto Err = SequenceTraits<ChannelT>::consumeSeparator(C)) |
| return Err; |
| return SequenceSerialization<ChannelT, ArgTs...>::deserialize(C, CArgs...); |
| } |
| }; |
| |
| template <typename ChannelT, typename... ArgTs> |
| Error serializeSeq(ChannelT &C, ArgTs &&... Args) { |
| return SequenceSerialization<ChannelT, typename std::decay<ArgTs>::type...>:: |
| serialize(C, std::forward<ArgTs>(Args)...); |
| } |
| |
| template <typename ChannelT, typename... ArgTs> |
| Error deserializeSeq(ChannelT &C, ArgTs &... Args) { |
| return SequenceSerialization<ChannelT, ArgTs...>::deserialize(C, Args...); |
| } |
| |
| template <typename ChannelT> |
| class SerializationTraits<ChannelT, Error> { |
| public: |
| |
| using WrappedErrorSerializer = |
| std::function<Error(ChannelT &C, const ErrorInfoBase&)>; |
| |
| using WrappedErrorDeserializer = |
| std::function<Error(ChannelT &C, Error &Err)>; |
| |
| template <typename ErrorInfoT, typename SerializeFtor, |
| typename DeserializeFtor> |
| static void registerErrorType(std::string Name, SerializeFtor Serialize, |
| DeserializeFtor Deserialize) { |
| assert(!Name.empty() && |
| "The empty string is reserved for the Success value"); |
| |
| const std::string *KeyName = nullptr; |
| { |
| // We're abusing the stability of std::map here: We take a reference to the |
| // key of the deserializers map to save us from duplicating the string in |
| // the serializer. This should be changed to use a stringpool if we switch |
| // to a map type that may move keys in memory. |
| std::lock_guard<std::recursive_mutex> Lock(DeserializersMutex); |
| auto I = |
| Deserializers.insert(Deserializers.begin(), |
| std::make_pair(std::move(Name), |
| std::move(Deserialize))); |
| KeyName = &I->first; |
| } |
| |
| { |
| assert(KeyName != nullptr && "No keyname pointer"); |
| std::lock_guard<std::recursive_mutex> Lock(SerializersMutex); |
| // FIXME: Move capture Serialize once we have C++14. |
| Serializers[ErrorInfoT::classID()] = |
| [KeyName, Serialize](ChannelT &C, const ErrorInfoBase &EIB) -> Error { |
| assert(EIB.dynamicClassID() == ErrorInfoT::classID() && |
| "Serializer called for wrong error type"); |
| if (auto Err = serializeSeq(C, *KeyName)) |
| return Err; |
| return Serialize(C, static_cast<const ErrorInfoT &>(EIB)); |
| }; |
| } |
| } |
| |
| static Error serialize(ChannelT &C, Error &&Err) { |
| std::lock_guard<std::recursive_mutex> Lock(SerializersMutex); |
| |
| if (!Err) |
| return serializeSeq(C, std::string()); |
| |
| return handleErrors(std::move(Err), |
| [&C](const ErrorInfoBase &EIB) { |
| auto SI = Serializers.find(EIB.dynamicClassID()); |
| if (SI == Serializers.end()) |
| return serializeAsStringError(C, EIB); |
| return (SI->second)(C, EIB); |
| }); |
| } |
| |
| static Error deserialize(ChannelT &C, Error &Err) { |
| std::lock_guard<std::recursive_mutex> Lock(DeserializersMutex); |
| |
| std::string Key; |
| if (auto Err = deserializeSeq(C, Key)) |
| return Err; |
| |
| if (Key.empty()) { |
| ErrorAsOutParameter EAO(&Err); |
| Err = Error::success(); |
| return Error::success(); |
| } |
| |
| auto DI = Deserializers.find(Key); |
| assert(DI != Deserializers.end() && "No deserializer for error type"); |
| return (DI->second)(C, Err); |
| } |
| |
| private: |
| |
| static Error serializeAsStringError(ChannelT &C, const ErrorInfoBase &EIB) { |
| std::string ErrMsg; |
| { |
| raw_string_ostream ErrMsgStream(ErrMsg); |
| EIB.log(ErrMsgStream); |
| } |
| return serialize(C, make_error<StringError>(std::move(ErrMsg), |
| inconvertibleErrorCode())); |
| } |
| |
| static std::recursive_mutex SerializersMutex; |
| static std::recursive_mutex DeserializersMutex; |
| static std::map<const void*, WrappedErrorSerializer> Serializers; |
| static std::map<std::string, WrappedErrorDeserializer> Deserializers; |
| }; |
| |
| template <typename ChannelT> |
| std::recursive_mutex SerializationTraits<ChannelT, Error>::SerializersMutex; |
| |
| template <typename ChannelT> |
| std::recursive_mutex SerializationTraits<ChannelT, Error>::DeserializersMutex; |
| |
| template <typename ChannelT> |
| std::map<const void*, |
| typename SerializationTraits<ChannelT, Error>::WrappedErrorSerializer> |
| SerializationTraits<ChannelT, Error>::Serializers; |
| |
| template <typename ChannelT> |
| std::map<std::string, |
| typename SerializationTraits<ChannelT, Error>::WrappedErrorDeserializer> |
| SerializationTraits<ChannelT, Error>::Deserializers; |
| |
| /// Registers a serializer and deserializer for the given error type on the |
| /// given channel type. |
| template <typename ChannelT, typename ErrorInfoT, typename SerializeFtor, |
| typename DeserializeFtor> |
| void registerErrorSerialization(std::string Name, SerializeFtor &&Serialize, |
| DeserializeFtor &&Deserialize) { |
| SerializationTraits<ChannelT, Error>::template registerErrorType<ErrorInfoT>( |
| std::move(Name), |
| std::forward<SerializeFtor>(Serialize), |
| std::forward<DeserializeFtor>(Deserialize)); |
| } |
| |
| /// Registers serialization/deserialization for StringError. |
| template <typename ChannelT> |
| void registerStringError() { |
| static bool AlreadyRegistered = false; |
| if (!AlreadyRegistered) { |
| registerErrorSerialization<ChannelT, StringError>( |
| "StringError", |
| [](ChannelT &C, const StringError &SE) { |
| return serializeSeq(C, SE.getMessage()); |
| }, |
| [](ChannelT &C, Error &Err) -> Error { |
| ErrorAsOutParameter EAO(&Err); |
| std::string Msg; |
| if (auto E2 = deserializeSeq(C, Msg)) |
| return E2; |
| Err = |
| make_error<StringError>(std::move(Msg), |
| orcError( |
| OrcErrorCode::UnknownErrorCodeFromRemote)); |
| return Error::success(); |
| }); |
| AlreadyRegistered = true; |
| } |
| } |
| |
| /// SerializationTraits for Expected<T1> from an Expected<T2>. |
| template <typename ChannelT, typename T1, typename T2> |
| class SerializationTraits<ChannelT, Expected<T1>, Expected<T2>> { |
| public: |
| |
| static Error serialize(ChannelT &C, Expected<T2> &&ValOrErr) { |
| if (ValOrErr) { |
| if (auto Err = serializeSeq(C, true)) |
| return Err; |
| return SerializationTraits<ChannelT, T1, T2>::serialize(C, *ValOrErr); |
| } |
| if (auto Err = serializeSeq(C, false)) |
| return Err; |
| return serializeSeq(C, ValOrErr.takeError()); |
| } |
| |
| static Error deserialize(ChannelT &C, Expected<T2> &ValOrErr) { |
| ExpectedAsOutParameter<T2> EAO(&ValOrErr); |
| bool HasValue; |
| if (auto Err = deserializeSeq(C, HasValue)) |
| return Err; |
| if (HasValue) |
| return SerializationTraits<ChannelT, T1, T2>::deserialize(C, *ValOrErr); |
| Error Err = Error::success(); |
| if (auto E2 = deserializeSeq(C, Err)) |
| return E2; |
| ValOrErr = std::move(Err); |
| return Error::success(); |
| } |
| }; |
| |
| /// SerializationTraits for Expected<T1> from a T2. |
| template <typename ChannelT, typename T1, typename T2> |
| class SerializationTraits<ChannelT, Expected<T1>, T2> { |
| public: |
| |
| static Error serialize(ChannelT &C, T2 &&Val) { |
| return serializeSeq(C, Expected<T2>(std::forward<T2>(Val))); |
| } |
| }; |
| |
| /// SerializationTraits for Expected<T1> from an Error. |
| template <typename ChannelT, typename T> |
| class SerializationTraits<ChannelT, Expected<T>, Error> { |
| public: |
| |
| static Error serialize(ChannelT &C, Error &&Err) { |
| return serializeSeq(C, Expected<T>(std::move(Err))); |
| } |
| }; |
| |
| /// SerializationTraits default specialization for std::pair. |
| template <typename ChannelT, typename T1, typename T2, typename T3, typename T4> |
| class SerializationTraits<ChannelT, std::pair<T1, T2>, std::pair<T3, T4>> { |
| public: |
| static Error serialize(ChannelT &C, const std::pair<T3, T4> &V) { |
| if (auto Err = SerializationTraits<ChannelT, T1, T3>::serialize(C, V.first)) |
| return Err; |
| return SerializationTraits<ChannelT, T2, T4>::serialize(C, V.second); |
| } |
| |
| static Error deserialize(ChannelT &C, std::pair<T3, T4> &V) { |
| if (auto Err = |
| SerializationTraits<ChannelT, T1, T3>::deserialize(C, V.first)) |
| return Err; |
| return SerializationTraits<ChannelT, T2, T4>::deserialize(C, V.second); |
| } |
| }; |
| |
| /// SerializationTraits default specialization for std::tuple. |
| template <typename ChannelT, typename... ArgTs> |
| class SerializationTraits<ChannelT, std::tuple<ArgTs...>> { |
| public: |
| |
| /// RPC channel serialization for std::tuple. |
| static Error serialize(ChannelT &C, const std::tuple<ArgTs...> &V) { |
| return serializeTupleHelper(C, V, llvm::index_sequence_for<ArgTs...>()); |
| } |
| |
| /// RPC channel deserialization for std::tuple. |
| static Error deserialize(ChannelT &C, std::tuple<ArgTs...> &V) { |
| return deserializeTupleHelper(C, V, llvm::index_sequence_for<ArgTs...>()); |
| } |
| |
| private: |
| // Serialization helper for std::tuple. |
| template <size_t... Is> |
| static Error serializeTupleHelper(ChannelT &C, const std::tuple<ArgTs...> &V, |
| llvm::index_sequence<Is...> _) { |
| return serializeSeq(C, std::get<Is>(V)...); |
| } |
| |
| // Serialization helper for std::tuple. |
| template <size_t... Is> |
| static Error deserializeTupleHelper(ChannelT &C, std::tuple<ArgTs...> &V, |
| llvm::index_sequence<Is...> _) { |
| return deserializeSeq(C, std::get<Is>(V)...); |
| } |
| }; |
| |
| /// SerializationTraits default specialization for std::vector. |
| template <typename ChannelT, typename T> |
| class SerializationTraits<ChannelT, std::vector<T>> { |
| public: |
| |
| /// Serialize a std::vector<T> from std::vector<T>. |
| static Error serialize(ChannelT &C, const std::vector<T> &V) { |
| if (auto Err = serializeSeq(C, static_cast<uint64_t>(V.size()))) |
| return Err; |
| |
| for (const auto &E : V) |
| if (auto Err = serializeSeq(C, E)) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| /// Deserialize a std::vector<T> to a std::vector<T>. |
| static Error deserialize(ChannelT &C, std::vector<T> &V) { |
| assert(V.empty() && |
| "Expected default-constructed vector to deserialize into"); |
| |
| uint64_t Count = 0; |
| if (auto Err = deserializeSeq(C, Count)) |
| return Err; |
| |
| V.resize(Count); |
| for (auto &E : V) |
| if (auto Err = deserializeSeq(C, E)) |
| return Err; |
| |
| return Error::success(); |
| } |
| }; |
| |
| template <typename ChannelT, typename T, typename T2> |
| class SerializationTraits<ChannelT, std::set<T>, std::set<T2>> { |
| public: |
| /// Serialize a std::set<T> from std::set<T2>. |
| static Error serialize(ChannelT &C, const std::set<T2> &S) { |
| if (auto Err = serializeSeq(C, static_cast<uint64_t>(S.size()))) |
| return Err; |
| |
| for (const auto &E : S) |
| if (auto Err = SerializationTraits<ChannelT, T, T2>::serialize(C, E)) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| /// Deserialize a std::set<T> to a std::set<T>. |
| static Error deserialize(ChannelT &C, std::set<T2> &S) { |
| assert(S.empty() && "Expected default-constructed set to deserialize into"); |
| |
| uint64_t Count = 0; |
| if (auto Err = deserializeSeq(C, Count)) |
| return Err; |
| |
| while (Count-- != 0) { |
| T2 Val; |
| if (auto Err = SerializationTraits<ChannelT, T, T2>::deserialize(C, Val)) |
| return Err; |
| |
| auto Added = S.insert(Val).second; |
| if (!Added) |
| return make_error<StringError>("Duplicate element in deserialized set", |
| orcError(OrcErrorCode::UnknownORCError)); |
| } |
| |
| return Error::success(); |
| } |
| }; |
| |
| template <typename ChannelT, typename K, typename V, typename K2, typename V2> |
| class SerializationTraits<ChannelT, std::map<K, V>, std::map<K2, V2>> { |
| public: |
| /// Serialize a std::map<K, V> from std::map<K2, V2>. |
| static Error serialize(ChannelT &C, const std::map<K2, V2> &M) { |
| if (auto Err = serializeSeq(C, static_cast<uint64_t>(M.size()))) |
| return Err; |
| |
| for (const auto &E : M) { |
| if (auto Err = |
| SerializationTraits<ChannelT, K, K2>::serialize(C, E.first)) |
| return Err; |
| if (auto Err = |
| SerializationTraits<ChannelT, V, V2>::serialize(C, E.second)) |
| return Err; |
| } |
| |
| return Error::success(); |
| } |
| |
| /// Deserialize a std::map<K, V> to a std::map<K, V>. |
| static Error deserialize(ChannelT &C, std::map<K2, V2> &M) { |
| assert(M.empty() && "Expected default-constructed map to deserialize into"); |
| |
| uint64_t Count = 0; |
| if (auto Err = deserializeSeq(C, Count)) |
| return Err; |
| |
| while (Count-- != 0) { |
| std::pair<K2, V2> Val; |
| if (auto Err = |
| SerializationTraits<ChannelT, K, K2>::deserialize(C, Val.first)) |
| return Err; |
| |
| if (auto Err = |
| SerializationTraits<ChannelT, V, V2>::deserialize(C, Val.second)) |
| return Err; |
| |
| auto Added = M.insert(Val).second; |
| if (!Added) |
| return make_error<StringError>("Duplicate element in deserialized map", |
| orcError(OrcErrorCode::UnknownORCError)); |
| } |
| |
| return Error::success(); |
| } |
| }; |
| |
| } // end namespace rpc |
| } // end namespace orc |
| } // end namespace llvm |
| |
| #endif // LLVM_EXECUTIONENGINE_ORC_RPCSERIALIZATION_H |