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 span<T> -> SPSSequence<SPSElementTagT> serialization.
285 template <typename SPSElementTagT
, typename T
>
286 class TrivialSPSSequenceSerialization
<SPSElementTagT
, span
<T
>> {
288 static constexpr bool available
= true;
291 /// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
292 template <typename SPSElementTagT
, typename T
>
293 class TrivialSPSSequenceDeserialization
<SPSElementTagT
, std::vector
<T
>> {
295 static constexpr bool available
= true;
297 using element_type
= typename
std::vector
<T
>::value_type
;
299 static void reserve(std::vector
<T
> &V
, uint64_t Size
) { V
.reserve(Size
); }
300 static bool append(std::vector
<T
> &V
, T E
) {
301 V
.push_back(std::move(E
));
306 /// Trivial std::unordered_map<K, V> -> SPSSequence<SPSTuple<SPSKey, SPSValue>>
308 template <typename SPSKeyTagT
, typename SPSValueTagT
, typename K
, typename V
>
309 class TrivialSPSSequenceSerialization
<SPSTuple
<SPSKeyTagT
, SPSValueTagT
>,
310 std::unordered_map
<K
, V
>> {
312 static constexpr bool available
= true;
315 /// Trivial SPSSequence<SPSTuple<SPSKey, SPSValue>> -> std::unordered_map<K, V>
317 template <typename SPSKeyTagT
, typename SPSValueTagT
, typename K
, typename V
>
318 class TrivialSPSSequenceDeserialization
<SPSTuple
<SPSKeyTagT
, SPSValueTagT
>,
319 std::unordered_map
<K
, V
>> {
321 static constexpr bool available
= true;
323 using element_type
= std::pair
<K
, V
>;
325 static void reserve(std::unordered_map
<K
, V
> &M
, uint64_t Size
) {
328 static bool append(std::unordered_map
<K
, V
> &M
, element_type E
) {
329 return M
.insert(std::move(E
)).second
;
333 /// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size
334 /// followed by a for-earch loop over the elements of the sequence to serialize
336 template <typename SPSElementTagT
, typename SequenceT
>
337 class SPSSerializationTraits
<SPSSequence
<SPSElementTagT
>, SequenceT
,
338 std::enable_if_t
<TrivialSPSSequenceSerialization
<
339 SPSElementTagT
, SequenceT
>::available
>> {
341 static size_t size(const SequenceT
&S
) {
342 size_t Size
= SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size()));
343 for (const auto &E
: S
)
344 Size
+= SPSArgList
<SPSElementTagT
>::size(E
);
348 static bool serialize(SPSOutputBuffer
&OB
, const SequenceT
&S
) {
349 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
351 for (const auto &E
: S
)
352 if (!SPSArgList
<SPSElementTagT
>::serialize(OB
, E
))
357 static bool deserialize(SPSInputBuffer
&IB
, SequenceT
&S
) {
358 using TBSD
= TrivialSPSSequenceDeserialization
<SPSElementTagT
, SequenceT
>;
360 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
362 TBSD::reserve(S
, Size
);
363 for (size_t I
= 0; I
!= Size
; ++I
) {
364 typename
TBSD::element_type E
;
365 if (!SPSArgList
<SPSElementTagT
>::deserialize(IB
, E
))
367 if (!TBSD::append(S
, std::move(E
)))
374 /// Trivial serialization / deserialization for span<char>
375 template <> class SPSSerializationTraits
<SPSSequence
<char>, span
<const char>> {
377 static size_t size(const span
<const char> &S
) {
378 return SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size())) +
381 static bool serialize(SPSOutputBuffer
&OB
, const span
<const char> &S
) {
382 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
384 return OB
.write(S
.data(), S
.size());
386 static bool deserialize(SPSInputBuffer
&IB
, span
<const char> &S
) {
388 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
390 S
= span
<const char>(IB
.data(), Size
);
391 return IB
.skip(Size
);
395 /// SPSTuple serialization for std::tuple.
396 template <typename
... SPSTagTs
, typename
... Ts
>
397 class SPSSerializationTraits
<SPSTuple
<SPSTagTs
...>, std::tuple
<Ts
...>> {
399 using TupleArgList
= typename SPSTuple
<SPSTagTs
...>::AsArgList
;
400 using ArgIndices
= std::make_index_sequence
<sizeof...(Ts
)>;
402 template <std::size_t... I
>
403 static size_t size(const std::tuple
<Ts
...> &T
, std::index_sequence
<I
...>) {
404 return TupleArgList::size(std::get
<I
>(T
)...);
407 template <std::size_t... I
>
408 static bool serialize(SPSOutputBuffer
&OB
, const std::tuple
<Ts
...> &T
,
409 std::index_sequence
<I
...>) {
410 return TupleArgList::serialize(OB
, std::get
<I
>(T
)...);
413 template <std::size_t... I
>
414 static bool deserialize(SPSInputBuffer
&IB
, std::tuple
<Ts
...> &T
,
415 std::index_sequence
<I
...>) {
416 return TupleArgList::deserialize(IB
, std::get
<I
>(T
)...);
420 static size_t size(const std::tuple
<Ts
...> &T
) {
421 return size(T
, ArgIndices
{});
424 static bool serialize(SPSOutputBuffer
&OB
, const std::tuple
<Ts
...> &T
) {
425 return serialize(OB
, T
, ArgIndices
{});
428 static bool deserialize(SPSInputBuffer
&IB
, std::tuple
<Ts
...> &T
) {
429 return deserialize(IB
, T
, ArgIndices
{});
433 /// SPSTuple serialization for std::pair.
434 template <typename SPSTagT1
, typename SPSTagT2
, typename T1
, typename T2
>
435 class SPSSerializationTraits
<SPSTuple
<SPSTagT1
, SPSTagT2
>, std::pair
<T1
, T2
>> {
437 static size_t size(const std::pair
<T1
, T2
> &P
) {
438 return SPSArgList
<SPSTagT1
>::size(P
.first
) +
439 SPSArgList
<SPSTagT2
>::size(P
.second
);
442 static bool serialize(SPSOutputBuffer
&OB
, const std::pair
<T1
, T2
> &P
) {
443 return SPSArgList
<SPSTagT1
>::serialize(OB
, P
.first
) &&
444 SPSArgList
<SPSTagT2
>::serialize(OB
, P
.second
);
447 static bool deserialize(SPSInputBuffer
&IB
, std::pair
<T1
, T2
> &P
) {
448 return SPSArgList
<SPSTagT1
>::deserialize(IB
, P
.first
) &&
449 SPSArgList
<SPSTagT2
>::deserialize(IB
, P
.second
);
453 /// SPSOptional serialization for std::optional.
454 template <typename SPSTagT
, typename T
>
455 class SPSSerializationTraits
<SPSOptional
<SPSTagT
>, std::optional
<T
>> {
457 static size_t size(const std::optional
<T
> &Value
) {
458 size_t Size
= SPSArgList
<bool>::size(!!Value
);
460 Size
+= SPSArgList
<SPSTagT
>::size(*Value
);
464 static bool serialize(SPSOutputBuffer
&OB
, const std::optional
<T
> &Value
) {
465 if (!SPSArgList
<bool>::serialize(OB
, !!Value
))
468 return SPSArgList
<SPSTagT
>::serialize(OB
, *Value
);
472 static bool deserialize(SPSInputBuffer
&IB
, std::optional
<T
> &Value
) {
474 if (!SPSArgList
<bool>::deserialize(IB
, HasValue
))
478 return SPSArgList
<SPSTagT
>::deserialize(IB
, *Value
);
480 Value
= std::optional
<T
>();
485 /// Serialization for string_views.
487 /// Serialization is as for regular strings. Deserialization points directly
489 template <> class SPSSerializationTraits
<SPSString
, std::string_view
> {
491 static size_t size(const std::string_view
&S
) {
492 return SPSArgList
<uint64_t>::size(static_cast<uint64_t>(S
.size())) +
496 static bool serialize(SPSOutputBuffer
&OB
, const std::string_view
&S
) {
497 if (!SPSArgList
<uint64_t>::serialize(OB
, static_cast<uint64_t>(S
.size())))
499 return OB
.write(S
.data(), S
.size());
502 static bool deserialize(SPSInputBuffer
&IB
, std::string_view
&S
) {
503 const char *Data
= nullptr;
505 if (!SPSArgList
<uint64_t>::deserialize(IB
, Size
))
507 if (Size
> std::numeric_limits
<size_t>::max())
512 S
= {Data
, static_cast<size_t>(Size
)};
517 /// SPS tag type for errors.
520 /// SPS tag type for expecteds, which are either a T or a string representing
522 template <typename SPSTagT
> class SPSExpected
;
526 /// Helper type for serializing Errors.
528 /// llvm::Errors are move-only, and not inspectable except by consuming them.
529 /// This makes them unsuitable for direct serialization via
530 /// SPSSerializationTraits, which needs to inspect values twice (once to
531 /// determine the amount of space to reserve, and then again to serialize).
533 /// The SPSSerializableError type is a helper that can be
534 /// constructed from an llvm::Error, but inspected more than once.
535 struct SPSSerializableError
{
536 bool HasError
= false;
540 /// Helper type for serializing Expected<T>s.
542 /// See SPSSerializableError for more details.
544 // FIXME: Use std::variant for storage once we have c++17.
545 template <typename T
> struct SPSSerializableExpected
{
546 bool HasValue
= false;
551 inline SPSSerializableError
toSPSSerializable(Error Err
) {
553 return {true, toString(std::move(Err
))};
557 inline Error
fromSPSSerializable(SPSSerializableError BSE
) {
559 return make_error
<StringError
>(BSE
.ErrMsg
);
560 return Error::success();
563 template <typename T
>
564 SPSSerializableExpected
<T
> toSPSSerializable(Expected
<T
> E
) {
566 return {true, std::move(*E
), {}};
568 return {false, {}, toString(E
.takeError())};
571 template <typename T
>
572 Expected
<T
> fromSPSSerializable(SPSSerializableExpected
<T
> BSE
) {
574 return std::move(BSE
.Value
);
576 return make_error
<StringError
>(BSE
.ErrMsg
);
579 } // namespace detail
581 /// Serialize to a SPSError from a detail::SPSSerializableError.
583 class SPSSerializationTraits
<SPSError
, detail::SPSSerializableError
> {
585 static size_t size(const detail::SPSSerializableError
&BSE
) {
586 size_t Size
= SPSArgList
<bool>::size(BSE
.HasError
);
588 Size
+= SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
592 static bool serialize(SPSOutputBuffer
&OB
,
593 const detail::SPSSerializableError
&BSE
) {
594 if (!SPSArgList
<bool>::serialize(OB
, BSE
.HasError
))
597 if (!SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
))
602 static bool deserialize(SPSInputBuffer
&IB
,
603 detail::SPSSerializableError
&BSE
) {
604 if (!SPSArgList
<bool>::deserialize(IB
, BSE
.HasError
))
610 return SPSArgList
<SPSString
>::deserialize(IB
, BSE
.ErrMsg
);
614 /// Serialize to a SPSExpected<SPSTagT> from a
615 /// detail::SPSSerializableExpected<T>.
616 template <typename SPSTagT
, typename T
>
617 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>,
618 detail::SPSSerializableExpected
<T
>> {
620 static size_t size(const detail::SPSSerializableExpected
<T
> &BSE
) {
621 size_t Size
= SPSArgList
<bool>::size(BSE
.HasValue
);
623 Size
+= SPSArgList
<SPSTagT
>::size(BSE
.Value
);
625 Size
+= SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
629 static bool serialize(SPSOutputBuffer
&OB
,
630 const detail::SPSSerializableExpected
<T
> &BSE
) {
631 if (!SPSArgList
<bool>::serialize(OB
, BSE
.HasValue
))
635 return SPSArgList
<SPSTagT
>::serialize(OB
, BSE
.Value
);
637 return SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
);
640 static bool deserialize(SPSInputBuffer
&IB
,
641 detail::SPSSerializableExpected
<T
> &BSE
) {
642 if (!SPSArgList
<bool>::deserialize(IB
, BSE
.HasValue
))
646 return SPSArgList
<SPSTagT
>::deserialize(IB
, BSE
.Value
);
648 return SPSArgList
<SPSString
>::deserialize(IB
, BSE
.ErrMsg
);
652 /// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.
653 template <typename SPSTagT
>
654 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>,
655 detail::SPSSerializableError
> {
657 static size_t size(const detail::SPSSerializableError
&BSE
) {
658 assert(BSE
.HasError
&& "Cannot serialize expected from a success value");
659 return SPSArgList
<bool>::size(false) +
660 SPSArgList
<SPSString
>::size(BSE
.ErrMsg
);
663 static bool serialize(SPSOutputBuffer
&OB
,
664 const detail::SPSSerializableError
&BSE
) {
665 assert(BSE
.HasError
&& "Cannot serialize expected from a success value");
666 if (!SPSArgList
<bool>::serialize(OB
, false))
668 return SPSArgList
<SPSString
>::serialize(OB
, BSE
.ErrMsg
);
672 /// Serialize to a SPSExpected<SPSTagT> from a T.
673 template <typename SPSTagT
, typename T
>
674 class SPSSerializationTraits
<SPSExpected
<SPSTagT
>, T
> {
676 static size_t size(const T
&Value
) {
677 return SPSArgList
<bool>::size(true) + SPSArgList
<SPSTagT
>::size(Value
);
680 static bool serialize(SPSOutputBuffer
&OB
, const T
&Value
) {
681 if (!SPSArgList
<bool>::serialize(OB
, true))
683 return SPSArgList
<SPSTagT
>::serialize(Value
);
687 } // namespace orc_rt
689 #endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H