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