ProfileBufferEntrySerialization.h (44225B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef ProfileBufferEntrySerialization_h 8 #define ProfileBufferEntrySerialization_h 9 10 #include "mozilla/Assertions.h" 11 #include "mozilla/leb128iterator.h" 12 #include "mozilla/Likely.h" 13 #include "mozilla/Maybe.h" 14 #include "mozilla/ProfileBufferIndex.h" 15 #include "mozilla/Span.h" 16 #include "mozilla/UniquePtrExtensions.h" 17 #include "mozilla/Variant.h" 18 19 #include <string> 20 #include <tuple> 21 22 namespace mozilla { 23 24 class ProfileBufferEntryWriter; 25 26 // Iterator-like class used to read from an entry. 27 // An entry may be split in two memory segments (e.g., the ends of a ring 28 // buffer, or two chunks of a chunked buffer); it doesn't deal with this 29 // underlying buffer, but only with one or two spans pointing at the space 30 // where the entry lives. 31 class ProfileBufferEntryReader { 32 public: 33 using Byte = uint8_t; 34 using Length = uint32_t; 35 36 using SpanOfConstBytes = Span<const Byte>; 37 38 // Class to be specialized for types to be read from a profile buffer entry. 39 // See common specializations at the bottom of this header. 40 // The following static functions must be provided: 41 // static void ReadInto(EntryReader aER&, T& aT) 42 // { 43 // /* Call `aER.ReadX(...)` function to deserialize into aT, be sure to 44 // read exactly `Bytes(aT)`! */ 45 // } 46 // static T Read(EntryReader& aER) { 47 // /* Call `aER.ReadX(...)` function to deserialize and return a `T`, be 48 // sure to read exactly `Bytes(returned value)`! */ 49 // } 50 template <typename T> 51 struct Deserializer; 52 53 ProfileBufferEntryReader() = default; 54 55 // Reader over one Span. 56 ProfileBufferEntryReader(SpanOfConstBytes aSpan, 57 ProfileBufferBlockIndex aCurrentBlockIndex, 58 ProfileBufferBlockIndex aNextBlockIndex) 59 : mCurrentSpan(aSpan), 60 mNextSpanOrEmpty(aSpan.Last(0)), 61 mCurrentBlockIndex(aCurrentBlockIndex), 62 mNextBlockIndex(aNextBlockIndex) { 63 // 2nd internal Span points at the end of the 1st internal Span, to enforce 64 // invariants. 65 CheckInvariants(); 66 } 67 68 // Reader over two Spans, the second one must not be empty. 69 ProfileBufferEntryReader(SpanOfConstBytes aSpanHead, 70 SpanOfConstBytes aSpanTail, 71 ProfileBufferBlockIndex aCurrentBlockIndex, 72 ProfileBufferBlockIndex aNextBlockIndex) 73 : mCurrentSpan(aSpanHead), 74 mNextSpanOrEmpty(aSpanTail), 75 mCurrentBlockIndex(aCurrentBlockIndex), 76 mNextBlockIndex(aNextBlockIndex) { 77 MOZ_RELEASE_ASSERT(!mNextSpanOrEmpty.IsEmpty()); 78 if (MOZ_UNLIKELY(mCurrentSpan.IsEmpty())) { 79 // First span is already empty, skip it. 80 mCurrentSpan = mNextSpanOrEmpty; 81 mNextSpanOrEmpty = mNextSpanOrEmpty.Last(0); 82 } 83 CheckInvariants(); 84 } 85 86 // Allow copying, which is needed when used as an iterator in some std 87 // functions (e.g., string assignment), and to occasionally backtrack. 88 // Be aware that the main profile buffer APIs give a reference to an entry 89 // reader, and expect that reader to advance to the end of the entry, so don't 90 // just advance copies! 91 ProfileBufferEntryReader(const ProfileBufferEntryReader&) = default; 92 ProfileBufferEntryReader& operator=(const ProfileBufferEntryReader&) = 93 default; 94 95 // Don't =default moving, as it doesn't bring any benefit in this class. 96 97 [[nodiscard]] Length RemainingBytes() const { 98 return mCurrentSpan.LengthBytes() + mNextSpanOrEmpty.LengthBytes(); 99 } 100 101 void SetRemainingBytes(Length aBytes) { 102 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 103 if (aBytes <= mCurrentSpan.LengthBytes()) { 104 mCurrentSpan = mCurrentSpan.First(aBytes); 105 mNextSpanOrEmpty = mCurrentSpan.Last(0); 106 } else { 107 mNextSpanOrEmpty = 108 mNextSpanOrEmpty.First(aBytes - mCurrentSpan.LengthBytes()); 109 } 110 } 111 112 [[nodiscard]] ProfileBufferBlockIndex CurrentBlockIndex() const { 113 return mCurrentBlockIndex; 114 } 115 116 [[nodiscard]] ProfileBufferBlockIndex NextBlockIndex() const { 117 return mNextBlockIndex; 118 } 119 120 // Create a reader of size zero, pointing at aOffset past the current position 121 // of this Reader, so it can be used as end iterator. 122 [[nodiscard]] ProfileBufferEntryReader EmptyIteratorAtOffset( 123 Length aOffset) const { 124 MOZ_RELEASE_ASSERT(aOffset <= RemainingBytes()); 125 if (MOZ_LIKELY(aOffset < mCurrentSpan.LengthBytes())) { 126 // aOffset is before the end of mCurrentSpan. 127 return ProfileBufferEntryReader(mCurrentSpan.Subspan(aOffset, 0), 128 mCurrentBlockIndex, mNextBlockIndex); 129 } 130 // aOffset is right at the end of mCurrentSpan, or inside mNextSpanOrEmpty. 131 return ProfileBufferEntryReader( 132 mNextSpanOrEmpty.Subspan(aOffset - mCurrentSpan.LengthBytes(), 0), 133 mCurrentBlockIndex, mNextBlockIndex); 134 } 135 136 // Be like a limited input iterator, with only `*`, prefix-`++`, `==`, `!=`. 137 // These definitions are expected by std functions, to recognize this as an 138 // iterator. See https://en.cppreference.com/w/cpp/iterator/iterator_traits 139 using difference_type = std::make_signed_t<Length>; 140 using value_type = Byte; 141 using pointer = const Byte*; 142 using reference = const Byte&; 143 using iterator_category = std::input_iterator_tag; 144 145 [[nodiscard]] const Byte& operator*() { 146 // Assume the caller will read from the returned reference (and not just 147 // take the address). 148 MOZ_RELEASE_ASSERT(mCurrentSpan.LengthBytes() >= 1); 149 return *(mCurrentSpan.Elements()); 150 } 151 152 ProfileBufferEntryReader& operator++() { 153 MOZ_RELEASE_ASSERT(mCurrentSpan.LengthBytes() >= 1); 154 if (MOZ_LIKELY(mCurrentSpan.LengthBytes() > 1)) { 155 // More than 1 byte left in mCurrentSpan, just eat it. 156 mCurrentSpan = mCurrentSpan.From(1); 157 } else { 158 // mCurrentSpan will be empty, move mNextSpanOrEmpty to mCurrentSpan. 159 mCurrentSpan = mNextSpanOrEmpty; 160 mNextSpanOrEmpty = mNextSpanOrEmpty.Last(0); 161 } 162 CheckInvariants(); 163 return *this; 164 } 165 166 ProfileBufferEntryReader& operator+=(Length aBytes) { 167 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 168 if (MOZ_LIKELY(aBytes <= mCurrentSpan.LengthBytes())) { 169 // All bytes are in mCurrentSpan. 170 // Update mCurrentSpan past the read bytes. 171 mCurrentSpan = mCurrentSpan.From(aBytes); 172 if (mCurrentSpan.IsEmpty() && !mNextSpanOrEmpty.IsEmpty()) { 173 // Don't leave mCurrentSpan empty, move non-empty mNextSpanOrEmpty into 174 // mCurrentSpan. 175 mCurrentSpan = mNextSpanOrEmpty; 176 mNextSpanOrEmpty = mNextSpanOrEmpty.Last(0); 177 } 178 } else { 179 // mCurrentSpan does not hold enough bytes. 180 // This should only happen at most once: Only for double spans, and when 181 // data crosses the gap. 182 const Length tail = 183 aBytes - static_cast<Length>(mCurrentSpan.LengthBytes()); 184 // Move mNextSpanOrEmpty to mCurrentSpan, past the data. So the next call 185 // will go back to the true case above. 186 mCurrentSpan = mNextSpanOrEmpty.From(tail); 187 mNextSpanOrEmpty = mNextSpanOrEmpty.Last(0); 188 } 189 CheckInvariants(); 190 return *this; 191 } 192 193 [[nodiscard]] bool operator==(const ProfileBufferEntryReader& aOther) const { 194 return mCurrentSpan.Elements() == aOther.mCurrentSpan.Elements(); 195 } 196 [[nodiscard]] bool operator!=(const ProfileBufferEntryReader& aOther) const { 197 return mCurrentSpan.Elements() != aOther.mCurrentSpan.Elements(); 198 } 199 200 // Read an unsigned LEB128 number and move iterator ahead. 201 template <typename T> 202 [[nodiscard]] T ReadULEB128() { 203 return ::mozilla::ReadULEB128<T>(*this); 204 } 205 206 // This struct points at a number of bytes through either one span, or two 207 // separate spans (in the rare cases when it is split between two chunks). 208 // So the possibilities are: 209 // - Totally empty: { [] [] } 210 // - First span is not empty: { [content] [] } (Most common case.) 211 // - Both spans are not empty: { [cont] [ent] } 212 // But something like { [] [content] } is not possible. 213 // 214 // Recommended usage patterns: 215 // - Call a utility function like `CopyBytesTo` if you always need to copy the 216 // data to an outside buffer, e.g., to deserialize an aligned object. 217 // - Access both spans one after the other; Note that the second one may be 218 // empty; and the fist could be empty as well if there is no data at all. 219 // - Check is the second span is empty, in which case you only need to read 220 // the first one; and since its part of a chunk, it may be directly passed 221 // as an unaligned pointer or reference, thereby saving one copy. But 222 // remember to always handle the double-span case as well. 223 // 224 // Reminder: An empty span still has a non-null pointer, so it's safe to use 225 // with functions like memcpy. 226 struct DoubleSpanOfConstBytes { 227 SpanOfConstBytes mFirstOrOnly; 228 SpanOfConstBytes mSecondOrEmpty; 229 230 void CheckInvariants() const { 231 MOZ_ASSERT(mFirstOrOnly.IsEmpty() ? mSecondOrEmpty.IsEmpty() : true, 232 "mSecondOrEmpty should not be the only span to contain data"); 233 } 234 235 DoubleSpanOfConstBytes() : mFirstOrOnly(), mSecondOrEmpty() { 236 CheckInvariants(); 237 } 238 239 DoubleSpanOfConstBytes(const Byte* aOnlyPointer, size_t aOnlyLength) 240 : mFirstOrOnly(aOnlyPointer, aOnlyLength), mSecondOrEmpty() { 241 CheckInvariants(); 242 } 243 244 DoubleSpanOfConstBytes(const Byte* aFirstPointer, size_t aFirstLength, 245 const Byte* aSecondPointer, size_t aSecondLength) 246 : mFirstOrOnly(aFirstPointer, aFirstLength), 247 mSecondOrEmpty(aSecondPointer, aSecondLength) { 248 CheckInvariants(); 249 } 250 251 // Is there no data at all? 252 [[nodiscard]] bool IsEmpty() const { 253 // We only need to check the first span, because if it's empty, the second 254 // one must be empty as well. 255 return mFirstOrOnly.IsEmpty(); 256 } 257 258 // Total length (in bytes) pointed at by both spans. 259 [[nodiscard]] size_t LengthBytes() const { 260 return mFirstOrOnly.LengthBytes() + mSecondOrEmpty.LengthBytes(); 261 } 262 263 // Utility functions to copy all `LengthBytes()` to a given buffer. 264 void CopyBytesTo(void* aDest) const { 265 memcpy(aDest, mFirstOrOnly.Elements(), mFirstOrOnly.LengthBytes()); 266 if (MOZ_UNLIKELY(!mSecondOrEmpty.IsEmpty())) { 267 memcpy(static_cast<Byte*>(aDest) + mFirstOrOnly.LengthBytes(), 268 mSecondOrEmpty.Elements(), mSecondOrEmpty.LengthBytes()); 269 } 270 } 271 272 // If the second span is empty, only the first span may point at data. 273 [[nodiscard]] bool IsSingleSpan() const { return mSecondOrEmpty.IsEmpty(); } 274 }; 275 276 // Get Span(s) to a sequence of bytes, see `DoubleSpanOfConstBytes` for usage. 277 // Note that the reader location is *not* updated, do `+=` on it afterwards. 278 [[nodiscard]] DoubleSpanOfConstBytes PeekSpans(Length aBytes) const { 279 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 280 if (MOZ_LIKELY(aBytes <= mCurrentSpan.LengthBytes())) { 281 // All `aBytes` are in the current chunk, only one span is needed. 282 return DoubleSpanOfConstBytes{mCurrentSpan.Elements(), aBytes}; 283 } 284 // Otherwise the first span covers then end of the current chunk, and the 285 // second span starts in the next chunk. 286 return DoubleSpanOfConstBytes{ 287 mCurrentSpan.Elements(), mCurrentSpan.LengthBytes(), 288 mNextSpanOrEmpty.Elements(), aBytes - mCurrentSpan.LengthBytes()}; 289 } 290 291 // Get Span(s) to a sequence of bytes, see `DoubleSpanOfConstBytes` for usage, 292 // and move the reader forward. 293 [[nodiscard]] DoubleSpanOfConstBytes ReadSpans(Length aBytes) { 294 DoubleSpanOfConstBytes spans = PeekSpans(aBytes); 295 (*this) += aBytes; 296 return spans; 297 } 298 299 // Read a sequence of bytes, like memcpy. 300 void ReadBytes(void* aDest, Length aBytes) { 301 DoubleSpanOfConstBytes spans = ReadSpans(aBytes); 302 MOZ_ASSERT(spans.LengthBytes() == aBytes); 303 spans.CopyBytesTo(aDest); 304 } 305 306 template <typename T> 307 void ReadIntoObject(T& aObject) { 308 Deserializer<T>::ReadInto(*this, aObject); 309 } 310 311 // Read into one or more objects, sequentially. 312 // `EntryReader::ReadIntoObjects()` with nothing is implicitly allowed, this 313 // could be useful for generic programming. 314 template <typename... Ts> 315 void ReadIntoObjects(Ts&... aTs) { 316 (ReadIntoObject(aTs), ...); 317 } 318 319 // Read data as an object and move iterator ahead. 320 template <typename T> 321 [[nodiscard]] T ReadObject() { 322 T ob = Deserializer<T>::Read(*this); 323 return ob; 324 } 325 326 private: 327 friend class ProfileBufferEntryWriter; 328 329 // Invariants: 330 // - mCurrentSpan cannot be empty unless mNextSpanOrEmpty is also empty. So 331 // mCurrentSpan always points at the next byte to read or the end. 332 // - If mNextSpanOrEmpty is empty, it points at the end of mCurrentSpan. So 333 // when reaching the end of mCurrentSpan, we can blindly move 334 // mNextSpanOrEmpty to mCurrentSpan and keep the invariants. 335 SpanOfConstBytes mCurrentSpan; 336 SpanOfConstBytes mNextSpanOrEmpty; 337 ProfileBufferBlockIndex mCurrentBlockIndex; 338 ProfileBufferBlockIndex mNextBlockIndex; 339 340 void CheckInvariants() const { 341 MOZ_ASSERT(!mCurrentSpan.IsEmpty() || mNextSpanOrEmpty.IsEmpty()); 342 MOZ_ASSERT(!mNextSpanOrEmpty.IsEmpty() || 343 (mNextSpanOrEmpty == mCurrentSpan.Last(0))); 344 } 345 }; 346 347 // Iterator-like class used to write into an entry. 348 // An entry may be split in two memory segments (e.g., the ends of a ring 349 // buffer, or two chunks of a chunked buffer); it doesn't deal with this 350 // underlying buffer, but only with one or two spans pointing at the space 351 // reserved for the entry. 352 class ProfileBufferEntryWriter { 353 public: 354 using Byte = uint8_t; 355 using Length = uint32_t; 356 357 using SpanOfBytes = Span<Byte>; 358 359 // Class to be specialized for types to be written in an entry. 360 // See common specializations at the bottom of this header. 361 // The following static functions must be provided: 362 // static Length Bytes(const T& aT) { 363 // /* Return number of bytes that will be written. */ 364 // } 365 // static void Write(ProfileBufferEntryWriter& aEW, 366 // const T& aT) { 367 // /* Call `aEW.WriteX(...)` functions to serialize aT, be sure to write 368 // exactly `Bytes(aT)` bytes! */ 369 // } 370 template <typename T> 371 struct Serializer; 372 373 ProfileBufferEntryWriter() = default; 374 375 ProfileBufferEntryWriter(SpanOfBytes aSpan, 376 ProfileBufferBlockIndex aCurrentBlockIndex, 377 ProfileBufferBlockIndex aNextBlockIndex) 378 : mCurrentSpan(aSpan), 379 mCurrentBlockIndex(aCurrentBlockIndex), 380 mNextBlockIndex(aNextBlockIndex) {} 381 382 ProfileBufferEntryWriter(SpanOfBytes aSpanHead, SpanOfBytes aSpanTail, 383 ProfileBufferBlockIndex aCurrentBlockIndex, 384 ProfileBufferBlockIndex aNextBlockIndex) 385 : mCurrentSpan(aSpanHead), 386 mNextSpanOrEmpty(aSpanTail), 387 mCurrentBlockIndex(aCurrentBlockIndex), 388 mNextBlockIndex(aNextBlockIndex) { 389 // Either: 390 // - mCurrentSpan is not empty, OR 391 // - mNextSpanOrEmpty is empty if mNextSpanOrEmpty is empty as well. 392 MOZ_RELEASE_ASSERT(!mCurrentSpan.IsEmpty() || mNextSpanOrEmpty.IsEmpty()); 393 } 394 395 // Disable copying and moving, so we can't have multiple writing heads. 396 ProfileBufferEntryWriter(const ProfileBufferEntryWriter&) = delete; 397 ProfileBufferEntryWriter& operator=(const ProfileBufferEntryWriter&) = delete; 398 ProfileBufferEntryWriter(ProfileBufferEntryWriter&&) = delete; 399 ProfileBufferEntryWriter& operator=(ProfileBufferEntryWriter&&) = delete; 400 401 void Set() { 402 mCurrentSpan = SpanOfBytes{}; 403 mNextSpanOrEmpty = SpanOfBytes{}; 404 mCurrentBlockIndex = nullptr; 405 mNextBlockIndex = nullptr; 406 } 407 408 void Set(SpanOfBytes aSpan, ProfileBufferBlockIndex aCurrentBlockIndex, 409 ProfileBufferBlockIndex aNextBlockIndex) { 410 mCurrentSpan = aSpan; 411 mNextSpanOrEmpty = SpanOfBytes{}; 412 mCurrentBlockIndex = aCurrentBlockIndex; 413 mNextBlockIndex = aNextBlockIndex; 414 } 415 416 void Set(SpanOfBytes aSpan0, SpanOfBytes aSpan1, 417 ProfileBufferBlockIndex aCurrentBlockIndex, 418 ProfileBufferBlockIndex aNextBlockIndex) { 419 mCurrentSpan = aSpan0; 420 mNextSpanOrEmpty = aSpan1; 421 mCurrentBlockIndex = aCurrentBlockIndex; 422 mNextBlockIndex = aNextBlockIndex; 423 // Either: 424 // - mCurrentSpan is not empty, OR 425 // - mNextSpanOrEmpty is empty if mNextSpanOrEmpty is empty as well. 426 MOZ_RELEASE_ASSERT(!mCurrentSpan.IsEmpty() || mNextSpanOrEmpty.IsEmpty()); 427 } 428 429 [[nodiscard]] Length RemainingBytes() const { 430 return mCurrentSpan.LengthBytes() + mNextSpanOrEmpty.LengthBytes(); 431 } 432 433 [[nodiscard]] ProfileBufferBlockIndex CurrentBlockIndex() const { 434 return mCurrentBlockIndex; 435 } 436 437 [[nodiscard]] ProfileBufferBlockIndex NextBlockIndex() const { 438 return mNextBlockIndex; 439 } 440 441 // Be like a limited output iterator, with only `*` and prefix-`++`. 442 // These definitions are expected by std functions, to recognize this as an 443 // iterator. See https://en.cppreference.com/w/cpp/iterator/iterator_traits 444 using value_type = Byte; 445 using pointer = Byte*; 446 using reference = Byte&; 447 using iterator_category = std::output_iterator_tag; 448 449 [[nodiscard]] Byte& operator*() { 450 MOZ_RELEASE_ASSERT(RemainingBytes() >= 1); 451 return *( 452 (MOZ_LIKELY(!mCurrentSpan.IsEmpty()) ? mCurrentSpan : mNextSpanOrEmpty) 453 .Elements()); 454 } 455 456 ProfileBufferEntryWriter& operator++() { 457 if (MOZ_LIKELY(mCurrentSpan.LengthBytes() >= 1)) { 458 // There is at least 1 byte in mCurrentSpan, eat it. 459 mCurrentSpan = mCurrentSpan.From(1); 460 } else { 461 // mCurrentSpan is empty, move mNextSpanOrEmpty (past the first byte) to 462 // mCurrentSpan. 463 MOZ_RELEASE_ASSERT(mNextSpanOrEmpty.LengthBytes() >= 1); 464 mCurrentSpan = mNextSpanOrEmpty.From(1); 465 mNextSpanOrEmpty = mNextSpanOrEmpty.First(0); 466 } 467 return *this; 468 } 469 470 ProfileBufferEntryWriter& operator+=(Length aBytes) { 471 // Note: This is a rare operation. The code below is a copy of `WriteBytes` 472 // but without the `memcpy`s. 473 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 474 if (MOZ_LIKELY(aBytes <= mCurrentSpan.LengthBytes())) { 475 // Data fits in mCurrentSpan. 476 // Update mCurrentSpan. It may become empty, so in case of a double span, 477 // the next call will go to the false case below. 478 mCurrentSpan = mCurrentSpan.From(aBytes); 479 } else { 480 // Data does not fully fit in mCurrentSpan. 481 // This should only happen at most once: Only for double spans, and when 482 // data crosses the gap or starts there. 483 const Length tail = 484 aBytes - static_cast<Length>(mCurrentSpan.LengthBytes()); 485 // Move mNextSpanOrEmpty to mCurrentSpan, past the data. So the next call 486 // will go back to the true case above. 487 mCurrentSpan = mNextSpanOrEmpty.From(tail); 488 mNextSpanOrEmpty = mNextSpanOrEmpty.First(0); 489 } 490 return *this; 491 } 492 493 // Number of bytes needed to represent `aValue` in unsigned LEB128. 494 template <typename T> 495 [[nodiscard]] static unsigned ULEB128Size(T aValue) { 496 return ::mozilla::ULEB128Size(aValue); 497 } 498 499 // Write number as unsigned LEB128 and move iterator ahead. 500 template <typename T> 501 void WriteULEB128(T aValue) { 502 ::mozilla::WriteULEB128(aValue, *this); 503 } 504 505 // Number of bytes needed to serialize objects. 506 template <typename... Ts> 507 [[nodiscard]] static Length SumBytes(const Ts&... aTs) { 508 return (0 + ... + Serializer<Ts>::Bytes(aTs)); 509 } 510 511 // Write a sequence of bytes, like memcpy. 512 void WriteBytes(const void* aSrc, Length aBytes) { 513 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 514 if (MOZ_LIKELY(aBytes <= mCurrentSpan.LengthBytes())) { 515 // Data fits in mCurrentSpan. 516 memcpy(mCurrentSpan.Elements(), aSrc, aBytes); 517 // Update mCurrentSpan. It may become empty, so in case of a double span, 518 // the next call will go to the false case below. 519 mCurrentSpan = mCurrentSpan.From(aBytes); 520 } else { 521 // Data does not fully fit in mCurrentSpan. 522 // This should only happen at most once: Only for double spans, and when 523 // data crosses the gap or starts there. 524 // Split data between the end of mCurrentSpan and the beginning of 525 // mNextSpanOrEmpty. (mCurrentSpan could be empty, it's ok to do a memcpy 526 // because Span::Elements() is never null.) 527 memcpy(mCurrentSpan.Elements(), aSrc, mCurrentSpan.LengthBytes()); 528 const Length tail = 529 aBytes - static_cast<Length>(mCurrentSpan.LengthBytes()); 530 memcpy(mNextSpanOrEmpty.Elements(), 531 reinterpret_cast<const Byte*>(aSrc) + mCurrentSpan.LengthBytes(), 532 tail); 533 // Move mNextSpanOrEmpty to mCurrentSpan, past the data. So the next call 534 // will go back to the true case above. 535 mCurrentSpan = mNextSpanOrEmpty.From(tail); 536 mNextSpanOrEmpty = mNextSpanOrEmpty.First(0); 537 } 538 } 539 540 void WriteFromReader(ProfileBufferEntryReader& aReader, Length aBytes) { 541 MOZ_RELEASE_ASSERT(aBytes <= RemainingBytes()); 542 MOZ_RELEASE_ASSERT(aBytes <= aReader.RemainingBytes()); 543 Length read0 = std::min( 544 aBytes, static_cast<Length>(aReader.mCurrentSpan.LengthBytes())); 545 if (read0 != 0) { 546 WriteBytes(aReader.mCurrentSpan.Elements(), read0); 547 } 548 Length read1 = aBytes - read0; 549 if (read1 != 0) { 550 WriteBytes(aReader.mNextSpanOrEmpty.Elements(), read1); 551 } 552 aReader += aBytes; 553 } 554 555 // Write a single object by using the appropriate Serializer. 556 template <typename T> 557 void WriteObject(const T& aObject) { 558 Serializer<T>::Write(*this, aObject); 559 } 560 561 // Write one or more objects, sequentially. 562 // Allow `EntryWrite::WriteObjects()` with nothing, this could be useful 563 // for generic programming. 564 template <typename... Ts> 565 void WriteObjects(const Ts&... aTs) { 566 (WriteObject(aTs), ...); 567 } 568 569 private: 570 // The two spans covering the memory still to be written. 571 SpanOfBytes mCurrentSpan; 572 SpanOfBytes mNextSpanOrEmpty; 573 ProfileBufferBlockIndex mCurrentBlockIndex; 574 ProfileBufferBlockIndex mNextBlockIndex; 575 }; 576 577 // ============================================================================ 578 // Serializer and Deserializer ready-to-use specializations. 579 580 // ---------------------------------------------------------------------------- 581 // Trivially-copyable types (default) 582 583 // The default implementation works for all trivially-copyable types (e.g., 584 // PODs). 585 // 586 // Usage: `aEW.WriteObject(123);`. 587 // 588 // Raw pointers, though trivially-copyable, are explicitly forbidden when 589 // writing (to avoid unexpected leaks/UAFs), instead use one of 590 // `WrapProfileBufferLiteralCStringPointer`, `WrapProfileBufferUnownedCString`, 591 // or `WrapProfileBufferRawPointer` as needed. 592 template <typename T> 593 struct ProfileBufferEntryWriter::Serializer { 594 static_assert(std::is_trivially_copyable_v<T>, 595 "Serializer only works with trivially-copyable types by " 596 "default, use/add specialization for other types."); 597 598 static constexpr Length Bytes(const T&) { return sizeof(T); } 599 600 static void Write(ProfileBufferEntryWriter& aEW, const T& aT) { 601 static_assert(!std::is_pointer<T>::value, 602 "Serializer won't write raw pointers by default, use " 603 "WrapProfileBufferRawPointer or other."); 604 aEW.WriteBytes(&aT, sizeof(T)); 605 } 606 }; 607 608 // Usage: `aER.ReadObject<int>();` or `int x; aER.ReadIntoObject(x);`. 609 template <typename T> 610 struct ProfileBufferEntryReader::Deserializer { 611 static_assert(std::is_trivially_copyable_v<T>, 612 "Deserializer only works with trivially-copyable types by " 613 "default, use/add specialization for other types."); 614 615 static void ReadInto(ProfileBufferEntryReader& aER, T& aT) { 616 aER.ReadBytes(&aT, sizeof(T)); 617 } 618 619 static T Read(ProfileBufferEntryReader& aER) { 620 // Note that this creates a default `T` first, and then overwrites it with 621 // bytes from the buffer. Trivially-copyable types support this without UB. 622 T ob; 623 ReadInto(aER, ob); 624 return ob; 625 } 626 }; 627 628 // ---------------------------------------------------------------------------- 629 // Strip const/volatile/reference from types. 630 631 // Automatically strip `const`. 632 template <typename T> 633 struct ProfileBufferEntryWriter::Serializer<const T> 634 : public ProfileBufferEntryWriter::Serializer<T> {}; 635 636 template <typename T> 637 struct ProfileBufferEntryReader::Deserializer<const T> 638 : public ProfileBufferEntryReader::Deserializer<T> {}; 639 640 // Automatically strip `volatile`. 641 template <typename T> 642 struct ProfileBufferEntryWriter::Serializer<volatile T> 643 : public ProfileBufferEntryWriter::Serializer<T> {}; 644 645 template <typename T> 646 struct ProfileBufferEntryReader::Deserializer<volatile T> 647 : public ProfileBufferEntryReader::Deserializer<T> {}; 648 649 // Automatically strip `lvalue-reference`. 650 template <typename T> 651 struct ProfileBufferEntryWriter::Serializer<T&> 652 : public ProfileBufferEntryWriter::Serializer<T> {}; 653 654 template <typename T> 655 struct ProfileBufferEntryReader::Deserializer<T&> 656 : public ProfileBufferEntryReader::Deserializer<T> {}; 657 658 // Automatically strip `rvalue-reference`. 659 template <typename T> 660 struct ProfileBufferEntryWriter::Serializer<T&&> 661 : public ProfileBufferEntryWriter::Serializer<T> {}; 662 663 template <typename T> 664 struct ProfileBufferEntryReader::Deserializer<T&&> 665 : public ProfileBufferEntryReader::Deserializer<T> {}; 666 667 // ---------------------------------------------------------------------------- 668 // ProfileBufferBlockIndex 669 670 // ProfileBufferBlockIndex, serialized as the underlying value. 671 template <> 672 struct ProfileBufferEntryWriter::Serializer<ProfileBufferBlockIndex> { 673 static constexpr Length Bytes(const ProfileBufferBlockIndex& aBlockIndex) { 674 return sizeof(ProfileBufferBlockIndex); 675 } 676 677 static void Write(ProfileBufferEntryWriter& aEW, 678 const ProfileBufferBlockIndex& aBlockIndex) { 679 aEW.WriteBytes(&aBlockIndex, sizeof(aBlockIndex)); 680 } 681 }; 682 683 template <> 684 struct ProfileBufferEntryReader::Deserializer<ProfileBufferBlockIndex> { 685 static void ReadInto(ProfileBufferEntryReader& aER, 686 ProfileBufferBlockIndex& aBlockIndex) { 687 aER.ReadBytes(&aBlockIndex, sizeof(aBlockIndex)); 688 } 689 690 static ProfileBufferBlockIndex Read(ProfileBufferEntryReader& aER) { 691 ProfileBufferBlockIndex blockIndex; 692 ReadInto(aER, blockIndex); 693 return blockIndex; 694 } 695 }; 696 697 // ---------------------------------------------------------------------------- 698 // Literal C string pointer 699 700 // Wrapper around a pointer to a literal C string. 701 template <size_t NonTerminalCharacters> 702 struct ProfileBufferLiteralCStringPointer { 703 const char* mCString; 704 }; 705 706 // Wrap a pointer to a literal C string. 707 template <size_t CharactersIncludingTerminal> 708 ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal - 1> 709 WrapProfileBufferLiteralCStringPointer( 710 const char (&aCString)[CharactersIncludingTerminal]) { 711 return {aCString}; 712 } 713 714 // Literal C strings, serialized as the raw pointer because it is unique and 715 // valid for the whole program lifetime. 716 // 717 // Usage: `aEW.WriteObject(WrapProfileBufferLiteralCStringPointer("hi"));`. 718 // 719 // No deserializer is provided for this type, instead it must be deserialized as 720 // a raw pointer: `aER.ReadObject<const char*>();` 721 template <size_t CharactersIncludingTerminal> 722 struct ProfileBufferEntryReader::Deserializer< 723 ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>> { 724 static constexpr Length Bytes( 725 const ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>&) { 726 // We're only storing a pointer, its size is independent from the pointer 727 // value. 728 return sizeof(const char*); 729 } 730 731 static void Write( 732 ProfileBufferEntryWriter& aEW, 733 const ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>& 734 aWrapper) { 735 // Write the pointer *value*, not the string contents. 736 aEW.WriteBytes(aWrapper.mCString, sizeof(aWrapper.mCString)); 737 } 738 }; 739 740 // ---------------------------------------------------------------------------- 741 // C string contents 742 743 // Wrapper around a pointer to a C string whose contents will be serialized. 744 struct ProfileBufferUnownedCString { 745 const char* mCString; 746 }; 747 748 // Wrap a pointer to a C string whose contents will be serialized. 749 inline ProfileBufferUnownedCString WrapProfileBufferUnownedCString( 750 const char* aCString) { 751 return {aCString}; 752 } 753 754 // The contents of a (probably) unowned C string are serialized as the number of 755 // characters (encoded as ULEB128) and all the characters in the string. The 756 // terminal '\0' is omitted. 757 // 758 // Usage: `aEW.WriteObject(WrapProfileBufferUnownedCString(str.c_str()))`. 759 // 760 // No deserializer is provided for this pointer type, instead it must be 761 // deserialized as one of the other string types that manages its contents, 762 // e.g.: `aER.ReadObject<std::string>();` 763 template <> 764 struct ProfileBufferEntryWriter::Serializer<ProfileBufferUnownedCString> { 765 static Length Bytes(const ProfileBufferUnownedCString& aS) { 766 const auto len = strlen(aS.mCString); 767 return ULEB128Size(len) + len; 768 } 769 770 static void Write(ProfileBufferEntryWriter& aEW, 771 const ProfileBufferUnownedCString& aS) { 772 const auto len = strlen(aS.mCString); 773 aEW.WriteULEB128(len); 774 aEW.WriteBytes(aS.mCString, len); 775 } 776 }; 777 778 // ---------------------------------------------------------------------------- 779 // Raw pointers 780 781 // Wrapper around a pointer to be serialized as the raw pointer value. 782 template <typename T> 783 struct ProfileBufferRawPointer { 784 T* mRawPointer; 785 }; 786 787 // Wrap a pointer to be serialized as the raw pointer value. 788 template <typename T> 789 ProfileBufferRawPointer<T> WrapProfileBufferRawPointer(T* aRawPointer) { 790 return {aRawPointer}; 791 } 792 793 // Raw pointers are serialized as the raw pointer value. 794 // 795 // Usage: `aEW.WriteObject(WrapProfileBufferRawPointer(ptr));` 796 // 797 // The wrapper is compulsory when writing pointers (to avoid unexpected 798 // leaks/UAFs), but reading can be done straight into a raw pointer object, 799 // e.g.: `aER.ReadObject<Foo*>;`. 800 template <typename T> 801 struct ProfileBufferEntryWriter::Serializer<ProfileBufferRawPointer<T>> { 802 template <typename U> 803 static constexpr Length Bytes(const U&) { 804 return sizeof(T*); 805 } 806 807 static void Write(ProfileBufferEntryWriter& aEW, 808 const ProfileBufferRawPointer<T>& aWrapper) { 809 aEW.WriteBytes(&aWrapper.mRawPointer, sizeof(aWrapper.mRawPointer)); 810 } 811 }; 812 813 // Usage: `aER.ReadObject<Foo*>;` or `Foo* p; aER.ReadIntoObject(p);`, no 814 // wrapper necessary. 815 template <typename T> 816 struct ProfileBufferEntryReader::Deserializer<ProfileBufferRawPointer<T>> { 817 static void ReadInto(ProfileBufferEntryReader& aER, 818 ProfileBufferRawPointer<T>& aPtr) { 819 aER.ReadBytes(&aPtr.mRawPointer, sizeof(aPtr)); 820 } 821 822 static ProfileBufferRawPointer<T> Read(ProfileBufferEntryReader& aER) { 823 ProfileBufferRawPointer<T> rawPointer; 824 ReadInto(aER, rawPointer); 825 return rawPointer; 826 } 827 }; 828 829 // ---------------------------------------------------------------------------- 830 // std::string contents 831 832 // std::string contents are serialized as the number of characters (encoded as 833 // ULEB128) and all the characters in the string. The terminal '\0' is omitted. 834 // 835 // Usage: `std::string s = ...; aEW.WriteObject(s);` 836 template <typename CHAR> 837 struct ProfileBufferEntryWriter::Serializer<std::basic_string<CHAR>> { 838 static Length Bytes(const std::basic_string<CHAR>& aS) { 839 const Length len = static_cast<Length>(aS.length()); 840 return ULEB128Size(len) + len; 841 } 842 843 static void Write(ProfileBufferEntryWriter& aEW, 844 const std::basic_string<CHAR>& aS) { 845 const Length len = static_cast<Length>(aS.length()); 846 aEW.WriteULEB128(len); 847 aEW.WriteBytes(aS.c_str(), len * sizeof(CHAR)); 848 } 849 }; 850 851 // Usage: `std::string s = aEW.ReadObject<std::string>(s);` or 852 // `std::string s; aER.ReadIntoObject(s);` 853 template <typename CHAR> 854 struct ProfileBufferEntryReader::Deserializer<std::basic_string<CHAR>> { 855 static void ReadCharsInto(ProfileBufferEntryReader& aER, 856 std::basic_string<CHAR>& aS, size_t aLength) { 857 // Assign to `aS` by using iterators. 858 // (`aER+0` so we get the same iterator type as `aER+len`.) 859 aS.assign(aER, aER.EmptyIteratorAtOffset(aLength)); 860 aER += aLength; 861 } 862 863 static void ReadInto(ProfileBufferEntryReader& aER, 864 std::basic_string<CHAR>& aS) { 865 ReadCharsInto( 866 aER, aS, 867 aER.ReadULEB128<typename std::basic_string<CHAR>::size_type>()); 868 } 869 870 static std::basic_string<CHAR> ReadChars(ProfileBufferEntryReader& aER, 871 size_t aLength) { 872 // Construct a string by using iterators. 873 // (`aER+0` so we get the same iterator type as `aER+len`.) 874 std::basic_string<CHAR> s(aER, aER.EmptyIteratorAtOffset(aLength)); 875 aER += aLength; 876 return s; 877 } 878 879 static std::basic_string<CHAR> Read(ProfileBufferEntryReader& aER) { 880 return ReadChars( 881 aER, aER.ReadULEB128<typename std::basic_string<CHAR>::size_type>()); 882 } 883 }; 884 885 // ---------------------------------------------------------------------------- 886 // mozilla::UniqueFreePtr<CHAR> 887 888 // UniqueFreePtr<CHAR>, which points at a string allocated with `malloc` 889 // (typically generated by `strdup()`), is serialized as the number of 890 // *bytes* (encoded as ULEB128) and all the characters in the string. The 891 // null terminator is omitted. 892 // `CHAR` can be any type that has a specialization for 893 // `std::char_traits<CHAR>::length(const CHAR*)`. 894 // 895 // Note: A nullptr pointer will be serialized like an empty string, so when 896 // deserializing it will result in an allocated buffer only containing a 897 // single null terminator. 898 template <typename CHAR> 899 struct ProfileBufferEntryWriter::Serializer<UniqueFreePtr<CHAR>> { 900 static Length Bytes(const UniqueFreePtr<CHAR>& aS) { 901 if (!aS) { 902 // Null pointer, store it as if it was an empty string (so: 0 bytes). 903 return ULEB128Size(0u); 904 } 905 // Note that we store the size in *bytes*, not in number of characters. 906 const auto bytes = std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR); 907 return ULEB128Size(bytes) + bytes; 908 } 909 910 static void Write(ProfileBufferEntryWriter& aEW, 911 const UniqueFreePtr<CHAR>& aS) { 912 if (!aS) { 913 // Null pointer, store it as if it was an empty string (so we write a 914 // length of 0 bytes). 915 aEW.WriteULEB128(0u); 916 return; 917 } 918 // Note that we store the size in *bytes*, not in number of characters. 919 const auto bytes = std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR); 920 aEW.WriteULEB128(bytes); 921 aEW.WriteBytes(aS.get(), bytes); 922 } 923 }; 924 925 template <typename CHAR> 926 struct ProfileBufferEntryReader::Deserializer<UniqueFreePtr<CHAR>> { 927 static void ReadInto(ProfileBufferEntryReader& aER, UniqueFreePtr<CHAR>& aS) { 928 aS = Read(aER); 929 } 930 931 static UniqueFreePtr<CHAR> Read(ProfileBufferEntryReader& aER) { 932 // Read the number of *bytes* that follow. 933 const auto bytes = aER.ReadULEB128<size_t>(); 934 // We need a buffer of the non-const character type. 935 using NC_CHAR = std::remove_const_t<CHAR>; 936 // We allocate the required number of bytes, plus one extra character for 937 // the null terminator. 938 NC_CHAR* buffer = static_cast<NC_CHAR*>(malloc(bytes + sizeof(NC_CHAR))); 939 // Copy the characters into the buffer. 940 aER.ReadBytes(buffer, bytes); 941 // And append a null terminator. 942 buffer[bytes / sizeof(NC_CHAR)] = NC_CHAR(0); 943 return UniqueFreePtr<CHAR>(buffer); 944 } 945 }; 946 947 // ---------------------------------------------------------------------------- 948 // std::tuple 949 950 // std::tuple is serialized as a sequence of each recursively-serialized item. 951 // 952 // This is equivalent to manually serializing each item, so reading/writing 953 // tuples is equivalent to reading/writing their elements in order, e.g.: 954 // ``` 955 // std::tuple<int, std::string> is = ...; 956 // aEW.WriteObject(is); // Write the tuple, equivalent to: 957 // aEW.WriteObject(/* int */ std::get<0>(is), /* string */ std::get<1>(is)); 958 // ... 959 // // Reading back can be done directly into a tuple: 960 // auto is = aER.ReadObject<std::tuple<int, std::string>>(); 961 // // Or each item could be read separately: 962 // auto i = aER.ReadObject<int>(); auto s = aER.ReadObject<std::string>(); 963 // ``` 964 template <typename... Ts> 965 struct ProfileBufferEntryWriter::Serializer<std::tuple<Ts...>> { 966 private: 967 template <size_t... Is> 968 static Length TupleBytes(const std::tuple<Ts...>& aTuple, 969 std::index_sequence<Is...>) { 970 return (0 + ... + SumBytes(std::get<Is>(aTuple))); 971 } 972 973 template <size_t... Is> 974 static void TupleWrite(ProfileBufferEntryWriter& aEW, 975 const std::tuple<Ts...>& aTuple, 976 std::index_sequence<Is...>) { 977 (aEW.WriteObject(std::get<Is>(aTuple)), ...); 978 } 979 980 public: 981 static Length Bytes(const std::tuple<Ts...>& aTuple) { 982 // Generate a 0..N-1 index pack, we'll add the sizes of each item. 983 return TupleBytes(aTuple, std::index_sequence_for<Ts...>()); 984 } 985 986 static void Write(ProfileBufferEntryWriter& aEW, 987 const std::tuple<Ts...>& aTuple) { 988 // Generate a 0..N-1 index pack, we'll write each item. 989 TupleWrite(aEW, aTuple, std::index_sequence_for<Ts...>()); 990 } 991 }; 992 993 template <typename... Ts> 994 struct ProfileBufferEntryReader::Deserializer<std::tuple<Ts...>> { 995 template <size_t I> 996 static void TupleIReadInto(ProfileBufferEntryReader& aER, 997 std::tuple<Ts...>& aTuple) { 998 aER.ReadIntoObject(std::get<I>(aTuple)); 999 } 1000 1001 template <size_t... Is> 1002 static void TupleReadInto(ProfileBufferEntryReader& aER, 1003 std::tuple<Ts...>& aTuple, 1004 std::index_sequence<Is...>) { 1005 (TupleIReadInto<Is>(aER, aTuple), ...); 1006 } 1007 1008 static void ReadInto(ProfileBufferEntryReader& aER, 1009 std::tuple<Ts...>& aTuple) { 1010 TupleReadInto(aER, aTuple, std::index_sequence_for<Ts...>()); 1011 } 1012 1013 static std::tuple<Ts...> Read(ProfileBufferEntryReader& aER) { 1014 // Note that this creates default `Ts` first, and then overwrites them. 1015 std::tuple<Ts...> ob; 1016 ReadInto(aER, ob); 1017 return ob; 1018 } 1019 }; 1020 // ---------------------------------------------------------------------------- 1021 // mozilla::Span 1022 1023 // Span. All elements are serialized in sequence. 1024 // The caller is assumed to know the number of elements (they may manually 1025 // write&read it before the span if needed). 1026 // Similar to tuples, reading/writing spans is equivalent to reading/writing 1027 // their elements in order. 1028 template <class T, size_t N> 1029 struct ProfileBufferEntryWriter::Serializer<Span<T, N>> { 1030 static Length Bytes(const Span<T, N>& aSpan) { 1031 Length bytes = 0; 1032 for (const T& element : aSpan) { 1033 bytes += SumBytes(element); 1034 } 1035 return bytes; 1036 } 1037 1038 static void Write(ProfileBufferEntryWriter& aEW, const Span<T, N>& aSpan) { 1039 for (const T& element : aSpan) { 1040 aEW.WriteObject(element); 1041 } 1042 } 1043 }; 1044 1045 template <class T, size_t N> 1046 struct ProfileBufferEntryReader::Deserializer<Span<T, N>> { 1047 // Read elements back into span pointing at a pre-allocated buffer. 1048 static void ReadInto(ProfileBufferEntryReader& aER, Span<T, N>& aSpan) { 1049 for (T& element : aSpan) { 1050 aER.ReadIntoObject(element); 1051 } 1052 } 1053 1054 // A Span does not own its data, this would probably leak so we forbid this. 1055 static Span<T, N> Read(ProfileBufferEntryReader& aER) = delete; 1056 }; 1057 1058 // ---------------------------------------------------------------------------- 1059 // mozilla::Maybe 1060 1061 // Maybe<T> is serialized as one byte containing either 'm' (Nothing), 1062 // or 'M' followed by the recursively-serialized `T` object. 1063 template <typename T> 1064 struct ProfileBufferEntryWriter::Serializer<Maybe<T>> { 1065 static Length Bytes(const Maybe<T>& aMaybe) { 1066 // 1 byte to store nothing/something flag, then object size if present. 1067 return aMaybe.isNothing() ? 1 : (1 + SumBytes(aMaybe.ref())); 1068 } 1069 1070 static void Write(ProfileBufferEntryWriter& aEW, const Maybe<T>& aMaybe) { 1071 // 'm'/'M' is just an arbitrary 1-byte value to distinguish states. 1072 if (aMaybe.isNothing()) { 1073 aEW.WriteObject<char>('m'); 1074 } else { 1075 aEW.WriteObject<char>('M'); 1076 // Use the Serializer for the contained type. 1077 aEW.WriteObject(aMaybe.ref()); 1078 } 1079 } 1080 }; 1081 1082 template <typename T> 1083 struct ProfileBufferEntryReader::Deserializer<Maybe<T>> { 1084 static void ReadInto(ProfileBufferEntryReader& aER, Maybe<T>& aMaybe) { 1085 char c = aER.ReadObject<char>(); 1086 if (c == 'm') { 1087 aMaybe.reset(); 1088 } else { 1089 MOZ_ASSERT(c == 'M'); 1090 // If aMaybe is empty, create a default `T` first, to be overwritten. 1091 // Otherwise we'll just overwrite whatever was already there. 1092 if (aMaybe.isNothing()) { 1093 aMaybe.emplace(); 1094 } 1095 // Use the Deserializer for the contained type. 1096 aER.ReadIntoObject(aMaybe.ref()); 1097 } 1098 } 1099 1100 static Maybe<T> Read(ProfileBufferEntryReader& aER) { 1101 Maybe<T> maybe; 1102 char c = aER.ReadObject<char>(); 1103 MOZ_ASSERT(c == 'M' || c == 'm'); 1104 if (c == 'M') { 1105 // Note that this creates a default `T` inside the Maybe first, and then 1106 // overwrites it. 1107 maybe = Some(T{}); 1108 // Use the Deserializer for the contained type. 1109 aER.ReadIntoObject(maybe.ref()); 1110 } 1111 return maybe; 1112 } 1113 }; 1114 1115 // ---------------------------------------------------------------------------- 1116 // mozilla::Variant 1117 1118 // Variant is serialized as the tag (0-based index of the stored type, encoded 1119 // as ULEB128), and the recursively-serialized object. 1120 template <typename... Ts> 1121 struct ProfileBufferEntryWriter::Serializer<Variant<Ts...>> { 1122 public: 1123 static Length Bytes(const Variant<Ts...>& aVariantTs) { 1124 return aVariantTs.match([](auto aIndex, const auto& aAlternative) { 1125 return ULEB128Size(aIndex) + SumBytes(aAlternative); 1126 }); 1127 } 1128 1129 static void Write(ProfileBufferEntryWriter& aEW, 1130 const Variant<Ts...>& aVariantTs) { 1131 aVariantTs.match([&aEW](auto aIndex, const auto& aAlternative) { 1132 aEW.WriteULEB128(aIndex); 1133 aEW.WriteObject(aAlternative); 1134 }); 1135 } 1136 }; 1137 1138 template <typename... Ts> 1139 struct ProfileBufferEntryReader::Deserializer<Variant<Ts...>> { 1140 private: 1141 // Called from the fold expression in `VariantReadInto()`, only the selected 1142 // variant will deserialize the object. 1143 template <size_t I> 1144 static void VariantIReadInto(ProfileBufferEntryReader& aER, 1145 Variant<Ts...>& aVariantTs, unsigned aTag) { 1146 if (I == aTag) { 1147 // Ensure the variant contains the target type. Note that this may create 1148 // a default object. 1149 if (!aVariantTs.template is<I>()) { 1150 aVariantTs = Variant<Ts...>(VariantIndex<I>{}); 1151 } 1152 aER.ReadIntoObject(aVariantTs.template as<I>()); 1153 } 1154 } 1155 1156 template <size_t... Is> 1157 static void VariantReadInto(ProfileBufferEntryReader& aER, 1158 Variant<Ts...>& aVariantTs, 1159 std::index_sequence<Is...>) { 1160 unsigned tag = aER.ReadULEB128<unsigned>(); 1161 (VariantIReadInto<Is>(aER, aVariantTs, tag), ...); 1162 } 1163 1164 public: 1165 static void ReadInto(ProfileBufferEntryReader& aER, 1166 Variant<Ts...>& aVariantTs) { 1167 // Generate a 0..N-1 index pack, the selected variant will deserialize 1168 // itself. 1169 VariantReadInto(aER, aVariantTs, std::index_sequence_for<Ts...>()); 1170 } 1171 1172 static Variant<Ts...> Read(ProfileBufferEntryReader& aER) { 1173 // Note that this creates a default `Variant` of the first type, and then 1174 // overwrites it. Consider using `ReadInto` for more control if needed. 1175 Variant<Ts...> variant(VariantIndex<0>{}); 1176 ReadInto(aER, variant); 1177 return variant; 1178 } 1179 }; 1180 1181 } // namespace mozilla 1182 1183 #endif // ProfileBufferEntrySerialization_h