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