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