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 SPSExecutorAddress {}; 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 /// SPSTuple serialization for std::pair. 358 template <typename SPSTagT1, typename SPSTagT2, typename T1, typename T2> 359 class SPSSerializationTraits<SPSTuple<SPSTagT1, SPSTagT2>, std::pair<T1, T2>> { 360 public: 361 static size_t size(const std::pair<T1, T2> &P) { 362 return SPSArgList<SPSTagT1>::size(P.first) + 363 SPSArgList<SPSTagT2>::size(P.second); 364 } 365 366 static bool serialize(SPSOutputBuffer &OB, const std::pair<T1, T2> &P) { 367 return SPSArgList<SPSTagT1>::serialize(OB, P.first) && 368 SPSArgList<SPSTagT2>::serialize(OB, P.second); 369 } 370 371 static bool deserialize(SPSInputBuffer &IB, std::pair<T1, T2> &P) { 372 return SPSArgList<SPSTagT1>::deserialize(IB, P.first) && 373 SPSArgList<SPSTagT2>::deserialize(IB, P.second); 374 } 375 }; 376 377 /// Serialization for string_views. 378 /// 379 /// Serialization is as for regular strings. Deserialization points directly 380 /// into the blob. 381 template <> class SPSSerializationTraits<SPSString, __orc_rt::string_view> { 382 public: 383 static size_t size(const __orc_rt::string_view &S) { 384 return SPSArgList<uint64_t>::size(static_cast<uint64_t>(S.size())) + 385 S.size(); 386 } 387 388 static bool serialize(SPSOutputBuffer &OB, const __orc_rt::string_view &S) { 389 if (!SPSArgList<uint64_t>::serialize(OB, static_cast<uint64_t>(S.size()))) 390 return false; 391 return OB.write(S.data(), S.size()); 392 } 393 394 static bool deserialize(SPSInputBuffer &IB, __orc_rt::string_view &S) { 395 const char *Data = nullptr; 396 uint64_t Size; 397 if (!SPSArgList<uint64_t>::deserialize(IB, Size)) 398 return false; 399 Data = IB.data(); 400 if (!IB.skip(Size)) 401 return false; 402 S = {Data, Size}; 403 return true; 404 } 405 }; 406 407 /// SPS tag type for errors. 408 class SPSError; 409 410 /// SPS tag type for expecteds, which are either a T or a string representing 411 /// an error. 412 template <typename SPSTagT> class SPSExpected; 413 414 namespace detail { 415 416 /// Helper type for serializing Errors. 417 /// 418 /// llvm::Errors are move-only, and not inspectable except by consuming them. 419 /// This makes them unsuitable for direct serialization via 420 /// SPSSerializationTraits, which needs to inspect values twice (once to 421 /// determine the amount of space to reserve, and then again to serialize). 422 /// 423 /// The SPSSerializableError type is a helper that can be 424 /// constructed from an llvm::Error, but inspected more than once. 425 struct SPSSerializableError { 426 bool HasError = false; 427 std::string ErrMsg; 428 }; 429 430 /// Helper type for serializing Expected<T>s. 431 /// 432 /// See SPSSerializableError for more details. 433 /// 434 // FIXME: Use std::variant for storage once we have c++17. 435 template <typename T> struct SPSSerializableExpected { 436 bool HasValue = false; 437 T Value{}; 438 std::string ErrMsg; 439 }; 440 441 inline SPSSerializableError toSPSSerializable(Error Err) { 442 if (Err) 443 return {true, toString(std::move(Err))}; 444 return {false, {}}; 445 } 446 447 inline Error fromSPSSerializable(SPSSerializableError BSE) { 448 if (BSE.HasError) 449 return make_error<StringError>(BSE.ErrMsg); 450 return Error::success(); 451 } 452 453 template <typename T> 454 SPSSerializableExpected<T> toSPSSerializable(Expected<T> E) { 455 if (E) 456 return {true, std::move(*E), {}}; 457 else 458 return {false, {}, toString(E.takeError())}; 459 } 460 461 template <typename T> 462 Expected<T> fromSPSSerializable(SPSSerializableExpected<T> BSE) { 463 if (BSE.HasValue) 464 return std::move(BSE.Value); 465 else 466 return make_error<StringError>(BSE.ErrMsg); 467 } 468 469 } // end namespace detail 470 471 /// Serialize to a SPSError from a detail::SPSSerializableError. 472 template <> 473 class SPSSerializationTraits<SPSError, detail::SPSSerializableError> { 474 public: 475 static size_t size(const detail::SPSSerializableError &BSE) { 476 size_t Size = SPSArgList<bool>::size(BSE.HasError); 477 if (BSE.HasError) 478 Size += SPSArgList<SPSString>::size(BSE.ErrMsg); 479 return Size; 480 } 481 482 static bool serialize(SPSOutputBuffer &OB, 483 const detail::SPSSerializableError &BSE) { 484 if (!SPSArgList<bool>::serialize(OB, BSE.HasError)) 485 return false; 486 if (BSE.HasError) 487 if (!SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg)) 488 return false; 489 return true; 490 } 491 492 static bool deserialize(SPSInputBuffer &IB, 493 detail::SPSSerializableError &BSE) { 494 if (!SPSArgList<bool>::deserialize(IB, BSE.HasError)) 495 return false; 496 497 if (!BSE.HasError) 498 return true; 499 500 return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg); 501 } 502 }; 503 504 /// Serialize to a SPSExpected<SPSTagT> from a 505 /// detail::SPSSerializableExpected<T>. 506 template <typename SPSTagT, typename T> 507 class SPSSerializationTraits<SPSExpected<SPSTagT>, 508 detail::SPSSerializableExpected<T>> { 509 public: 510 static size_t size(const detail::SPSSerializableExpected<T> &BSE) { 511 size_t Size = SPSArgList<bool>::size(BSE.HasValue); 512 if (BSE.HasValue) 513 Size += SPSArgList<SPSTagT>::size(BSE.Value); 514 else 515 Size += SPSArgList<SPSString>::size(BSE.ErrMsg); 516 return Size; 517 } 518 519 static bool serialize(SPSOutputBuffer &OB, 520 const detail::SPSSerializableExpected<T> &BSE) { 521 if (!SPSArgList<bool>::serialize(OB, BSE.HasValue)) 522 return false; 523 524 if (BSE.HasValue) 525 return SPSArgList<SPSTagT>::serialize(OB, BSE.Value); 526 527 return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg); 528 } 529 530 static bool deserialize(SPSInputBuffer &IB, 531 detail::SPSSerializableExpected<T> &BSE) { 532 if (!SPSArgList<bool>::deserialize(IB, BSE.HasValue)) 533 return false; 534 535 if (BSE.HasValue) 536 return SPSArgList<SPSTagT>::deserialize(IB, BSE.Value); 537 538 return SPSArgList<SPSString>::deserialize(IB, BSE.ErrMsg); 539 } 540 }; 541 542 /// Serialize to a SPSExpected<SPSTagT> from a detail::SPSSerializableError. 543 template <typename SPSTagT> 544 class SPSSerializationTraits<SPSExpected<SPSTagT>, 545 detail::SPSSerializableError> { 546 public: 547 static size_t size(const detail::SPSSerializableError &BSE) { 548 assert(BSE.HasError && "Cannot serialize expected from a success value"); 549 return SPSArgList<bool>::size(false) + 550 SPSArgList<SPSString>::size(BSE.ErrMsg); 551 } 552 553 static bool serialize(SPSOutputBuffer &OB, 554 const detail::SPSSerializableError &BSE) { 555 assert(BSE.HasError && "Cannot serialize expected from a success value"); 556 if (!SPSArgList<bool>::serialize(OB, false)) 557 return false; 558 return SPSArgList<SPSString>::serialize(OB, BSE.ErrMsg); 559 } 560 }; 561 562 /// Serialize to a SPSExpected<SPSTagT> from a T. 563 template <typename SPSTagT, typename T> 564 class SPSSerializationTraits<SPSExpected<SPSTagT>, T> { 565 public: 566 static size_t size(const T &Value) { 567 return SPSArgList<bool>::size(true) + SPSArgList<SPSTagT>::size(Value); 568 } 569 570 static bool serialize(SPSOutputBuffer &OB, const T &Value) { 571 if (!SPSArgList<bool>::serialize(OB, true)) 572 return false; 573 return SPSArgList<SPSTagT>::serialize(Value); 574 } 575 }; 576 577 } // end namespace __orc_rt 578 579 #endif // ORC_RT_SIMPLE_PACKED_SERIALIZATION_H 580