1 //===--- simple_packed_serialization.h - simple serialization ---*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 // This file is a part of the ORC runtime support library.
11 // The behavior of the utilities in this header must be synchronized with the
12 // behavior of the utilities in
13 // llvm/ExecutionEngine/Orc/Shared/WrapperFunctionUtils.h.
15 // The Simple Packed Serialization (SPS) utilities are used to generate
16 // argument and return buffers for wrapper functions using the following
17 // serialization scheme:
20 // bool, char, int8_t, uint8_t -- Two's complement 8-bit (0=false, 1=true)
21 // int16_t, uint16_t -- Two's complement 16-bit little endian
22 // int32_t, uint32_t -- Two's complement 32-bit little endian
23 // int64_t, int64_t -- Two's complement 64-bit little endian
26 // Serialized as the sequence length (as a uint64_t) followed by the
27 // serialization of each of the elements without padding.
29 // Tuple<T1, ..., TN>:
30 // Serialized as each of the element types from T1 to TN without padding.
32 //===----------------------------------------------------------------------===//
34 #ifndef ORC_RT_SIMPLE_PACKED_SERIALIZATION_H
35 #define ORC_RT_SIMPLE_PACKED_SERIALIZATION_H
38 #include "endianness.h"
40 #include "stl_extras.h"
44 #include <string_view>
46 #include <type_traits>
47 #include <unordered_map>
53 /// Output char buffer with overflow check.
54 class SPSOutputBuffer
{
56 SPSOutputBuffer(char *Buffer
, size_t Remaining
)
57 : Buffer(Buffer
), Remaining(Remaining
) {}
58 bool write(const char *Data
, size_t Size
) {
61 memcpy(Buffer
, Data
, Size
);
68 char *Buffer
= nullptr;
72 /// Input char buffer with underflow check.
73 class SPSInputBuffer
{
75 SPSInputBuffer() = default;
76 SPSInputBuffer(const char *Buffer
, size_t Remaining
)
77 : Buffer(Buffer
), Remaining(Remaining
) {}
78 bool read(char *Data
, size_t Size
) {
81 memcpy(Data
, Buffer
, Size
);
87 const char *data() const { return Buffer
; }
88 bool skip(size_t Size
) {
97 const char *Buffer
= nullptr;
101 /// Specialize to describe how to serialize/deserialize to/from the given
103 template <typename SPSTagT
, typename ConcreteT
, typename _
= void>
104 class SPSSerializationTraits
;
106 /// A utility class for serializing to a blob from a variadic list.
107 template <typename
... ArgTs
> class SPSArgList
;
109 // Empty list specialization for SPSArgList.
110 template <> class SPSArgList
<> {
112 static size_t size() { return 0; }
114 static bool serialize(SPSOutputBuffer
&OB
) { return true; }
115 static bool deserialize(SPSInputBuffer
&IB
) { return true; }
118 // Non-empty list specialization for SPSArgList.
119 template <typename SPSTagT
, typename
... SPSTagTs
>
120 class SPSArgList
<SPSTagT
, SPSTagTs
...> {
122 template <typename ArgT
, typename
... ArgTs
>
123 static size_t size(const ArgT
&Arg
, const ArgTs
&...Args
) {
124 return SPSSerializationTraits
<SPSTagT
, ArgT
>::size(Arg
) +
125 SPSArgList
<SPSTagTs
...>::size(Args
...);
128 template <typename ArgT
, typename
... ArgTs
>
129 static bool serialize(SPSOutputBuffer
&OB
, const ArgT
&Arg
,
130 const ArgTs
&...Args
) {
131 return SPSSerializationTraits
<SPSTagT
, ArgT
>::serialize(OB
, Arg
) &&
132 SPSArgList
<SPSTagTs
...>::serialize(OB
, Args
...);
135 template <typename ArgT
, typename
... ArgTs
>
136 static bool deserialize(SPSInputBuffer
&IB
, ArgT
&Arg
, ArgTs
&...Args
) {
137 return SPSSerializationTraits
<SPSTagT
, ArgT
>::deserialize(IB
, Arg
) &&
138 SPSArgList
<SPSTagTs
...>::deserialize(IB
, Args
...);
142 /// SPS serialization for integral types, bool, and char.
143 template <typename SPSTagT
>
144 class SPSSerializationTraits
<
146 std::enable_if_t
<std::is_same
<SPSTagT
, bool>::value
||
147 std::is_same
<SPSTagT
, char>::value
||
148 std::is_same
<SPSTagT
, int8_t>::value
||
149 std::is_same
<SPSTagT
, int16_t>::value
||
150 std::is_same
<SPSTagT
, int32_t>::value
||
151 std::is_same
<SPSTagT
, int64_t>::value
||
152 std::is_same
<SPSTagT
, uint8_t>::value
||
153 std::is_same
<SPSTagT
, uint16_t>::value
||
154 std::is_same
<SPSTagT
, uint32_t>::value
||
155 std::is_same
<SPSTagT
, uint64_t>::value
>> {
157 static size_t size(const SPSTagT
&Value
) { return sizeof(SPSTagT
); }
159 static bool serialize(SPSOutputBuffer
&OB
, const SPSTagT
&Value
) {
163 return OB
.write(reinterpret_cast<const char *>(&Tmp
), sizeof(Tmp
));
166 static bool deserialize(SPSInputBuffer
&IB
, SPSTagT
&Value
) {
168 if (!IB
.read(reinterpret_cast<char *>(&Tmp
), sizeof(Tmp
)))
177 /// Any empty placeholder suitable as a substitute for void when deserializing
180 /// Represents an address in the executor.
181 class SPSExecutorAddr
{};
183 /// SPS tag type for tuples.
185 /// A blob tuple should be serialized by serializing each of the elements in
187 template <typename
... SPSTagTs
> class SPSTuple
{
189 /// Convenience typedef of the corresponding arg list.
190 typedef SPSArgList
<SPSTagTs
...> AsArgList
;
193 /// SPS tag type for optionals.
195 /// SPSOptionals should be serialized as a bool with true indicating that an
196 /// SPSTagT value is present, and false indicating that there is no value.
197 /// If the boolean is true then the serialized SPSTagT will follow immediately
199 template <typename SPSTagT
> class SPSOptional
{};
201 /// SPS tag type for sequences.
203 /// SPSSequences should be serialized as a uint64_t sequence length,
204 /// followed by the serialization of each of the elements.
205 template <typename SPSElementTagT
> class SPSSequence
;
207 /// SPS tag type for strings, which are equivalent to sequences of chars.
208 using SPSString
= SPSSequence
<char>;
210 /// SPS tag type for maps.
212 /// SPS maps are just sequences of (Key, Value) tuples.
213 template <typename SPSTagT1
, typename SPSTagT2
>
214 using SPSMap
= SPSSequence
<SPSTuple
<SPSTagT1
, SPSTagT2
>>;
216 /// Serialization for SPSEmpty type.
217 template <> class SPSSerializationTraits
<SPSEmpty
, SPSEmpty
> {
219 static size_t size(const SPSEmpty
&EP
) { return 0; }
220 static bool serialize(SPSOutputBuffer
&OB
, const SPSEmpty
&BE
) {
223 static bool deserialize(SPSInputBuffer
&IB
, SPSEmpty
&BE
) { return true; }
226 /// Specialize this to implement 'trivial' sequence serialization for
227 /// a concrete sequence type.
229 /// Trivial sequence serialization uses the sequence's 'size' member to get the
230 /// length of the sequence, and uses a range-based for loop to iterate over the
233 /// Specializing this template class means that you do not need to provide a
234 /// specialization of SPSSerializationTraits for your type.
235 template <typename SPSElementTagT
, typename ConcreteSequenceT
>
236 class TrivialSPSSequenceSerialization
{
238 static constexpr bool available
= false;
241 /// Specialize this to implement 'trivial' sequence deserialization for
242 /// a concrete sequence type.
244 /// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your
245 /// specialization (you must implement this) to reserve space, and then calls
246 /// a static 'append(SequenceT&, ElementT&) method to append each of the
247 /// deserialized elements.
249 /// Specializing this template class means that you do not need to provide a
250 /// specialization of SPSSerializationTraits for your type.
251 template <typename SPSElementTagT
, typename ConcreteSequenceT
>
252 class TrivialSPSSequenceDeserialization
{
254 static constexpr bool available
= false;
257 /// Trivial std::string -> SPSSequence<char> serialization.
258 template <> class TrivialSPSSequenceSerialization
<char, std::string
> {
260 static constexpr bool available
= true;
263 /// Trivial SPSSequence<char> -> std::string deserialization.
264 template <> class TrivialSPSSequenceDeserialization
<char, std::string
> {
266 static constexpr bool available
= true;
268 using element_type
= char;
270 static void reserve(std::string
&S
, uint64_t Size
) { S
.reserve(Size
); }
271 static bool append(std::string
&S
, char C
) {
277 /// Trivial std::vector<T> -> SPSSequence<SPSElementTagT> serialization.
278 template <typename SPSElementTagT
, typename T
>
279 class TrivialSPSSequenceSerialization
<SPSElementTagT
, std::vector
<T
>> {
281 static constexpr bool available
= true;
284 /// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
285 template <typename SPSElementTagT
, typename T
>
286 class TrivialSPSSequenceDeserialization
<SPSElementTagT
, std::vector
<T
>> {
288 static constexpr bool available
= true;
290 using element_type
= typename
std::vector
<T
>::value_type
;
292 static void reserve(std::vector
<T
> &V
, uint64_t Size
) { V
.reserve(Size
); }
293 static bool append(std::vector
<T
> &V
, T E
) {
294 V
.push_back(std::move(E
));
299 /// Trivial std::unordered_map<K, V> -> SPSSequence<SPSTuple<SPSKey, SPSValue>>
301 template <typename SPSKeyTagT
, typename SPSValueTagT
, typename K
, typename V
>
302 class TrivialSPSSequenceSerialization
<SPSTuple
<SPSKeyTagT
, SPSValueTagT
>,
303 std::unordered_map
<K
, V
>> {
305 static constexpr bool available
= true;
308 /// Trivial SPSSequence<SPSTuple<SPSKey, SPSValue>> -> std::unordered_map<K, V>
310 template <typename SPSKeyTagT
, typename SPSValueTagT
, typename K
, typename V
>
311 class TrivialSPSSequenceDeserialization
<SPSTuple
<SPSKeyTagT
, SPSValueTagT
>,
312 std::unordered_map
<K
, V
>> {
314 static constexpr bool available
= true;
316 using element_type
= std::pair
<K
, V
>;
318 static void reserve(std::unordered_map
<K
, V
> &M
, uint64_t Size
) {
321 static bool append(std::unordered_map
<K
, V
> &M
, element_type E
) {
322 return M
.insert(std::move(E
)).second
;
326 /// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size
327 /// followed by a for-earch loop over the elements of the sequence to serialize
329 template <typename SPSElementTagT
, typename SequenceT
>
330 class SPSSerializationTraits
<SPSSequence
<SPSElementTagT
>, SequenceT
,
331 std::enable_if_t
<TrivialSPSSequenceSerialization
<
332 SPSElementTagT
, SequenceT
>::available
>> {
334 static size_t size(const SequenceT
&S
) {
335 size_t Size
= SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size()));
336 for (const auto &E
: S
)
337 Size
+= SPSArgList
<SPSElementTagT
>::size(E
);
341 static bool serialize(SPSOutputBuffer
&OB
, const SequenceT
&S
) {
342 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
344 for (const auto &E
: S
)
345 if (!SPSArgList
<SPSElementTagT
>::serialize(OB
, E
))
350 static bool deserialize(SPSInputBuffer
&IB
, SequenceT
&S
) {
351 using TBSD
= TrivialSPSSequenceDeserialization
<SPSElementTagT
, SequenceT
>;
353 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
355 TBSD::reserve(S
, Size
);
356 for (size_t I
= 0; I
!= Size
; ++I
) {
357 typename
TBSD::element_type E
;
358 if (!SPSArgList
<SPSElementTagT
>::deserialize(IB
, E
))
360 if (!TBSD::append(S
, std::move(E
)))
367 /// Trivial serialization / deserialization for span<char>
368 template <> class SPSSerializationTraits
<SPSSequence
<char>, span
<const char>> {
370 static size_t size(const span
<const char> &S
) {
371 return SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size())) +
374 static bool serialize(SPSOutputBuffer
&OB
, const span
<const char> &S
) {
375 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
377 return OB
.write(S
.data(), S
.size());
379 static bool deserialize(SPSInputBuffer
&IB
, span
<const char> &S
) {
381 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
383 S
= span
<const char>(IB
.data(), Size
);
384 return IB
.skip(Size
);
388 /// SPSTuple serialization for std::pair.
389 template <typename SPSTagT1
, typename SPSTagT2
, typename T1
, typename T2
>
390 class SPSSerializationTraits
<SPSTuple
<SPSTagT1
, SPSTagT2
>, std::pair
<T1
, T2
>> {
392 static size_t size(const std::pair
<T1
, T2
> &P
) {
393 return SPSArgList
<SPSTagT1
>::size(P
.first
) +
394 SPSArgList
<SPSTagT2
>::size(P
.second
);
397 static bool serialize(SPSOutputBuffer
&OB
, const std::pair
<T1
, T2
> &P
) {
398 return SPSArgList
<SPSTagT1
>::serialize(OB
, P
.first
) &&
399 SPSArgList
<SPSTagT2
>::serialize(OB
, P
.second
);
402 static bool deserialize(SPSInputBuffer
&IB
, std::pair
<T1
, T2
> &P
) {
403 return SPSArgList
<SPSTagT1
>::deserialize(IB
, P
.first
) &&
404 SPSArgList
<SPSTagT2
>::deserialize(IB
, P
.second
);
408 /// SPSOptional serialization for std::optional.
409 template <typename SPSTagT
, typename T
>
410 class SPSSerializationTraits
<SPSOptional
<SPSTagT
>, std::optional
<T
>> {
412 static size_t size(const std::optional
<T
> &Value
) {
413 size_t Size
= SPSArgList
<bool>::size(!!Value
);
415 Size
+= SPSArgList
<SPSTagT
>::size(*Value
);
419 static bool serialize(SPSOutputBuffer
&OB
, const std::optional
<T
> &Value
) {
420 if (!SPSArgList
<bool>::serialize(OB
, !!Value
))
423 return SPSArgList
<SPSTagT
>::serialize(OB
, *Value
);
427 static bool deserialize(SPSInputBuffer
&IB
, std::optional
<T
> &Value
) {
429 if (!SPSArgList
<bool>::deserialize(IB
, HasValue
))
433 return SPSArgList
<SPSTagT
>::deserialize(IB
, *Value
);
435 Value
= std::optional
<T
>();
440 /// Serialization for string_views.
442 /// Serialization is as for regular strings. Deserialization points directly
444 template <> class SPSSerializationTraits
<SPSString
, std::string_view
> {
446 static size_t size(const std::string_view
&S
) {
447 return SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size())) +
451 static bool serialize(SPSOutputBuffer
&OB
, const std::string_view
&S
) {
452 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
454 return OB
.write(S
.data(), S
.size());
457 static bool deserialize(SPSInputBuffer
&IB
, std::string_view
&S
) {
458 const char *Data
= nullptr;
460 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
462 if (Size
> std::numeric_limits
<size_t>::max())
467 S
= {Data
, static_cast<size_t>(Size
)};
472 /// SPS tag type for errors.
475 /// SPS tag type for expecteds, which are either a T or a string representing
477 template <typename SPSTagT
> class SPSExpected
;
481 /// Helper type for serializing Errors.
483 /// llvm::Errors are move-only, and not inspectable except by consuming them.
484 /// This makes them unsuitable for direct serialization via
485 /// SPSSerializationTraits, which needs to inspect values twice (once to
486 /// determine the amount of space to reserve, and then again to serialize).
488 /// The SPSSerializableError type is a helper that can be
489 /// constructed from an llvm::Error, but inspected more than once.
490 struct SPSSerializableError
{
491 bool HasError
= false;
495 /// Helper type for serializing Expected<T>s.
497 /// See SPSSerializableError for more details.
499 // FIXME: Use std::variant for storage once we have c++17.
500 template <typename T
> struct SPSSerializableExpected
{
501 bool HasValue
= false;
506 inline SPSSerializableError
toSPSSerializable(Error Err
) {
508 return {true, toString(std::move(Err
))};
512 inline Error
fromSPSSerializable(SPSSerializableError BSE
) {
514 return make_error
<StringError
>(BSE
.ErrMsg
);
515 return Error::success();
518 template <typename T
>
519 SPSSerializableExpected
<T
> toSPSSerializable(Expected
<T
> E
) {
521 return {true, std::move(*E
), {}};
523 return {false, {}, toString(E
.takeError())};
526 template <typename T
>
527 Expected
<T
> fromSPSSerializable(SPSSerializableExpected
<T
> BSE
) {
529 return std::move(BSE
.Value
);
531 return make_error
<StringError
>(BSE
.ErrMsg
);
534 } // end namespace detail
536 /// Serialize to a SPSError from a detail::SPSSerializableError.
538 class SPSSerializationTraits
<SPSError
, detail::SPSSerializableError
> {
540 static size_t size(const detail::SPSSerializableError
&BSE
) {
541 size_t Size
= SPSArgList
<bool>::size(BSE
.HasError
);
543 Size
+= SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
547 static bool serialize(SPSOutputBuffer
&OB
,
548 const detail::SPSSerializableError
&BSE
) {
549 if (!SPSArgList
<bool>::serialize(OB
, BSE
.HasError
))
552 if (!SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
))
557 static bool deserialize(SPSInputBuffer
&IB
,
558 detail::SPSSerializableError
&BSE
) {
559 if (!SPSArgList
<bool>::deserialize(IB
, BSE
.HasError
))
565 return SPSArgList
<SPSString
>::deserialize(IB
, BSE
.ErrMsg
);
569 /// Serialize to a SPSExpected<SPSTagT> from a
570 /// detail::SPSSerializableExpected<T>.
571 template <typename SPSTagT
, typename T
>
572 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>,
573 detail::SPSSerializableExpected
<T
>> {
575 static size_t size(const detail::SPSSerializableExpected
<T
> &BSE
) {
576 size_t Size
= SPSArgList
<bool>::size(BSE
.HasValue
);
578 Size
+= SPSArgList
<SPSTagT
>::size(BSE
.Value
);
580 Size
+= SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
584 static bool serialize(SPSOutputBuffer
&OB
,
585 const detail::SPSSerializableExpected
<T
> &BSE
) {
586 if (!SPSArgList
<bool>::serialize(OB
, BSE
.HasValue
))
590 return SPSArgList
<SPSTagT
>::serialize(OB
, BSE
.Value
);
592 return SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
);
595 static bool deserialize(SPSInputBuffer
&IB
,
596 detail::SPSSerializableExpected
<T
> &BSE
) {
597 if (!SPSArgList
<bool>::deserialize(IB
, BSE
.HasValue
))
601 return SPSArgList
<SPSTagT
>::deserialize(IB
, BSE
.Value
);
603 return SPSArgList
<SPSString
>::deserialize(IB
, BSE
.ErrMsg
);
607 /// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.
608 template <typename SPSTagT
>
609 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>,
610 detail::SPSSerializableError
> {
612 static size_t size(const detail::SPSSerializableError
&BSE
) {
613 assert(BSE
.HasError
&& "Cannot serialize expected from a success value");
614 return SPSArgList
<bool>::size(false) +
615 SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
618 static bool serialize(SPSOutputBuffer
&OB
,
619 const detail::SPSSerializableError
&BSE
) {
620 assert(BSE
.HasError
&& "Cannot serialize expected from a success value");
621 if (!SPSArgList
<bool>::serialize(OB
, false))
623 return SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
);
627 /// Serialize to a SPSExpected<SPSTagT> from a T.
628 template <typename SPSTagT
, typename T
>
629 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>, T
> {
631 static size_t size(const T
&Value
) {
632 return SPSArgList
<bool>::size(true) + SPSArgList
<SPSTagT
>::size(Value
);
635 static bool serialize(SPSOutputBuffer
&OB
, const T
&Value
) {
636 if (!SPSArgList
<bool>::serialize(OB
, true))
638 return SPSArgList
<SPSTagT
>::serialize(Value
);
642 } // end namespace __orc_rt
644 #endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H