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/SmallVector.h" |
37 | #include "llvm/ADT/StringMap.h" |
38 | #include "llvm/ADT/StringRef.h" |
39 | #include "llvm/Support/Error.h" |
40 | #include "llvm/Support/SwapByteOrder.h" |
41 | |
42 | #include <limits> |
43 | #include <optional> |
44 | #include <string> |
45 | #include <tuple> |
46 | #include <type_traits> |
47 | #include <utility> |
48 | #include <vector> |
49 | |
50 | namespace llvm { |
51 | namespace orc { |
52 | namespace shared { |
53 | |
54 | /// Output char buffer with overflow check. |
55 | class SPSOutputBuffer { |
56 | public: |
57 | SPSOutputBuffer(char *Buffer, size_t Remaining) |
58 | : Buffer(Buffer), Remaining(Remaining) {} |
59 | bool write(const char *Data, size_t Size) { |
60 | assert(Data && "Data must not be null" ); |
61 | if (Size > Remaining) |
62 | return false; |
63 | memcpy(dest: Buffer, src: Data, n: Size); |
64 | Buffer += Size; |
65 | Remaining -= Size; |
66 | return true; |
67 | } |
68 | |
69 | private: |
70 | char *Buffer = nullptr; |
71 | size_t Remaining = 0; |
72 | }; |
73 | |
74 | /// Input char buffer with underflow check. |
75 | class SPSInputBuffer { |
76 | public: |
77 | SPSInputBuffer() = default; |
78 | SPSInputBuffer(const char *Buffer, size_t Remaining) |
79 | : Buffer(Buffer), Remaining(Remaining) {} |
80 | bool read(char *Data, size_t Size) { |
81 | if (Size > Remaining) |
82 | return false; |
83 | memcpy(dest: Data, src: Buffer, n: Size); |
84 | Buffer += Size; |
85 | Remaining -= Size; |
86 | return true; |
87 | } |
88 | |
89 | const char *data() const { return Buffer; } |
90 | bool skip(size_t Size) { |
91 | if (Size > Remaining) |
92 | return false; |
93 | Buffer += Size; |
94 | Remaining -= Size; |
95 | return true; |
96 | } |
97 | |
98 | private: |
99 | const char *Buffer = nullptr; |
100 | size_t Remaining = 0; |
101 | }; |
102 | |
103 | /// Specialize to describe how to serialize/deserialize to/from the given |
104 | /// concrete type. |
105 | template <typename SPSTagT, typename ConcreteT, typename _ = void> |
106 | class SPSSerializationTraits; |
107 | |
108 | /// A utility class for serializing to a blob from a variadic list. |
109 | template <typename... ArgTs> class SPSArgList; |
110 | |
111 | // Empty list specialization for SPSArgList. |
112 | template <> class SPSArgList<> { |
113 | public: |
114 | static size_t size() { return 0; } |
115 | |
116 | static bool serialize(SPSOutputBuffer &OB) { return true; } |
117 | static bool deserialize(SPSInputBuffer &IB) { return true; } |
118 | |
119 | static bool serializeToSmallVector(SmallVectorImpl<char> &V) { return true; } |
120 | |
121 | static bool deserializeFromSmallVector(const SmallVectorImpl<char> &V) { |
122 | return true; |
123 | } |
124 | }; |
125 | |
126 | // Non-empty list specialization for SPSArgList. |
127 | template <typename SPSTagT, typename... SPSTagTs> |
128 | class SPSArgList<SPSTagT, SPSTagTs...> { |
129 | public: |
130 | // FIXME: This typedef is here to enable SPS arg serialization from |
131 | // JITLink. It can be removed once JITLink can access SPS directly. |
132 | using OutputBuffer = SPSOutputBuffer; |
133 | |
134 | template <typename ArgT, typename... ArgTs> |
135 | static size_t size(const ArgT &Arg, const ArgTs &...Args) { |
136 | return SPSSerializationTraits<SPSTagT, ArgT>::size(Arg) + |
137 | SPSArgList<SPSTagTs...>::size(Args...); |
138 | } |
139 | |
140 | template <typename ArgT, typename... ArgTs> |
141 | static bool serialize(SPSOutputBuffer &OB, const ArgT &Arg, |
142 | const ArgTs &...Args) { |
143 | return SPSSerializationTraits<SPSTagT, ArgT>::serialize(OB, Arg) && |
144 | SPSArgList<SPSTagTs...>::serialize(OB, Args...); |
145 | } |
146 | |
147 | template <typename ArgT, typename... ArgTs> |
148 | static bool deserialize(SPSInputBuffer &IB, ArgT &Arg, ArgTs &...Args) { |
149 | return SPSSerializationTraits<SPSTagT, ArgT>::deserialize(IB, Arg) && |
150 | SPSArgList<SPSTagTs...>::deserialize(IB, Args...); |
151 | } |
152 | }; |
153 | |
154 | /// SPS serialization for integral types, bool, and char. |
155 | template <typename SPSTagT> |
156 | class SPSSerializationTraits< |
157 | SPSTagT, SPSTagT, |
158 | std::enable_if_t<std::is_same<SPSTagT, bool>::value || |
159 | std::is_same<SPSTagT, char>::value || |
160 | std::is_same<SPSTagT, int8_t>::value || |
161 | std::is_same<SPSTagT, int16_t>::value || |
162 | std::is_same<SPSTagT, int32_t>::value || |
163 | std::is_same<SPSTagT, int64_t>::value || |
164 | std::is_same<SPSTagT, uint8_t>::value || |
165 | std::is_same<SPSTagT, uint16_t>::value || |
166 | std::is_same<SPSTagT, uint32_t>::value || |
167 | std::is_same<SPSTagT, uint64_t>::value>> { |
168 | public: |
169 | static size_t size(const SPSTagT &Value) { return sizeof(SPSTagT); } |
170 | |
171 | static bool serialize(SPSOutputBuffer &OB, const SPSTagT &Value) { |
172 | SPSTagT Tmp = Value; |
173 | if (sys::IsBigEndianHost) |
174 | sys::swapByteOrder(Tmp); |
175 | return OB.write(Data: reinterpret_cast<const char *>(&Tmp), Size: sizeof(Tmp)); |
176 | } |
177 | |
178 | static bool deserialize(SPSInputBuffer &IB, SPSTagT &Value) { |
179 | SPSTagT Tmp; |
180 | if (!IB.read(Data: reinterpret_cast<char *>(&Tmp), Size: sizeof(Tmp))) |
181 | return false; |
182 | if (sys::IsBigEndianHost) |
183 | sys::swapByteOrder(Tmp); |
184 | Value = Tmp; |
185 | return true; |
186 | } |
187 | }; |
188 | |
189 | // Any empty placeholder suitable as a substitute for void when deserializing |
190 | class SPSEmpty {}; |
191 | |
192 | /// SPS tag type for tuples. |
193 | /// |
194 | /// A blob tuple should be serialized by serializing each of the elements in |
195 | /// sequence. |
196 | template <typename... SPSTagTs> class SPSTuple { |
197 | public: |
198 | /// Convenience typedef of the corresponding arg list. |
199 | typedef SPSArgList<SPSTagTs...> AsArgList; |
200 | }; |
201 | |
202 | /// SPS tag type for optionals. |
203 | /// |
204 | /// SPSOptionals should be serialized as a bool with true indicating that an |
205 | /// SPSTagT value is present, and false indicating that there is no value. |
206 | /// If the boolean is true then the serialized SPSTagT will follow immediately |
207 | /// after it. |
208 | template <typename SPSTagT> class SPSOptional {}; |
209 | |
210 | /// SPS tag type for sequences. |
211 | /// |
212 | /// SPSSequences should be serialized as a uint64_t sequence length, |
213 | /// followed by the serialization of each of the elements. |
214 | template <typename SPSElementTagT> class SPSSequence; |
215 | |
216 | /// SPS tag type for strings, which are equivalent to sequences of chars. |
217 | using SPSString = SPSSequence<char>; |
218 | |
219 | /// SPS tag type for maps. |
220 | /// |
221 | /// SPS maps are just sequences of (Key, Value) tuples. |
222 | template <typename SPSTagT1, typename SPSTagT2> |
223 | using SPSMap = SPSSequence<SPSTuple<SPSTagT1, SPSTagT2>>; |
224 | |
225 | /// Serialization for SPSEmpty type. |
226 | template <> class SPSSerializationTraits<SPSEmpty, SPSEmpty> { |
227 | public: |
228 | static size_t size(const SPSEmpty &EP) { return 0; } |
229 | static bool serialize(SPSOutputBuffer &OB, const SPSEmpty &BE) { |
230 | return true; |
231 | } |
232 | static bool deserialize(SPSInputBuffer &IB, SPSEmpty &BE) { return true; } |
233 | }; |
234 | |
235 | /// Specialize this to implement 'trivial' sequence serialization for |
236 | /// a concrete sequence type. |
237 | /// |
238 | /// Trivial sequence serialization uses the sequence's 'size' member to get the |
239 | /// length of the sequence, and uses a range-based for loop to iterate over the |
240 | /// 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 TrivialSPSSequenceSerialization { |
246 | public: |
247 | static constexpr bool available = false; |
248 | }; |
249 | |
250 | /// Specialize this to implement 'trivial' sequence deserialization for |
251 | /// a concrete sequence type. |
252 | /// |
253 | /// Trivial deserialization calls a static 'reserve(SequenceT&)' method on your |
254 | /// specialization (you must implement this) to reserve space, and then calls |
255 | /// a static 'append(SequenceT&, ElementT&) method to append each of the |
256 | /// deserialized elements. |
257 | /// |
258 | /// Specializing this template class means that you do not need to provide a |
259 | /// specialization of SPSSerializationTraits for your type. |
260 | template <typename SPSElementTagT, typename ConcreteSequenceT> |
261 | class TrivialSPSSequenceDeserialization { |
262 | public: |
263 | static constexpr bool available = false; |
264 | }; |
265 | |
266 | /// Trivial std::string -> SPSSequence<char> serialization. |
267 | template <> class TrivialSPSSequenceSerialization<char, std::string> { |
268 | public: |
269 | static constexpr bool available = true; |
270 | }; |
271 | |
272 | /// Trivial SPSSequence<char> -> std::string deserialization. |
273 | template <> class TrivialSPSSequenceDeserialization<char, std::string> { |
274 | public: |
275 | static constexpr bool available = true; |
276 | |
277 | using element_type = char; |
278 | |
279 | static void reserve(std::string &S, uint64_t Size) { S.reserve(res_arg: Size); } |
280 | static bool append(std::string &S, char C) { |
281 | S.push_back(c: C); |
282 | return true; |
283 | } |
284 | }; |
285 | |
286 | /// Trivial std::vector<T> -> SPSSequence<SPSElementTagT> serialization. |
287 | template <typename SPSElementTagT, typename T> |
288 | class TrivialSPSSequenceSerialization<SPSElementTagT, std::vector<T>> { |
289 | public: |
290 | static constexpr bool available = true; |
291 | }; |
292 | |
293 | /// Trivial SPSSequence<SPSElementTagT> -> std::vector<T> deserialization. |
294 | template <typename SPSElementTagT, typename T> |
295 | class TrivialSPSSequenceDeserialization<SPSElementTagT, std::vector<T>> { |
296 | public: |
297 | static constexpr bool available = true; |
298 | |
299 | using element_type = typename std::vector<T>::value_type; |
300 | |
301 | static void reserve(std::vector<T> &V, uint64_t Size) { V.reserve(Size); } |
302 | static bool append(std::vector<T> &V, T E) { |
303 | V.push_back(std::move(E)); |
304 | return true; |
305 | } |
306 | }; |
307 | |
308 | /// Trivial SmallVectorImpl<T> -> SPSSequence<char> serialization. |
309 | template <typename SPSElementTagT, typename T> |
310 | class TrivialSPSSequenceSerialization<SPSElementTagT, SmallVectorImpl<T>> { |
311 | public: |
312 | static constexpr bool available = true; |
313 | }; |
314 | |
315 | /// Trivial SPSSequence<SPSElementTagT> -> SmallVectorImpl<T> deserialization. |
316 | template <typename SPSElementTagT, typename T> |
317 | class TrivialSPSSequenceDeserialization<SPSElementTagT, SmallVectorImpl<T>> { |
318 | public: |
319 | static constexpr bool available = true; |
320 | |
321 | using element_type = typename SmallVectorImpl<T>::value_type; |
322 | |
323 | static void reserve(SmallVectorImpl<T> &V, uint64_t Size) { V.reserve(Size); } |
324 | static bool append(SmallVectorImpl<T> &V, T E) { |
325 | V.push_back(std::move(E)); |
326 | return true; |
327 | } |
328 | }; |
329 | |
330 | /// Trivial SmallVectorImpl<T> -> SPSSequence<char> serialization. |
331 | template <typename SPSElementTagT, typename T, unsigned N> |
332 | class TrivialSPSSequenceSerialization<SPSElementTagT, SmallVector<T, N>> |
333 | : public TrivialSPSSequenceSerialization<SPSElementTagT, |
334 | SmallVectorImpl<T>> {}; |
335 | |
336 | /// Trivial SPSSequence<SPSElementTagT> -> SmallVectorImpl<T> deserialization. |
337 | template <typename SPSElementTagT, typename T, unsigned N> |
338 | class TrivialSPSSequenceDeserialization<SPSElementTagT, SmallVector<T, N>> |
339 | : public TrivialSPSSequenceDeserialization<SPSElementTagT, |
340 | SmallVectorImpl<T>> {}; |
341 | |
342 | /// Trivial ArrayRef<T> -> SPSSequence<SPSElementTagT> serialization. |
343 | template <typename SPSElementTagT, typename T> |
344 | class TrivialSPSSequenceSerialization<SPSElementTagT, ArrayRef<T>> { |
345 | public: |
346 | static constexpr bool available = true; |
347 | }; |
348 | |
349 | /// Specialized SPSSequence<char> -> ArrayRef<char> serialization. |
350 | /// |
351 | /// On deserialize, points directly into the input buffer. |
352 | template <> class SPSSerializationTraits<SPSSequence<char>, ArrayRef<char>> { |
353 | public: |
354 | static size_t size(const ArrayRef<char> &A) { |
355 | return SPSArgList<uint64_t>::size(Arg: static_cast<uint64_t>(A.size())) + |
356 | A.size(); |
357 | } |
358 | |
359 | static bool serialize(SPSOutputBuffer &OB, const ArrayRef<char> &A) { |
360 | if (!SPSArgList<uint64_t>::serialize(OB, Arg: static_cast<uint64_t>(A.size()))) |
361 | return false; |
362 | if (A.empty()) // Empty ArrayRef may have null data, so bail out early. |
363 | return true; |
364 | return OB.write(Data: A.data(), Size: A.size()); |
365 | } |
366 | |
367 | static bool deserialize(SPSInputBuffer &IB, ArrayRef<char> &A) { |
368 | uint64_t Size; |
369 | if (!SPSArgList<uint64_t>::deserialize(IB, Arg&: Size)) |
370 | return false; |
371 | if (Size > std::numeric_limits<size_t>::max()) |
372 | return false; |
373 | A = {Size ? IB.data() : nullptr, static_cast<size_t>(Size)}; |
374 | return IB.skip(Size); |
375 | } |
376 | }; |
377 | |
378 | /// 'Trivial' sequence serialization: Sequence is serialized as a uint64_t size |
379 | /// followed by a for-earch loop over the elements of the sequence to serialize |
380 | /// each of them. |
381 | template <typename SPSElementTagT, typename SequenceT> |
382 | class SPSSerializationTraits<SPSSequence<SPSElementTagT>, SequenceT, |
383 | std::enable_if_t<TrivialSPSSequenceSerialization< |
384 | SPSElementTagT, SequenceT>::available>> { |
385 | public: |
386 | static size_t size(const SequenceT &S) { |
387 | size_t Size = SPSArgList<uint64_t>::size(Arg: static_cast<uint64_t>(S.size())); |
388 | for (const auto &E : S) |
389 | Size += SPSArgList<SPSElementTagT>::size(E); |
390 | return Size; |
391 | } |
392 | |
393 | static bool serialize(SPSOutputBuffer &OB, const SequenceT &S) { |
394 | if (!SPSArgList<uint64_t>::serialize(OB, Arg: static_cast<uint64_t>(S.size()))) |
395 | return false; |
396 | for (const auto &E : S) |
397 | if (!SPSArgList<SPSElementTagT>::serialize(OB, E)) |
398 | return false; |
399 | return true; |
400 | } |
401 | |
402 | static bool deserialize(SPSInputBuffer &IB, SequenceT &S) { |
403 | using TBSD = TrivialSPSSequenceDeserialization<SPSElementTagT, SequenceT>; |
404 | uint64_t Size; |
405 | if (!SPSArgList<uint64_t>::deserialize(IB, Arg&: Size)) |
406 | return false; |
407 | TBSD::reserve(S, Size); |
408 | for (size_t I = 0; I != Size; ++I) { |
409 | typename TBSD::element_type E; |
410 | if (!SPSArgList<SPSElementTagT>::deserialize(IB, E)) |
411 | return false; |
412 | if (!TBSD::append(S, std::move(E))) |
413 | return false; |
414 | } |
415 | return true; |
416 | } |
417 | }; |
418 | |
419 | /// SPSTuple serialization for std::tuple. |
420 | template <typename... SPSTagTs, typename... Ts> |
421 | class SPSSerializationTraits<SPSTuple<SPSTagTs...>, std::tuple<Ts...>> { |
422 | private: |
423 | using TupleArgList = typename SPSTuple<SPSTagTs...>::AsArgList; |
424 | using ArgIndices = std::make_index_sequence<sizeof...(Ts)>; |
425 | |
426 | template <std::size_t... I> |
427 | static size_t size(const std::tuple<Ts...> &T, std::index_sequence<I...>) { |
428 | return TupleArgList::size(std::get<I>(T)...); |
429 | } |
430 | |
431 | template <std::size_t... I> |
432 | static bool serialize(SPSOutputBuffer &OB, const std::tuple<Ts...> &T, |
433 | std::index_sequence<I...>) { |
434 | return TupleArgList::serialize(OB, std::get<I>(T)...); |
435 | } |
436 | |
437 | template <std::size_t... I> |
438 | static bool deserialize(SPSInputBuffer &IB, std::tuple<Ts...> &T, |
439 | std::index_sequence<I...>) { |
440 | return TupleArgList::deserialize(IB, std::get<I>(T)...); |
441 | } |
442 | |
443 | public: |
444 | static size_t size(const std::tuple<Ts...> &T) { |
445 | return size(T, ArgIndices{}); |
446 | } |
447 | |
448 | static bool serialize(SPSOutputBuffer &OB, const std::tuple<Ts...> &T) { |
449 | return serialize(OB, T, ArgIndices{}); |
450 | } |
451 | |
452 | static bool deserialize(SPSInputBuffer &IB, std::tuple<Ts...> &T) { |
453 | return deserialize(IB, T, ArgIndices{}); |
454 | } |
455 | }; |
456 | |
457 | /// SPSTuple serialization for std::pair. |
458 | template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2> |
459 | class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> { |
460 | public: |
461 | static size_t size(const std::pair<T1, T2> &P) { |
462 | return SPSArgList<SPSTagT1>::size(P.first) + |
463 | SPSArgList<SPSTagT2>::size(P.second); |
464 | } |
465 | |
466 | static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) { |
467 | return SPSArgList<SPSTagT1>::serialize(OB, P.first) && |
468 | SPSArgList<SPSTagT2>::serialize(OB, P.second); |
469 | } |
470 | |
471 | static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) { |
472 | return SPSArgList<SPSTagT1>::deserialize(IB, P.first) && |
473 | SPSArgList<SPSTagT2>::deserialize(IB, P.second); |
474 | } |
475 | }; |
476 | |
477 | /// SPSOptional serialization for std::optional. |
478 | template <typename SPSTagT, typename T> |
479 | class SPSSerializationTraits<SPSOptional<SPSTagT>, std::optional<T>> { |
480 | public: |
481 | static size_t size(const std::optional<T> &Value) { |
482 | size_t Size = SPSArgList<bool>::size(!!Value); |
483 | if (Value) |
484 | Size += SPSArgList<SPSTagT>::size(*Value); |
485 | return Size; |
486 | } |
487 | |
488 | static bool serialize(SPSOutputBuffer &OB, const std::optional<T> &Value) { |
489 | if (!SPSArgList<bool>::serialize(OB, !!Value)) |
490 | return false; |
491 | if (Value) |
492 | return SPSArgList<SPSTagT>::serialize(OB, *Value); |
493 | return true; |
494 | } |
495 | |
496 | static bool deserialize(SPSInputBuffer &IB, std::optional<T> &Value) { |
497 | bool HasValue; |
498 | if (!SPSArgList<bool>::deserialize(IB, Arg&: HasValue)) |
499 | return false; |
500 | if (HasValue) { |
501 | Value = T(); |
502 | return SPSArgList<SPSTagT>::deserialize(IB, *Value); |
503 | } else |
504 | Value = std::optional<T>(); |
505 | return true; |
506 | } |
507 | }; |
508 | |
509 | /// Serialization for StringRefs. |
510 | /// |
511 | /// Serialization is as for regular strings. Deserialization points directly |
512 | /// into the blob. |
513 | template <> class SPSSerializationTraits<SPSString, StringRef> { |
514 | public: |
515 | static size_t size(const StringRef &S) { |
516 | return SPSArgList<uint64_t>::size(Arg: static_cast<uint64_t>(S.size())) + |
517 | S.size(); |
518 | } |
519 | |
520 | static bool serialize(SPSOutputBuffer &OB, StringRef S) { |
521 | if (!SPSArgList<uint64_t>::serialize(OB, Arg: static_cast<uint64_t>(S.size()))) |
522 | return false; |
523 | if (S.empty()) // Empty StringRef may have null data, so bail out early. |
524 | return true; |
525 | return OB.write(Data: S.data(), Size: S.size()); |
526 | } |
527 | |
528 | static bool deserialize(SPSInputBuffer &IB, StringRef &S) { |
529 | const char *Data = nullptr; |
530 | uint64_t Size; |
531 | if (!SPSArgList<uint64_t>::deserialize(IB, Arg&: Size)) |
532 | return false; |
533 | Data = IB.data(); |
534 | if (!IB.skip(Size)) |
535 | return false; |
536 | S = StringRef(Size ? Data : nullptr, Size); |
537 | return true; |
538 | } |
539 | }; |
540 | |
541 | /// Serialization for StringMap<ValueT>s. |
542 | template <typename SPSValueT, typename ValueT> |
543 | class SPSSerializationTraits<SPSSequence<SPSTuple<SPSString, SPSValueT>>, |
544 | StringMap<ValueT>> { |
545 | public: |
546 | static size_t size(const StringMap<ValueT> &M) { |
547 | size_t Sz = SPSArgList<uint64_t>::size(Arg: static_cast<uint64_t>(M.size())); |
548 | for (auto &E : M) |
549 | Sz += SPSArgList<SPSString, SPSValueT>::size(E.first(), E.second); |
550 | return Sz; |
551 | } |
552 | |
553 | static bool serialize(SPSOutputBuffer &OB, const StringMap<ValueT> &M) { |
554 | if (!SPSArgList<uint64_t>::serialize(OB, Arg: static_cast<uint64_t>(M.size()))) |
555 | return false; |
556 | |
557 | for (auto &E : M) |
558 | if (!SPSArgList<SPSString, SPSValueT>::serialize(OB, E.first(), E.second)) |
559 | return false; |
560 | |
561 | return true; |
562 | } |
563 | |
564 | static bool deserialize(SPSInputBuffer &IB, StringMap<ValueT> &M) { |
565 | uint64_t Size; |
566 | assert(M.empty() && "M already contains elements" ); |
567 | |
568 | if (!SPSArgList<uint64_t>::deserialize(IB, Arg&: Size)) |
569 | return false; |
570 | |
571 | while (Size--) { |
572 | StringRef S; |
573 | ValueT V; |
574 | if (!SPSArgList<SPSString, SPSValueT>::deserialize(IB, S, V)) |
575 | return false; |
576 | if (!M.insert(std::make_pair(S, V)).second) |
577 | return false; |
578 | } |
579 | |
580 | return true; |
581 | } |
582 | }; |
583 | |
584 | /// SPS tag type for errors. |
585 | class SPSError; |
586 | |
587 | /// SPS tag type for expecteds, which are either a T or a string representing |
588 | /// an error. |
589 | template <typename SPSTagT> class SPSExpected; |
590 | |
591 | namespace detail { |
592 | |
593 | /// Helper type for serializing Errors. |
594 | /// |
595 | /// llvm::Errors are move-only, and not inspectable except by consuming them. |
596 | /// This makes them unsuitable for direct serialization via |
597 | /// SPSSerializationTraits, which needs to inspect values twice (once to |
598 | /// determine the amount of space to reserve, and then again to serialize). |
599 | /// |
600 | /// The SPSSerializableError type is a helper that can be |
601 | /// constructed from an llvm::Error, but inspected more than once. |
602 | struct SPSSerializableError { |
603 | bool HasError = false; |
604 | std::string ErrMsg; |
605 | }; |
606 | |
607 | /// Helper type for serializing Expected<T>s. |
608 | /// |
609 | /// See SPSSerializableError for more details. |
610 | /// |
611 | // FIXME: Use std::variant for storage once we have c++17. |
612 | template <typename T> struct SPSSerializableExpected { |
613 | bool HasValue = false; |
614 | T Value{}; |
615 | std::string ErrMsg; |
616 | }; |
617 | |
618 | inline SPSSerializableError toSPSSerializable(Error Err) { |
619 | if (Err) |
620 | return {.HasError: true, .ErrMsg: toString(E: std::move(Err))}; |
621 | return {.HasError: false, .ErrMsg: {}}; |
622 | } |
623 | |
624 | inline Error fromSPSSerializable(SPSSerializableError BSE) { |
625 | if (BSE.HasError) |
626 | return make_error<StringError>(Args&: BSE.ErrMsg, Args: inconvertibleErrorCode()); |
627 | return Error::success(); |
628 | } |
629 | |
630 | template <typename T> |
631 | SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) { |
632 | if (E) |
633 | return {true, std::move(*E), {}}; |
634 | else |
635 | return {false, T(), toString(E.takeError())}; |
636 | } |
637 | |
638 | template <typename T> |
639 | Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) { |
640 | if (BSE.HasValue) |
641 | return std::move(BSE.Value); |
642 | else |
643 | return make_error<StringError>(BSE.ErrMsg, inconvertibleErrorCode()); |
644 | } |
645 | |
646 | } // end namespace detail |
647 | |
648 | /// Serialize to a SPSError from a detail::SPSSerializableError. |
649 | template <> |
650 | class SPSSerializationTraits<SPSError, detail::SPSSerializableError> { |
651 | public: |
652 | static size_t size(const detail::SPSSerializableError &BSE) { |
653 | size_t Size = SPSArgList<bool>::size(Arg: BSE.HasError); |
654 | if (BSE.HasError) |
655 | Size += SPSArgList<SPSString>::size(Arg: BSE.ErrMsg); |
656 | return Size; |
657 | } |
658 | |
659 | static bool serialize(SPSOutputBuffer &OB, |
660 | const detail::SPSSerializableError &BSE) { |
661 | if (!SPSArgList<bool>::serialize(OB, Arg: BSE.HasError)) |
662 | return false; |
663 | if (BSE.HasError) |
664 | if (!SPSArgList<SPSString>::serialize(OB, Arg: BSE.ErrMsg)) |
665 | return false; |
666 | return true; |
667 | } |
668 | |
669 | static bool deserialize(SPSInputBuffer &IB, |
670 | detail::SPSSerializableError &BSE) { |
671 | if (!SPSArgList<bool>::deserialize(IB, Arg&: BSE.HasError)) |
672 | return false; |
673 | |
674 | if (!BSE.HasError) |
675 | return true; |
676 | |
677 | return SPSArgList<SPSString>::deserialize(IB, Arg&: BSE.ErrMsg); |
678 | } |
679 | }; |
680 | |
681 | /// Serialize to a SPSExpected<SPSTagT> from a |
682 | /// detail::SPSSerializableExpected<T>. |
683 | template <typename SPSTagT, typename T> |
684 | class SPSSerializationTraits<SPSExpected<SPSTagT>, |
685 | detail::SPSSerializableExpected<T>> { |
686 | public: |
687 | static size_t size(const detail::SPSSerializableExpected<T> &BSE) { |
688 | size_t Size = SPSArgList<bool>::size(BSE.HasValue); |
689 | if (BSE.HasValue) |
690 | Size += SPSArgList<SPSTagT>::size(BSE.Value); |
691 | else |
692 | Size += SPSArgList<SPSString>::size(BSE.ErrMsg); |
693 | return Size; |
694 | } |
695 | |
696 | static bool serialize(SPSOutputBuffer &OB, |
697 | const detail::SPSSerializableExpected<T> &BSE) { |
698 | if (!SPSArgList<bool>::serialize(OB, BSE.HasValue)) |
699 | return false; |
700 | |
701 | if (BSE.HasValue) |
702 | return SPSArgList<SPSTagT>::serialize(OB, BSE.Value); |
703 | |
704 | return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg); |
705 | } |
706 | |
707 | static bool deserialize(SPSInputBuffer &IB, |
708 | detail::SPSSerializableExpected<T> &BSE) { |
709 | if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue)) |
710 | return false; |
711 | |
712 | if (BSE.HasValue) |
713 | return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value); |
714 | |
715 | return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg); |
716 | } |
717 | }; |
718 | |
719 | /// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError. |
720 | template <typename SPSTagT> |
721 | class SPSSerializationTraits<SPSExpected<SPSTagT>, |
722 | detail::SPSSerializableError> { |
723 | public: |
724 | static size_t size(const detail::SPSSerializableError &BSE) { |
725 | assert(BSE.HasError && "Cannot serialize expected from a success value" ); |
726 | return SPSArgList<bool>::size(Arg: false) + |
727 | SPSArgList<SPSString>::size(Arg: BSE.ErrMsg); |
728 | } |
729 | |
730 | static bool serialize(SPSOutputBuffer &OB, |
731 | const detail::SPSSerializableError &BSE) { |
732 | assert(BSE.HasError && "Cannot serialize expected from a success value" ); |
733 | if (!SPSArgList<bool>::serialize(OB, Arg: false)) |
734 | return false; |
735 | return SPSArgList<SPSString>::serialize(OB, Arg: BSE.ErrMsg); |
736 | } |
737 | }; |
738 | |
739 | /// Serialize to a SPSExpected<SPSTagT> from a T. |
740 | template <typename SPSTagT, typename T> |
741 | class SPSSerializationTraits<SPSExpected<SPSTagT>, T> { |
742 | public: |
743 | static size_t size(const T &Value) { |
744 | return SPSArgList<bool>::size(Arg: true) + SPSArgList<SPSTagT>::size(Value); |
745 | } |
746 | |
747 | static bool serialize(SPSOutputBuffer &OB, const T &Value) { |
748 | if (!SPSArgList<bool>::serialize(OB, Arg: true)) |
749 | return false; |
750 | return SPSArgList<SPSTagT>::serialize(Value); |
751 | } |
752 | }; |
753 | |
754 | } // end namespace shared |
755 | } // end namespace orc |
756 | } // end namespace llvm |
757 | |
758 | #endif // LLVM_EXECUTIONENGINE_ORC_SHARED_SIMPLEPACKEDSERIALIZATION_H |
759 | |