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:
SPSOutputBuffer(char * Buffer,size_t Remaining)53 SPSOutputBuffer(char *Buffer, size_t Remaining)
54 : Buffer(Buffer), Remaining(Remaining) {}
write(const char * Data,size_t Size)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;
SPSInputBuffer(const char * Buffer,size_t Remaining)73 SPSInputBuffer(const char *Buffer, size_t Remaining)
74 : Buffer(Buffer), Remaining(Remaining) {}
read(char * Data,size_t Size)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
data()84 const char *data() const { return Buffer; }
skip(size_t Size)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:
size()109 static size_t size() { return 0; }
110
serialize(SPSOutputBuffer & OB)111 static bool serialize(SPSOutputBuffer &OB) { return true; }
deserialize(SPSInputBuffer & IB)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>
size(const ArgT & Arg,const ArgTs &...Args)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>
serialize(SPSOutputBuffer & OB,const ArgT & Arg,const ArgTs &...Args)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>
deserialize(SPSInputBuffer & IB,ArgT & Arg,ArgTs &...Args)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:
size(const SPSTagT & Value)154 static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); }
155
serialize(SPSOutputBuffer & OB,const SPSTagT & Value)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
deserialize(SPSInputBuffer & IB,SPSTagT & Value)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:
size(const SPSEmpty & EP)212 static size_t size(const SPSEmpty &EP) { return 0; }
serialize(SPSOutputBuffer & OB,const SPSEmpty & BE)213 static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) {
214 return true;
215 }
deserialize(SPSInputBuffer & IB,SPSEmpty & BE)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
reserve(std::string & S,uint64_t Size)263 static void reserve(std::string &S, uint64_t Size) { S.reserve(Size); }
append(std::string & S,char C)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
reserve(std::vector<T> & V,uint64_t Size)285 static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); }
append(std::vector<T> & V,T E)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:
size(const SequenceT & S)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
serialize(SPSOutputBuffer & OB,const SequenceT & S)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
deserialize(SPSInputBuffer & IB,SequenceT & S)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:
size(const std::pair<T1,T2> & P)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
serialize(SPSOutputBuffer & OB,const std::pair<T1,T2> & P)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
deserialize(SPSInputBuffer & IB,std::pair<T1,T2> & P)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:
size(const StringRef & S)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
serialize(SPSOutputBuffer & OB,StringRef S)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
deserialize(SPSInputBuffer & IB,StringRef & S)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
toSPSSerializable(Error Err)417 inline SPSSerializableError toSPSSerializable(Error Err) {
418 if (Err)
419 return {true, toString(std::move(Err))};
420 return {false, {}};
421 }
422
fromSPSSerializable(SPSSerializableError BSE)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>
toSPSSerializable(Expected<T> E)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>
fromSPSSerializable(SPSSerializableExpected<T> BSE)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:
size(const detail::SPSSerializableError & BSE)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
serialize(SPSOutputBuffer & OB,const detail::SPSSerializableError & BSE)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
deserialize(SPSInputBuffer & IB,detail::SPSSerializableError & BSE)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:
size(const detail::SPSSerializableExpected<T> & BSE)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
serialize(SPSOutputBuffer & OB,const detail::SPSSerializableExpected<T> & BSE)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
deserialize(SPSInputBuffer & IB,detail::SPSSerializableExpected<T> & BSE)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:
size(const detail::SPSSerializableError & BSE)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
serialize(SPSOutputBuffer & OB,const detail::SPSSerializableError & BSE)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:
size(const T & Value)542 static size_t size(const T &Value) {
543 return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value);
544 }
545
serialize(SPSOutputBuffer & OB,const T & Value)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