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