xref: /freebsd/contrib/llvm-project/compiler-rt/lib/orc/simple_packed_serialization.h (revision 370e009188ba90c3290b1479aa06ec98b66e140a)
1 //===--- simple_packed_serialization.h - simple serialization ---*- C++ -*-===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file is a part of the ORC runtime support library.
10 //
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.
14 //
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:
18 //
19 // Primitives:
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
24 //
25 // Sequence<T>:
26 //   Serialized as the sequence length (as a uint64_t) followed by the
27 //   serialization of each of the elements without padding.
28 //
29 // Tuple<T1, ..., TN>:
30 //   Serialized as each of the element types from T1 to TN without padding.
31 //
32 //===----------------------------------------------------------------------===//
33 
34 #ifndef ORC_RT_SIMPLE_PACKED_SERIALIZATION_H
35 #define ORC_RT_SIMPLE_PACKED_SERIALIZATION_H
36 
37 #include "adt.h"
38 #include "endianness.h"
39 #include "error.h"
40 #include "stl_extras.h"
41 
42 #include <string>
43 #include <tuple>
44 #include <type_traits>
45 #include <unordered_map>
46 #include <utility>
47 #include <vector>
48 
49 namespace __orc_rt {
50 
51 /// Output char buffer with overflow check.
52 class SPSOutputBuffer {
53 public:
54   SPSOutputBuffer(char *Buffer, size_t Remaining)
55       : Buffer(Buffer), Remaining(Remaining) {}
56   bool write(const char *Data, size_t Size) {
57     if (Size > Remaining)
58       return false;
59     memcpy(Buffer, Data, Size);
60     Buffer += Size;
61     Remaining -= Size;
62     return true;
63   }
64 
65 private:
66   char *Buffer = nullptr;
67   size_t Remaining = 0;
68 };
69 
70 /// Input char buffer with underflow check.
71 class SPSInputBuffer {
72 public:
73   SPSInputBuffer() = default;
74   SPSInputBuffer(const char *Buffer, size_t Remaining)
75       : Buffer(Buffer), Remaining(Remaining) {}
76   bool read(char *Data, size_t Size) {
77     if (Size > Remaining)
78       return false;
79     memcpy(Data, Buffer, Size);
80     Buffer += Size;
81     Remaining -= Size;
82     return true;
83   }
84 
85   const char *data() const { return Buffer; }
86   bool skip(size_t Size) {
87     if (Size > Remaining)
88       return false;
89     Buffer += Size;
90     Remaining -= Size;
91     return true;
92   }
93 
94 private:
95   const char *Buffer = nullptr;
96   size_t Remaining = 0;
97 };
98 
99 /// Specialize to describe how to serialize/deserialize to/from the given
100 /// concrete type.
101 template <typename SPSTagT, typename ConcreteT, typename _ = void>
102 class SPSSerializationTraits;
103 
104 /// A utility class for serializing to a blob from a variadic list.
105 template <typename... ArgTs> class SPSArgList;
106 
107 // Empty list specialization for SPSArgList.
108 template <> class SPSArgList<> {
109 public:
110   static size_t size() { return 0; }
111 
112   static bool serialize(SPSOutputBuffer &OB) { return true; }
113   static bool deserialize(SPSInputBuffer &IB) { return true; }
114 };
115 
116 // Non-empty list specialization for SPSArgList.
117 template <typename SPSTagT, typename... SPSTagTs>
118 class SPSArgList<SPSTagT, SPSTagTs...> {
119 public:
120   template <typename ArgT, typename... ArgTs>
121   static size_t size(const ArgT &Arg, const ArgTs &...Args) {
122     return SPSSerializationTraits<SPSTagT, ArgT>::size(Arg) +
123            SPSArgList<SPSTagTs...>::size(Args...);
124   }
125 
126   template <typename ArgT, typename... ArgTs>
127   static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg,
128                         const ArgTs &...Args) {
129     return SPSSerializationTraits<SPSTagT, ArgT>::serialize(OB, Arg) &&
130            SPSArgList<SPSTagTs...>::serialize(OB, Args...);
131   }
132 
133   template <typename ArgT, typename... ArgTs>
134   static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) {
135     return SPSSerializationTraits<SPSTagT, ArgT>::deserialize(IB, Arg) &&
136            SPSArgList<SPSTagTs...>::deserialize(IB, Args...);
137   }
138 };
139 
140 /// SPS serialization for integral types, bool, and char.
141 template <typename SPSTagT>
142 class SPSSerializationTraits<
143     SPSTagT, SPSTagT,
144     std::enable_if_t<std::is_same<SPSTagT, bool>::value ||
145                      std::is_same<SPSTagT, char>::value ||
146                      std::is_same<SPSTagT, int8_t>::value ||
147                      std::is_same<SPSTagT, int16_t>::value ||
148                      std::is_same<SPSTagT, int32_t>::value ||
149                      std::is_same<SPSTagT, int64_t>::value ||
150                      std::is_same<SPSTagT, uint8_t>::value ||
151                      std::is_same<SPSTagT, uint16_t>::value ||
152                      std::is_same<SPSTagT, uint32_t>::value ||
153                      std::is_same<SPSTagT, uint64_t>::value>> {
154 public:
155   static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); }
156 
157   static bool serialize(SPSOutputBuffer &OB, const SPSTagT &Value) {
158     SPSTagT Tmp = Value;
159     if (IsBigEndianHost)
160       swapByteOrder(Tmp);
161     return OB.write(reinterpret_cast<const char *>(&Tmp), sizeof(Tmp));
162   }
163 
164   static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) {
165     SPSTagT Tmp;
166     if (!IB.read(reinterpret_cast<char *>(&Tmp), sizeof(Tmp)))
167       return false;
168     if (IsBigEndianHost)
169       swapByteOrder(Tmp);
170     Value = Tmp;
171     return true;
172   }
173 };
174 
175 /// Any empty placeholder suitable as a substitute for void when deserializing
176 class SPSEmpty {};
177 
178 /// Represents an address in the executor.
179 class SPSExecutorAddr {};
180 
181 /// SPS tag type for tuples.
182 ///
183 /// A blob tuple should be serialized by serializing each of the elements in
184 /// sequence.
185 template <typename... SPSTagTs> class SPSTuple {
186 public:
187   /// Convenience typedef of the corresponding arg list.
188   typedef SPSArgList<SPSTagTs...> AsArgList;
189 };
190 
191 /// SPS tag type for sequences.
192 ///
193 /// SPSSequences should be serialized as a uint64_t sequence length,
194 /// followed by the serialization of each of the elements.
195 template <typename SPSElementTagT> class SPSSequence;
196 
197 /// SPS tag type for strings, which are equivalent to sequences of chars.
198 using SPSString = SPSSequence<char>;
199 
200 /// SPS tag type for maps.
201 ///
202 /// SPS maps are just sequences of (Key, Value) tuples.
203 template <typename SPSTagT1, typename SPSTagT2>
204 using SPSMap = SPSSequence<SPSTuple<SPSTagT1, SPSTagT2>>;
205 
206 /// Serialization for SPSEmpty type.
207 template <> class SPSSerializationTraits<SPSEmpty, SPSEmpty> {
208 public:
209   static size_t size(const SPSEmpty &EP) { return 0; }
210   static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) {
211     return true;
212   }
213   static bool deserialize(SPSInputBuffer &IB, SPSEmpty &BE) { return true; }
214 };
215 
216 /// Specialize this to implement 'trivial' sequence serialization for
217 /// a concrete sequence type.
218 ///
219 /// Trivial sequence serialization uses the sequence's 'size' member to get the
220 /// length of the sequence, and uses a range-based for loop to iterate over the
221 /// elements.
222 ///
223 /// Specializing this template class means that you do not need to provide a
224 /// specialization of SPSSerializationTraits for your type.
225 template <typename SPSElementTagT, typename ConcreteSequenceT>
226 class TrivialSPSSequenceSerialization {
227 public:
228   static constexpr bool available = false;
229 };
230 
231 /// Specialize this to implement 'trivial' sequence deserialization for
232 /// a concrete sequence type.
233 ///
234 /// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your
235 /// specialization (you must implement this) to reserve space, and then calls
236 /// a static 'append(SequenceT&, ElementT&) method to append each of the
237 /// deserialized elements.
238 ///
239 /// Specializing this template class means that you do not need to provide a
240 /// specialization of SPSSerializationTraits for your type.
241 template <typename SPSElementTagT, typename ConcreteSequenceT>
242 class TrivialSPSSequenceDeserialization {
243 public:
244   static constexpr bool available = false;
245 };
246 
247 /// Trivial std::string -> SPSSequence<char> serialization.
248 template <> class TrivialSPSSequenceSerialization<char, std::string> {
249 public:
250   static constexpr bool available = true;
251 };
252 
253 /// Trivial SPSSequence<char> -> std::string deserialization.
254 template <> class TrivialSPSSequenceDeserialization<char, std::string> {
255 public:
256   static constexpr bool available = true;
257 
258   using element_type = char;
259 
260   static void reserve(std::string &S, uint64_t Size) { S.reserve(Size); }
261   static bool append(std::string &S, char C) {
262     S.push_back(C);
263     return true;
264   }
265 };
266 
267 /// Trivial std::vector<T> -> SPSSequence<SPSElementTagT> serialization.
268 template <typename SPSElementTagT, typename T>
269 class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> {
270 public:
271   static constexpr bool available = true;
272 };
273 
274 /// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization.
275 template <typename SPSElementTagT, typename T>
276 class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> {
277 public:
278   static constexpr bool available = true;
279 
280   using element_type = typename std::vector<T>::value_type;
281 
282   static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); }
283   static bool append(std::vector<T> &V, T E) {
284     V.push_back(std::move(E));
285     return true;
286   }
287 };
288 
289 /// Trivial std::unordered_map<K, V> -> SPSSequence<SPSTuple<SPSKey, SPSValue>>
290 /// serialization.
291 template <typename SPSKeyTagT, typename SPSValueTagT, typename K, typename V>
292 class TrivialSPSSequenceSerialization<SPSTuple<SPSKeyTagT, SPSValueTagT>,
293                                       std::unordered_map<K, V>> {
294 public:
295   static constexpr bool available = true;
296 };
297 
298 /// Trivial SPSSequence<SPSTuple<SPSKey, SPSValue>> -> std::unordered_map<K, V>
299 /// deserialization.
300 template <typename SPSKeyTagT, typename SPSValueTagT, typename K, typename V>
301 class TrivialSPSSequenceDeserialization<SPSTuple<SPSKeyTagT, SPSValueTagT>,
302                                         std::unordered_map<K, V>> {
303 public:
304   static constexpr bool available = true;
305 
306   using element_type = std::pair<K, V>;
307 
308   static void reserve(std::unordered_map<K, V> &M, uint64_t Size) {
309     M.reserve(Size);
310   }
311   static bool append(std::unordered_map<K, V> &M, element_type E) {
312     return M.insert(std::move(E)).second;
313   }
314 };
315 
316 /// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size
317 /// followed by a for-earch loop over the elements of the sequence to serialize
318 /// each of them.
319 template <typename SPSElementTagT, typename SequenceT>
320 class SPSSerializationTraits<SPSSequence<SPSElementTagT>, SequenceT,
321                              std::enable_if_t<TrivialSPSSequenceSerialization<
322                                  SPSElementTagT, SequenceT>::available>> {
323 public:
324   static size_t size(const SequenceT &S) {
325     size_t Size = SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size()));
326     for (const auto &E : S)
327       Size += SPSArgList<SPSElementTagT>::size(E);
328     return Size;
329   }
330 
331   static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) {
332     if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))
333       return false;
334     for (const auto &E : S)
335       if (!SPSArgList<SPSElementTagT>::serialize(OB, E))
336         return false;
337     return true;
338   }
339 
340   static bool deserialize(SPSInputBuffer &IB, SequenceT &S) {
341     using TBSD = TrivialSPSSequenceDeserialization<SPSElementTagT, SequenceT>;
342     uint64_t Size;
343     if (!SPSArgList<uint64_t>::deserialize(IB, Size))
344       return false;
345     TBSD::reserve(S, Size);
346     for (size_t I = 0; I != Size; ++I) {
347       typename TBSD::element_type E;
348       if (!SPSArgList<SPSElementTagT>::deserialize(IB, E))
349         return false;
350       if (!TBSD::append(S, std::move(E)))
351         return false;
352     }
353     return true;
354   }
355 };
356 
357 /// Trivial serialization / deserialization for span<char>
358 template <> class SPSSerializationTraits<SPSSequence<char>, span<const char>> {
359 public:
360   static size_t size(const span<const char> &S) {
361     return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +
362            S.size();
363   }
364   static bool serialize(SPSOutputBuffer &OB, const span<const char> &S) {
365     if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))
366       return false;
367     return OB.write(S.data(), S.size());
368   }
369   static bool deserialize(SPSInputBuffer &IB, span<const char> &S) {
370     uint64_t Size;
371     if (!SPSArgList<uint64_t>::deserialize(IB, Size))
372       return false;
373     S = span<const char>(IB.data(), Size);
374     return IB.skip(Size);
375   }
376 };
377 
378 /// SPSTuple serialization for std::pair.
379 template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2>
380 class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> {
381 public:
382   static size_t size(const std::pair<T1, T2> &P) {
383     return SPSArgList<SPSTagT1>::size(P.first) +
384            SPSArgList<SPSTagT2>::size(P.second);
385   }
386 
387   static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) {
388     return SPSArgList<SPSTagT1>::serialize(OB, P.first) &&
389            SPSArgList<SPSTagT2>::serialize(OB, P.second);
390   }
391 
392   static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) {
393     return SPSArgList<SPSTagT1>::deserialize(IB, P.first) &&
394            SPSArgList<SPSTagT2>::deserialize(IB, P.second);
395   }
396 };
397 
398 /// Serialization for string_views.
399 ///
400 /// Serialization is as for regular strings. Deserialization points directly
401 /// into the blob.
402 template <> class SPSSerializationTraits<SPSString, __orc_rt::string_view> {
403 public:
404   static size_t size(const __orc_rt::string_view &S) {
405     return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) +
406            S.size();
407   }
408 
409   static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) {
410     if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size())))
411       return false;
412     return OB.write(S.data(), S.size());
413   }
414 
415   static bool deserialize(SPSInputBuffer &IB, __orc_rt::string_view &S) {
416     const char *Data = nullptr;
417     uint64_t Size;
418     if (!SPSArgList<uint64_t>::deserialize(IB, Size))
419       return false;
420     if (Size > std::numeric_limits<size_t>::max())
421       return false;
422     Data = IB.data();
423     if (!IB.skip(Size))
424       return false;
425     S = {Data, static_cast<size_t>(Size)};
426     return true;
427   }
428 };
429 
430 /// SPS tag type for errors.
431 class SPSError;
432 
433 /// SPS tag type for expecteds, which are either a T or a string representing
434 /// an error.
435 template <typename SPSTagT> class SPSExpected;
436 
437 namespace detail {
438 
439 /// Helper type for serializing Errors.
440 ///
441 /// llvm::Errors are move-only, and not inspectable except by consuming them.
442 /// This makes them unsuitable for direct serialization via
443 /// SPSSerializationTraits, which needs to inspect values twice (once to
444 /// determine the amount of space to reserve, and then again to serialize).
445 ///
446 /// The SPSSerializableError type is a helper that can be
447 /// constructed from an llvm::Error, but inspected more than once.
448 struct SPSSerializableError {
449   bool HasError = false;
450   std::string ErrMsg;
451 };
452 
453 /// Helper type for serializing Expected<T>s.
454 ///
455 /// See SPSSerializableError for more details.
456 ///
457 // FIXME: Use std::variant for storage once we have c++17.
458 template <typename T> struct SPSSerializableExpected {
459   bool HasValue = false;
460   T Value{};
461   std::string ErrMsg;
462 };
463 
464 inline SPSSerializableError toSPSSerializable(Error Err) {
465   if (Err)
466     return {true, toString(std::move(Err))};
467   return {false, {}};
468 }
469 
470 inline Error fromSPSSerializable(SPSSerializableError BSE) {
471   if (BSE.HasError)
472     return make_error<StringError>(BSE.ErrMsg);
473   return Error::success();
474 }
475 
476 template <typename T>
477 SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) {
478   if (E)
479     return {true, std::move(*E), {}};
480   else
481     return {false, {}, toString(E.takeError())};
482 }
483 
484 template <typename T>
485 Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) {
486   if (BSE.HasValue)
487     return std::move(BSE.Value);
488   else
489     return make_error<StringError>(BSE.ErrMsg);
490 }
491 
492 } // end namespace detail
493 
494 /// Serialize to a SPSError from a detail::SPSSerializableError.
495 template <>
496 class SPSSerializationTraits<SPSError, detail::SPSSerializableError> {
497 public:
498   static size_t size(const detail::SPSSerializableError &BSE) {
499     size_t Size = SPSArgList<bool>::size(BSE.HasError);
500     if (BSE.HasError)
501       Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
502     return Size;
503   }
504 
505   static bool serialize(SPSOutputBuffer &OB,
506                         const detail::SPSSerializableError &BSE) {
507     if (!SPSArgList<bool>::serialize(OB, BSE.HasError))
508       return false;
509     if (BSE.HasError)
510       if (!SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg))
511         return false;
512     return true;
513   }
514 
515   static bool deserialize(SPSInputBuffer &IB,
516                           detail::SPSSerializableError &BSE) {
517     if (!SPSArgList<bool>::deserialize(IB, BSE.HasError))
518       return false;
519 
520     if (!BSE.HasError)
521       return true;
522 
523     return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
524   }
525 };
526 
527 /// Serialize to a SPSExpected<SPSTagT> from a
528 /// detail::SPSSerializableExpected<T>.
529 template <typename SPSTagT, typename T>
530 class SPSSerializationTraits<SPSExpected<SPSTagT>,
531                              detail::SPSSerializableExpected<T>> {
532 public:
533   static size_t size(const detail::SPSSerializableExpected<T> &BSE) {
534     size_t Size = SPSArgList<bool>::size(BSE.HasValue);
535     if (BSE.HasValue)
536       Size += SPSArgList<SPSTagT>::size(BSE.Value);
537     else
538       Size += SPSArgList<SPSString>::size(BSE.ErrMsg);
539     return Size;
540   }
541 
542   static bool serialize(SPSOutputBuffer &OB,
543                         const detail::SPSSerializableExpected<T> &BSE) {
544     if (!SPSArgList<bool>::serialize(OB, BSE.HasValue))
545       return false;
546 
547     if (BSE.HasValue)
548       return SPSArgList<SPSTagT>::serialize(OB, BSE.Value);
549 
550     return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
551   }
552 
553   static bool deserialize(SPSInputBuffer &IB,
554                           detail::SPSSerializableExpected<T> &BSE) {
555     if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue))
556       return false;
557 
558     if (BSE.HasValue)
559       return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value);
560 
561     return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg);
562   }
563 };
564 
565 /// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError.
566 template <typename SPSTagT>
567 class SPSSerializationTraits<SPSExpected<SPSTagT>,
568                              detail::SPSSerializableError> {
569 public:
570   static size_t size(const detail::SPSSerializableError &BSE) {
571     assert(BSE.HasError && "Cannot serialize expected from a success value");
572     return SPSArgList<bool>::size(false) +
573            SPSArgList<SPSString>::size(BSE.ErrMsg);
574   }
575 
576   static bool serialize(SPSOutputBuffer &OB,
577                         const detail::SPSSerializableError &BSE) {
578     assert(BSE.HasError && "Cannot serialize expected from a success value");
579     if (!SPSArgList<bool>::serialize(OB, false))
580       return false;
581     return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg);
582   }
583 };
584 
585 /// Serialize to a SPSExpected<SPSTagT> from a T.
586 template <typename SPSTagT, typename T>
587 class SPSSerializationTraits<SPSExpected<SPSTagT>, T> {
588 public:
589   static size_t size(const T &Value) {
590     return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value);
591   }
592 
593   static bool serialize(SPSOutputBuffer &OB, const T &Value) {
594     if (!SPSArgList<bool>::serialize(OB, true))
595       return false;
596     return SPSArgList<SPSTagT>::serialize(Value);
597   }
598 };
599 
600 } // end namespace __orc_rt
601 
602 #endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H
603