Path: blob/main/contrib/llvm-project/compiler-rt/lib/orc/simple_packed_serialization.h
39566 views
//===--- simple_packed_serialization.h - simple serialization ---*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of the ORC runtime support library.9//10// The behavior of the utilities in this header must be synchronized with the11// behavior of the utilities in12// llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h.13//14// The Simple Packed Serialization (SPS) utilities are used to generate15// argument and return buffers for wrapper functions using the following16// serialization scheme:17//18// Primitives:19// bool, char, int8_t, uint8_t -- Two's complement 8-bit (0=false, 1=true)20// int16_t, uint16_t -- Two's complement 16-bit little endian21// int32_t, uint32_t -- Two's complement 32-bit little endian22// int64_t, int64_t -- Two's complement 64-bit little endian23//24// Sequence<T>:25// Serialized as the sequence length (as a uint64_t) followed by the26// serialization of each of the elements without padding.27//28// Tuple<T1, ..., TN>:29// Serialized as each of the element types from T1 to TN without padding.30//31//===----------------------------------------------------------------------===//3233#ifndef ORC_RT_SIMPLE_PACKED_SERIALIZATION_H34#define ORC_RT_SIMPLE_PACKED_SERIALIZATION_H3536#include "adt.h"37#include "endianness.h"38#include "error.h"39#include "stl_extras.h"4041#include <optional>42#include <string>43#include <string_view>44#include <tuple>45#include <type_traits>46#include <unordered_map>47#include <utility>48#include <vector>4950namespace __orc_rt {5152/// Output char buffer with overflow check.53class SPSOutputBuffer {54public:55SPSOutputBuffer(char *Buffer, size_t Remaining)56: Buffer(Buffer), Remaining(Remaining) {}57bool write(const char *Data, size_t Size) {58if (Size > Remaining)59return false;60memcpy(Buffer, Data, Size);61Buffer += Size;62Remaining -= Size;63return true;64}6566private:67char *Buffer = nullptr;68size_t Remaining = 0;69};7071/// Input char buffer with underflow check.72class SPSInputBuffer {73public:74SPSInputBuffer() = default;75SPSInputBuffer(const char *Buffer, size_t Remaining)76: Buffer(Buffer), Remaining(Remaining) {}77bool read(char *Data, size_t Size) {78if (Size > Remaining)79return false;80memcpy(Data, Buffer, Size);81Buffer += Size;82Remaining -= Size;83return true;84}8586const char *data() const { return Buffer; }87bool skip(size_t Size) {88if (Size > Remaining)89return false;90Buffer += Size;91Remaining -= Size;92return true;93}9495private:96const char *Buffer = nullptr;97size_t Remaining = 0;98};99100/// Specialize to describe how to serialize/deserialize to/from the given101/// concrete type.102template <typename SPSTagT, typename ConcreteT, typename _ = void>103class SPSSerializationTraits;104105/// A utility class for serializing to a blob from a variadic list.106template <typename... ArgTs> class SPSArgList;107108// Empty list specialization for SPSArgList.109template <> class SPSArgList<> {110public:111static size_t size() { return 0; }112113static bool serialize(SPSOutputBuffer &OB) { return true; }114static bool deserialize(SPSInputBuffer &IB) { return true; }115};116117// Non-empty list specialization for SPSArgList.118template <typename SPSTagT, typename... SPSTagTs>119class SPSArgList<SPSTagT, SPSTagTs...> {120public:121template <typename ArgT, typename... ArgTs>122static size_t size(const ArgT &Arg, const ArgTs &...Args) {123return SPSSerializationTraits<SPSTagT, ArgT>::size(Arg) +124SPSArgList<SPSTagTs...>::size(Args...);125}126127template <typename ArgT, typename... ArgTs>128static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg,129const ArgTs &...Args) {130return SPSSerializationTraits<SPSTagT, ArgT>::serialize(OB, Arg) &&131SPSArgList<SPSTagTs...>::serialize(OB, Args...);132}133134template <typename ArgT, typename... ArgTs>135static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) {136return SPSSerializationTraits<SPSTagT, ArgT>::deserialize(IB, Arg) &&137SPSArgList<SPSTagTs...>::deserialize(IB, Args...);138}139};140141/// SPS serialization for integral types, bool, and char.142template <typename SPSTagT>143class SPSSerializationTraits<144SPSTagT, SPSTagT,145std::enable_if_t<std::is_same<SPSTagT, bool>::value ||146std::is_same<SPSTagT, char>::value ||147std::is_same<SPSTagT, int8_t>::value ||148std::is_same<SPSTagT, int16_t>::value ||149std::is_same<SPSTagT, int32_t>::value ||150std::is_same<SPSTagT, int64_t>::value ||151std::is_same<SPSTagT, uint8_t>::value ||152std::is_same<SPSTagT, uint16_t>::value ||153std::is_same<SPSTagT, uint32_t>::value ||154std::is_same<SPSTagT, uint64_t>::value>> {155public:156static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); }157158static bool serialize(SPSOutputBuffer &OB, const SPSTagT &Value) {159SPSTagT Tmp = Value;160if (IsBigEndianHost)161swapByteOrder(Tmp);162return OB.write(reinterpret_cast<const char *>(&Tmp), sizeof(Tmp));163}164165static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) {166SPSTagT Tmp;167if (!IB.read(reinterpret_cast<char *>(&Tmp), sizeof(Tmp)))168return false;169if (IsBigEndianHost)170swapByteOrder(Tmp);171Value = Tmp;172return true;173}174};175176/// Any empty placeholder suitable as a substitute for void when deserializing177class SPSEmpty {};178179/// Represents an address in the executor.180class SPSExecutorAddr {};181182/// SPS tag type for tuples.183///184/// A blob tuple should be serialized by serializing each of the elements in185/// sequence.186template <typename... SPSTagTs> class SPSTuple {187public:188/// Convenience typedef of the corresponding arg list.189typedef SPSArgList<SPSTagTs...> AsArgList;190};191192/// SPS tag type for optionals.193///194/// SPSOptionals should be serialized as a bool with true indicating that an195/// SPSTagT value is present, and false indicating that there is no value.196/// If the boolean is true then the serialized SPSTagT will follow immediately197/// after it.198template <typename SPSTagT> class SPSOptional {};199200/// SPS tag type for sequences.201///202/// SPSSequences should be serialized as a uint64_t sequence length,203/// followed by the serialization of each of the elements.204template <typename SPSElementTagT> class SPSSequence;205206/// SPS tag type for strings, which are equivalent to sequences of chars.207using SPSString = SPSSequence<char>;208209/// SPS tag type for maps.210///211/// SPS maps are just sequences of (Key, Value) tuples.212template <typename SPSTagT1, typename SPSTagT2>213using SPSMap = SPSSequence<SPSTuple<SPSTagT1, SPSTagT2>>;214215/// Serialization for SPSEmpty type.216template <> class SPSSerializationTraits<SPSEmpty, SPSEmpty> {217public:218static size_t size(const SPSEmpty &EP) { return 0; }219static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) {220return true;221}222static bool deserialize(SPSInputBuffer &IB, SPSEmpty &BE) { return true; }223};224225/// Specialize this to implement 'trivial' sequence serialization for226/// a concrete sequence type.227///228/// Trivial sequence serialization uses the sequence's 'size' member to get the229/// length of the sequence, and uses a range-based for loop to iterate over the230/// elements.231///232/// Specializing this template class means that you do not need to provide a233/// specialization of SPSSerializationTraits for your type.234template <typename SPSElementTagT, typename ConcreteSequenceT>235class TrivialSPSSequenceSerialization {236public:237static constexpr bool available = false;238};239240/// Specialize this to implement 'trivial' sequence deserialization for241/// a concrete sequence type.242///243/// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your244/// specialization (you must implement this) to reserve space, and then calls245/// a static 'append(SequenceT&, ElementT&) method to append each of the246/// deserialized elements.247///248/// Specializing this template class means that you do not need to provide a249/// specialization of SPSSerializationTraits for your type.250template <typename SPSElementTagT, typename ConcreteSequenceT>251class TrivialSPSSequenceDeserialization {252public:253static constexpr bool available = false;254};255256/// Trivial std::string -> SPSSequence<char> serialization.257template <> class TrivialSPSSequenceSerialization<char, std::string> {258public:259static constexpr bool available = true;260};261262/// Trivial SPSSequence<char> -> std::string deserialization.263template <> class TrivialSPSSequenceDeserialization<char, std::string> {264public:265static constexpr bool available = true;266267using element_type = char;268269static void reserve(std::string &S, uint64_t Size) { S.reserve(Size); }270static bool append(std::string &S, char C) {271S.push_back(C);272return true;273}274};275276/// Trivial std::vector<T> -> SPSSequence<SPSElementTagT> serialization.277template <typename SPSElementTagT, typename T>278class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> {279public:280static constexpr bool available = true;281};282283/// Trivial span<T> -> SPSSequence<SPSElementTagT> serialization.284template <typename SPSElementTagT, typename T>285class TrivialSPSSequenceSerialization<SPSElementTagT, span<T>> {286public:287static constexpr bool available = true;288};289290/// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.291template <typename SPSElementTagT, typename T>292class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> {293public:294static constexpr bool available = true;295296using element_type = typename std::vector<T>::value_type;297298static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); }299static bool append(std::vector<T> &V, T E) {300V.push_back(std::move(E));301return true;302}303};304305/// Trivial std::unordered_map<K, V> -> SPSSequence<SPSTuple<SPSKey, SPSValue>>306/// serialization.307template <typename SPSKeyTagT, typename SPSValueTagT, typename K, typename V>308class TrivialSPSSequenceSerialization<SPSTuple<SPSKeyTagT, SPSValueTagT>,309std::unordered_map<K, V>> {310public:311static constexpr bool available = true;312};313314/// Trivial SPSSequence<SPSTuple<SPSKey, SPSValue>> -> std::unordered_map<K, V>315/// deserialization.316template <typename SPSKeyTagT, typename SPSValueTagT, typename K, typename V>317class TrivialSPSSequenceDeserialization<SPSTuple<SPSKeyTagT, SPSValueTagT>,318std::unordered_map<K, V>> {319public:320static constexpr bool available = true;321322using element_type = std::pair<K, V>;323324static void reserve(std::unordered_map<K, V> &M, uint64_t Size) {325M.reserve(Size);326}327static bool append(std::unordered_map<K, V> &M, element_type E) {328return M.insert(std::move(E)).second;329}330};331332/// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size333/// followed by a for-earch loop over the elements of the sequence to serialize334/// each of them.335template <typename SPSElementTagT, typename SequenceT>336class SPSSerializationTraits<SPSSequence<SPSElementTagT>, SequenceT,337std::enable_if_t<TrivialSPSSequenceSerialization<338SPSElementTagT, SequenceT>::available>> {339public:340static size_t size(const SequenceT &S) {341size_t Size = SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size()));342for (const auto &E : S)343Size += SPSArgList<SPSElementTagT>::size(E);344return Size;345}346347static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) {348if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))349return false;350for (const auto &E : S)351if (!SPSArgList<SPSElementTagT>::serialize(OB, E))352return false;353return true;354}355356static bool deserialize(SPSInputBuffer &IB, SequenceT &S) {357using TBSD = TrivialSPSSequenceDeserialization<SPSElementTagT, SequenceT>;358uint64_t Size;359if (!SPSArgList<uint64_t>::deserialize(IB, Size))360return false;361TBSD::reserve(S, Size);362for (size_t I = 0; I != Size; ++I) {363typename TBSD::element_type E;364if (!SPSArgList<SPSElementTagT>::deserialize(IB, E))365return false;366if (!TBSD::append(S, std::move(E)))367return false;368}369return true;370}371};372373/// Trivial serialization / deserialization for span<char>374template <> class SPSSerializationTraits<SPSSequence<char>, span<const char>> {375public:376static size_t size(const span<const char> &S) {377return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +378S.size();379}380static bool serialize(SPSOutputBuffer &OB, const span<const char> &S) {381if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))382return false;383return OB.write(S.data(), S.size());384}385static bool deserialize(SPSInputBuffer &IB, span<const char> &S) {386uint64_t Size;387if (!SPSArgList<uint64_t>::deserialize(IB, Size))388return false;389S = span<const char>(IB.data(), Size);390return IB.skip(Size);391}392};393394/// SPSTuple serialization for std::tuple.395template <typename... SPSTagTs, typename... Ts>396class SPSSerializationTraits<SPSTuple<SPSTagTs...>, std::tuple<Ts...>> {397private:398using TupleArgList = typename SPSTuple<SPSTagTs...>::AsArgList;399using ArgIndices = std::make_index_sequence<sizeof...(Ts)>;400401template <std::size_t... I>402static size_t size(const std::tuple<Ts...> &T, std::index_sequence<I...>) {403return TupleArgList::size(std::get<I>(T)...);404}405406template <std::size_t... I>407static bool serialize(SPSOutputBuffer &OB, const std::tuple<Ts...> &T,408std::index_sequence<I...>) {409return TupleArgList::serialize(OB, std::get<I>(T)...);410}411412template <std::size_t... I>413static bool deserialize(SPSInputBuffer &IB, std::tuple<Ts...> &T,414std::index_sequence<I...>) {415return TupleArgList::deserialize(IB, std::get<I>(T)...);416}417418public:419static size_t size(const std::tuple<Ts...> &T) {420return size(T, ArgIndices{});421}422423static bool serialize(SPSOutputBuffer &OB, const std::tuple<Ts...> &T) {424return serialize(OB, T, ArgIndices{});425}426427static bool deserialize(SPSInputBuffer &IB, std::tuple<Ts...> &T) {428return deserialize(IB, T, ArgIndices{});429}430};431432/// SPSTuple serialization for std::pair.433template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2>434class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> {435public:436static size_t size(const std::pair<T1, T2> &P) {437return SPSArgList<SPSTagT1>::size(P.first) +438SPSArgList<SPSTagT2>::size(P.second);439}440441static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) {442return SPSArgList<SPSTagT1>::serialize(OB, P.first) &&443SPSArgList<SPSTagT2>::serialize(OB, P.second);444}445446static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) {447return SPSArgList<SPSTagT1>::deserialize(IB, P.first) &&448SPSArgList<SPSTagT2>::deserialize(IB, P.second);449}450};451452/// SPSOptional serialization for std::optional.453template <typename SPSTagT, typename T>454class SPSSerializationTraits<SPSOptional<SPSTagT>, std::optional<T>> {455public:456static size_t size(const std::optional<T> &Value) {457size_t Size = SPSArgList<bool>::size(!!Value);458if (Value)459Size += SPSArgList<SPSTagT>::size(*Value);460return Size;461}462463static bool serialize(SPSOutputBuffer &OB, const std::optional<T> &Value) {464if (!SPSArgList<bool>::serialize(OB, !!Value))465return false;466if (Value)467return SPSArgList<SPSTagT>::serialize(OB, *Value);468return true;469}470471static bool deserialize(SPSInputBuffer &IB, std::optional<T> &Value) {472bool HasValue;473if (!SPSArgList<bool>::deserialize(IB, HasValue))474return false;475if (HasValue) {476Value = T();477return SPSArgList<SPSTagT>::deserialize(IB, *Value);478} else479Value = std::optional<T>();480return true;481}482};483484/// Serialization for string_views.485///486/// Serialization is as for regular strings. Deserialization points directly487/// into the blob.488template <> class SPSSerializationTraits<SPSString, std::string_view> {489public:490static size_t size(const std::string_view &S) {491return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +492S.size();493}494495static bool serialize(SPSOutputBuffer &OB, const std::string_view &S) {496if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))497return false;498return OB.write(S.data(), S.size());499}500501static bool deserialize(SPSInputBuffer &IB, std::string_view &S) {502const char *Data = nullptr;503uint64_t Size;504if (!SPSArgList<uint64_t>::deserialize(IB, Size))505return false;506if (Size > std::numeric_limits<size_t>::max())507return false;508Data = IB.data();509if (!IB.skip(Size))510return false;511S = {Data, static_cast<size_t>(Size)};512return true;513}514};515516/// SPS tag type for errors.517class SPSError;518519/// SPS tag type for expecteds, which are either a T or a string representing520/// an error.521template <typename SPSTagT> class SPSExpected;522523namespace detail {524525/// Helper type for serializing Errors.526///527/// llvm::Errors are move-only, and not inspectable except by consuming them.528/// This makes them unsuitable for direct serialization via529/// SPSSerializationTraits, which needs to inspect values twice (once to530/// determine the amount of space to reserve, and then again to serialize).531///532/// The SPSSerializableError type is a helper that can be533/// constructed from an llvm::Error, but inspected more than once.534struct SPSSerializableError {535bool HasError = false;536std::string ErrMsg;537};538539/// Helper type for serializing Expected<T>s.540///541/// See SPSSerializableError for more details.542///543// FIXME: Use std::variant for storage once we have c++17.544template <typename T> struct SPSSerializableExpected {545bool HasValue = false;546T Value{};547std::string ErrMsg;548};549550inline SPSSerializableError toSPSSerializable(Error Err) {551if (Err)552return {true, toString(std::move(Err))};553return {false, {}};554}555556inline Error fromSPSSerializable(SPSSerializableError BSE) {557if (BSE.HasError)558return make_error<StringError>(BSE.ErrMsg);559return Error::success();560}561562template <typename T>563SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) {564if (E)565return {true, std::move(*E), {}};566else567return {false, {}, toString(E.takeError())};568}569570template <typename T>571Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) {572if (BSE.HasValue)573return std::move(BSE.Value);574else575return make_error<StringError>(BSE.ErrMsg);576}577578} // end namespace detail579580/// Serialize to a SPSError from a detail::SPSSerializableError.581template <>582class SPSSerializationTraits<SPSError, detail::SPSSerializableError> {583public:584static size_t size(const detail::SPSSerializableError &BSE) {585size_t Size = SPSArgList<bool>::size(BSE.HasError);586if (BSE.HasError)587Size += SPSArgList<SPSString>::size(BSE.ErrMsg);588return Size;589}590591static bool serialize(SPSOutputBuffer &OB,592const detail::SPSSerializableError &BSE) {593if (!SPSArgList<bool>::serialize(OB, BSE.HasError))594return false;595if (BSE.HasError)596if (!SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg))597return false;598return true;599}600601static bool deserialize(SPSInputBuffer &IB,602detail::SPSSerializableError &BSE) {603if (!SPSArgList<bool>::deserialize(IB, BSE.HasError))604return false;605606if (!BSE.HasError)607return true;608609return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);610}611};612613/// Serialize to a SPSExpected<SPSTagT> from a614/// detail::SPSSerializableExpected<T>.615template <typename SPSTagT, typename T>616class SPSSerializationTraits<SPSExpected<SPSTagT>,617detail::SPSSerializableExpected<T>> {618public:619static size_t size(const detail::SPSSerializableExpected<T> &BSE) {620size_t Size = SPSArgList<bool>::size(BSE.HasValue);621if (BSE.HasValue)622Size += SPSArgList<SPSTagT>::size(BSE.Value);623else624Size += SPSArgList<SPSString>::size(BSE.ErrMsg);625return Size;626}627628static bool serialize(SPSOutputBuffer &OB,629const detail::SPSSerializableExpected<T> &BSE) {630if (!SPSArgList<bool>::serialize(OB, BSE.HasValue))631return false;632633if (BSE.HasValue)634return SPSArgList<SPSTagT>::serialize(OB, BSE.Value);635636return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);637}638639static bool deserialize(SPSInputBuffer &IB,640detail::SPSSerializableExpected<T> &BSE) {641if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue))642return false;643644if (BSE.HasValue)645return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value);646647return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);648}649};650651/// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.652template <typename SPSTagT>653class SPSSerializationTraits<SPSExpected<SPSTagT>,654detail::SPSSerializableError> {655public:656static size_t size(const detail::SPSSerializableError &BSE) {657assert(BSE.HasError && "Cannot serialize expected from a success value");658return SPSArgList<bool>::size(false) +659SPSArgList<SPSString>::size(BSE.ErrMsg);660}661662static bool serialize(SPSOutputBuffer &OB,663const detail::SPSSerializableError &BSE) {664assert(BSE.HasError && "Cannot serialize expected from a success value");665if (!SPSArgList<bool>::serialize(OB, false))666return false;667return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);668}669};670671/// Serialize to a SPSExpected<SPSTagT> from a T.672template <typename SPSTagT, typename T>673class SPSSerializationTraits<SPSExpected<SPSTagT>, T> {674public:675static size_t size(const T &Value) {676return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value);677}678679static bool serialize(SPSOutputBuffer &OB, const T &Value) {680if (!SPSArgList<bool>::serialize(OB, true))681return false;682return SPSArgList<SPSTagT>::serialize(Value);683}684};685686} // end namespace __orc_rt687688#endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H689690691