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"
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
{
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)
44 // /* Call `aER.ReadX(...)` function to deserialize into aT, be sure to
45 // read exactly `Bytes(aT)`! */
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)`! */
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
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);
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
&) =
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);
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);
159 // mCurrentSpan will be empty, move mNextSpanOrEmpty to mCurrentSpan.
160 mCurrentSpan
= mNextSpanOrEmpty
;
161 mNextSpanOrEmpty
= mNextSpanOrEmpty
.Last(0);
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
176 mCurrentSpan
= mNextSpanOrEmpty
;
177 mNextSpanOrEmpty
= mNextSpanOrEmpty
.Last(0);
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.
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);
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() {
240 DoubleSpanOfConstBytes(const Byte
* aOnlyPointer
, size_t aOnlyLength
)
241 : mFirstOrOnly(aOnlyPointer
, aOnlyLength
), mSecondOrEmpty() {
245 DoubleSpanOfConstBytes(const Byte
* aFirstPointer
, size_t aFirstLength
,
246 const Byte
* aSecondPointer
, size_t aSecondLength
)
247 : mFirstOrOnly(aFirstPointer
, aFirstLength
),
248 mSecondOrEmpty(aSecondPointer
, aSecondLength
) {
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
);
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);
328 friend class ProfileBufferEntryWriter
;
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
{
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. */
366 // static void Write(ProfileBufferEntryWriter& aEW,
368 // /* Call `aEW.WriteX(...)` functions to serialize aT, be sure to write
369 // exactly `Bytes(aT)` bytes! */
371 template <typename T
>
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
) {
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;
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
;
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);
453 (MOZ_LIKELY(!mCurrentSpan
.IsEmpty()) ? mCurrentSpan
: mNextSpanOrEmpty
)
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);
462 // mCurrentSpan is empty, move mNextSpanOrEmpty (past the first byte) to
464 MOZ_RELEASE_ASSERT(mNextSpanOrEmpty
.LengthBytes() >= 1);
465 mCurrentSpan
= mNextSpanOrEmpty
.From(1);
466 mNextSpanOrEmpty
= mNextSpanOrEmpty
.First(0);
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
);
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.
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);
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
);
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());
530 aBytes
- static_cast<Length
>(mCurrentSpan
.LengthBytes());
531 memcpy(mNextSpanOrEmpty
.Elements(),
532 reinterpret_cast<const Byte
*>(aSrc
) + mCurrentSpan
.LengthBytes(),
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()));
547 WriteBytes(aReader
.mCurrentSpan
.Elements(), read0
);
549 Length read1
= aBytes
- read0
;
551 WriteBytes(aReader
.mNextSpanOrEmpty
.Elements(), read1
);
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
), ...);
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.,
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.
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.
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
));
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
);
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
]) {
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
729 return sizeof(const char*);
733 ProfileBufferEntryWriter
& aEW
,
734 const ProfileBufferLiteralCStringPointer
<CharactersIncludingTerminal
>&
736 // Write the pointer *value*, not the string contents.
737 aEW
.WriteBytes(aWrapper
.mCString
, sizeof(aWrapper
.mCString
));
741 // ----------------------------------------------------------------------------
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
) {
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>();`
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 // ----------------------------------------------------------------------------
782 // Wrapper around a pointer to be serialized as the raw pointer value.
783 template <typename T
>
784 struct ProfileBufferRawPointer
{
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
&) {
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
);
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
));
864 static void ReadInto(ProfileBufferEntryReader
& aER
,
865 std::basic_string
<CHAR
>& aS
) {
868 aER
.ReadULEB128
<typename
std::basic_string
<CHAR
>::size_type
>());
871 static std::basic_string
<CHAR
> ReadChars(ProfileBufferEntryReader
& aER
,
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
));
880 static std::basic_string
<CHAR
> Read(ProfileBufferEntryReader
& aER
) {
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
) {
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
) {
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);
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
) {
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 // ----------------------------------------------------------------------------
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.:
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));
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>();
965 template <typename
... Ts
>
966 struct ProfileBufferEntryWriter::Serializer
<std::tuple
<Ts
...>> {
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
)), ...);
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
...>> {
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
;
1021 // ----------------------------------------------------------------------------
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
) {
1033 for (const T
& element
: aSpan
) {
1034 bytes
+= SumBytes(element
);
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 // ----------------------------------------------------------------------------
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');
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>();
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()) {
1096 // Use the Deserializer for the contained type.
1097 aER
.ReadIntoObject(aMaybe
.ref());
1101 static Maybe
<T
> Read(ProfileBufferEntryReader
& aER
) {
1103 char c
= aER
.ReadObject
<char>();
1104 MOZ_ASSERT(c
== 'M' || c
== 'm');
1106 // Note that this creates a default `T` inside the Maybe first, and then
1109 // Use the Deserializer for the contained type.
1110 aER
.ReadIntoObject(maybe
.ref());
1116 // ----------------------------------------------------------------------------
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
...>> {
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
...>> {
1142 // Called from the fold expression in `VariantReadInto()`, only the selected
1143 // variant will deserialize the object.
1145 static void VariantIReadInto(ProfileBufferEntryReader
& aER
,
1146 Variant
<Ts
...>& aVariantTs
, unsigned 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
), ...);
1166 static void ReadInto(ProfileBufferEntryReader
& aER
,
1167 Variant
<Ts
...>& aVariantTs
) {
1168 // Generate a 0..N-1 index pack, the selected variant will deserialize
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
);
1182 } // namespace mozilla
1184 #endif // ProfileBufferEntrySerialization_h