Bug 1620515 - ProfileBufferEntrySerialization.h with (de)serializers - r=canaltinova
authorGerald Squelart <gsquelart@mozilla.com>
Wed, 11 Mar 2020 21:56:50 +0000
changeset 518268 6dd083a1efc11e7c635a797d16caedd7f2faa411
parent 518267 b5ff9d8d43ece9e199a200b68f4ef73df3a7d9a5
child 518269 b8005deff29e62a0072ad9b813f28c28bdde0ad0
push id37206
push useraciure@mozilla.com
push dateThu, 12 Mar 2020 03:57:49 +0000
treeherdermozilla-central@4fd5c458be4c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscanaltinova
bugs1620515
milestone76.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1620515 - ProfileBufferEntrySerialization.h with (de)serializers - r=canaltinova ProfileBufferEntrySerialization.h will contain the entry reader and writer. This patch creates the file with renamed copies of the (de)serializers from BlocksRingBuffer; there shouldn't be significant code changes, this `hg cp` will help keep their history. See next patch for how they will actually be used. Differential Revision: https://phabricator.services.mozilla.com/D65695
mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
copy from mozglue/baseprofiler/public/BlocksRingBuffer.h
copy to mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
--- a/mozglue/baseprofiler/public/BlocksRingBuffer.h
+++ b/mozglue/baseprofiler/public/ProfileBufferEntrySerialization.h
@@ -1,1389 +1,359 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#ifndef BlocksRingBuffer_h
-#define BlocksRingBuffer_h
+#ifndef ProfileBufferEntrySerialization_h
+#define ProfileBufferEntrySerialization_h
 
-#include "mozilla/DebugOnly.h"
-#include "mozilla/BaseProfilerDetail.h"
-#include "mozilla/ModuloBuffer.h"
-#include "mozilla/Pair.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/leb128iterator.h"
+#include "mozilla/Likely.h"
+#include "mozilla/Maybe.h"
 #include "mozilla/ProfileBufferIndex.h"
-#include "mozilla/Unused.h"
-
-#include "mozilla/Maybe.h"
 #include "mozilla/Span.h"
 #include "mozilla/Tuple.h"
 #include "mozilla/UniquePtrExtensions.h"
+#include "mozilla/Unused.h"
 #include "mozilla/Variant.h"
 
-#include <functional>
 #include <string>
 #include <tuple>
-#include <utility>
 
 namespace mozilla {
 
-// Thread-safe Ring buffer that can store blocks of different sizes during
-// defined sessions.
-// Each *block* contains an *entry* and the entry size:
-// [ entry_size | entry ] [ entry_size | entry ] ...
-// *In-session* is a period of time during which `BlocksRingBuffer` allows
-// reading and writing. *Out-of-session*, the `BlocksRingBuffer` object is
-// still valid, but contains no data, and gracefully denies accesses.
-//
-// To write an entry, the buffer reserves a block of sufficient size (to contain
-// user data of predetermined size), writes the entry size, and lets the caller
-// fill the entry contents using ModuloBuffer::Iterator APIs and a few entry-
-// specific APIs. E.g.:
-// ```
-// BlockRingsBuffer brb(PowerOfTwo<BlockRingsBuffer::Length>(1024));
-// brb.ReserveAndPut([]() { return sizeof(123); },
-//                   [&](BlocksRingBuffer::EntryWriter& aEW) {
-//                     aEW.WriteObject(123);
-//                   });
-// ```
-// Other `Put...` functions may be used as shortcuts for simple entries.
-// The objects given to the caller's callbacks should only be used inside the
-// callbacks and not stored elsewhere, because they keep their own references to
-// the BlocksRingBuffer and therefore should not live longer.
-// Different type of objects may be serialized into an entry, see `Serializer`
-// for more information.
-//
-// When reading data, the buffer iterates over blocks (it knows how to read the
-// entry size, and therefore move to the next block), and lets the caller read
-// the entry inside of each block. E.g.:
-// ```
-// brb.Read([](BlocksRingBuffer::Reader aR) {}
-//   for (BlocksRingBuffer::EntryReader aER : aR) {
-//     /* Use EntryReader functions to read back serialized objects. */
-//     int n = aER.ReadObject<int>();
-//   }
-// });
-// ```
-// Different type of objects may be deserialized from an entry, see
-// `Deserializer` for more information.
-//
-// The caller may retrieve the `ProfileBufferBlockIndex` corresponding to an
-// entry (`ProfileBufferBlockIndex` is an opaque type preventing the user from
-// modifying it). That index may later be used to get back to that particular
-// entry if it still exists.
-class BlocksRingBuffer {
+class ProfileBufferEntryWriter;
+
+class ProfileBufferEntryReader {
  public:
-  // Using ModuloBuffer as underlying circular byte buffer.
-  using Buffer = ModuloBuffer<uint32_t, ProfileBufferIndex>;
-  using Byte = Buffer::Byte;
-  using BufferWriter = Buffer::Writer;
-  using BufferReader = Buffer::Reader;
-
-  // Length type for total buffer (as PowerOfTwo<Length>) and each entry.
-  using Length = uint32_t;
-
-  // Class to be specialized for types to be read from a BlocksRingBuffer.
+  // Class to be specialized for types to be read from a profile buffer entry.
   // See common specializations at the bottom of this header.
   // The following static functions must be provided:
   //   static void ReadInto(EntryReader aER&, T& aT)
   //   {
   //     /* Call `aER.ReadX(...)` function to deserialize into aT, be sure to
   //        read exactly `Bytes(aT)`! */
   //   }
   //   static T Read(EntryReader& aER) {
   //     /* Call `aER.ReadX(...)` function to deserialize and return a `T`, be
   //        sure to read exactly `Bytes(returned value)`! */
   //   }
   template <typename T>
   struct Deserializer;
+};
 
-  // Class to be specialized for types to be written in a BlocksRingBuffer.
+class ProfileBufferEntryWriter {
+ public:
+  // Class to be specialized for types to be written in an entry.
   // See common specializations at the bottom of this header.
   // The following static functions must be provided:
   //   static Length Bytes(const T& aT) {
   //     /* Return number of bytes that will be written. */
   //   }
-  //   static void Write(EntryWriter& aEW,
+  //   static void Write(ProfileBufferEntryWriter& aEW,
   //                     const T& aT) {
   //     /* Call `aEW.WriteX(...)` functions to serialize aT, be sure to write
   //        exactly `Bytes(aT)` bytes! */
   //   }
   template <typename T>
   struct Serializer;
-
-  enum class ThreadSafety { WithoutMutex, WithMutex };
-
-  // Default constructor starts out-of-session (nothing to read or write).
-  explicit BlocksRingBuffer(ThreadSafety aThreadSafety)
-      : mMutex(aThreadSafety != ThreadSafety::WithoutMutex) {}
-
-  // Create a buffer of the given length.
-  explicit BlocksRingBuffer(ThreadSafety aThreadSafety,
-                            PowerOfTwo<Length> aLength)
-      : mMutex(aThreadSafety != ThreadSafety::WithoutMutex),
-        mMaybeUnderlyingBuffer(Some(UnderlyingBuffer(aLength))) {}
-
-  // Take ownership of an existing buffer.
-  BlocksRingBuffer(ThreadSafety aThreadSafety,
-                   UniquePtr<Buffer::Byte[]> aExistingBuffer,
-                   PowerOfTwo<Length> aLength)
-      : mMutex(aThreadSafety != ThreadSafety::WithoutMutex),
-        mMaybeUnderlyingBuffer(
-            Some(UnderlyingBuffer(std::move(aExistingBuffer), aLength))) {}
-
-  // Use an externally-owned buffer.
-  BlocksRingBuffer(ThreadSafety aThreadSafety, Buffer::Byte* aExternalBuffer,
-                   PowerOfTwo<Length> aLength)
-      : mMutex(aThreadSafety != ThreadSafety::WithoutMutex),
-        mMaybeUnderlyingBuffer(
-            Some(UnderlyingBuffer(aExternalBuffer, aLength))) {}
-
-  // Destructor doesn't need to do anything special. (Clearing entries would
-  // only update indices and stats, which won't be accessible after the object
-  // is destroyed anyway.)
-  ~BlocksRingBuffer() = default;
-
-  // Remove underlying buffer, if any.
-  void Reset() {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    ResetUnderlyingBuffer();
-  }
-
-  // Create a buffer of the given length.
-  void Set(PowerOfTwo<Length> aLength) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    ResetUnderlyingBuffer();
-    mMaybeUnderlyingBuffer.emplace(aLength);
-  }
-
-  // Take ownership of an existing buffer.
-  void Set(UniquePtr<Buffer::Byte[]> aExistingBuffer,
-           PowerOfTwo<Length> aLength) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    ResetUnderlyingBuffer();
-    mMaybeUnderlyingBuffer.emplace(std::move(aExistingBuffer), aLength);
-  }
-
-  // Use an externally-owned buffer.
-  void Set(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    ResetUnderlyingBuffer();
-    mMaybeUnderlyingBuffer.emplace(aExternalBuffer, aLength);
-  }
-
-  bool IsThreadSafe() const { return mMutex.IsActivated(); }
-
-  // Lock the buffer mutex and run the provided callback.
-  // This can be useful when the caller needs to explicitly lock down this
-  // buffer, but not do anything else with it.
-  template <typename Callback>
-  auto LockAndRun(Callback&& aCallback) const {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    return std::forward<Callback>(aCallback)();
-  }
-
-  // Buffer length in bytes.
-  Maybe<PowerOfTwo<Length>> BufferLength() const {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    return mMaybeUnderlyingBuffer.map([](const UnderlyingBuffer& aBuffer) {
-      return aBuffer.mBuffer.BufferLength();
-    });
-    ;
-  }
-
-  // Size of external resources.
-  size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
-    if (!mMaybeUnderlyingBuffer) {
-      return 0;
-    }
-    return mMaybeUnderlyingBuffer->mBuffer.SizeOfExcludingThis(aMallocSizeOf);
-  }
-
-  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
-    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
-  }
-
-  // Snapshot of the buffer state.
-  struct State {
-    // Index to the first block.
-    ProfileBufferBlockIndex mRangeStart;
-
-    // Index past the last block. Equals mRangeStart if empty.
-    ProfileBufferBlockIndex mRangeEnd;
-
-    // Number of blocks that have been pushed into this buffer.
-    uint64_t mPushedBlockCount = 0;
-
-    // Number of blocks that have been removed from this buffer.
-    // Note: Live entries = pushed - cleared.
-    uint64_t mClearedBlockCount = 0;
-  };
-
-  // Get a snapshot of the current state.
-  // When out-of-session, mFirstReadIndex==mNextWriteIndex, and
-  // mPushedBlockCount==mClearedBlockCount==0.
-  // Note that these may change right after this thread-safe call, so they
-  // should only be used for statistical purposes.
-  State GetState() const {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    return {
-        mFirstReadIndex, mNextWriteIndex,
-        mMaybeUnderlyingBuffer ? mMaybeUnderlyingBuffer->mPushedBlockCount : 0,
-        mMaybeUnderlyingBuffer ? mMaybeUnderlyingBuffer->mClearedBlockCount
-                               : 0};
-  }
-
-  // No objects, no bytes! (May be useful for generic programming.)
-  static Length SumBytes() { return 0; }
-
-  // Number of bytes needed to serialize objects.
-  template <typename T0, typename... Ts>
-  static Length SumBytes(const T0& aT0, const Ts&... aTs) {
-    return Serializer<T0>::Bytes(aT0) + SumBytes(aTs...);
-  }
-
-  // Iterator-like class used to read from an entry.
-  // Created through `BlockIterator`, or a `GetEntryAt()` function, lives
-  // within a lock guard lifetime.
-  class EntryReader : public BufferReader {
-   public:
-    // Allow move-construction.
-    EntryReader(EntryReader&& aOther) = default;
-    // Disallow copying and assignments.
-    EntryReader(const EntryReader& aOther) = delete;
-    EntryReader& operator=(const EntryReader& aOther) = delete;
-    EntryReader& operator=(EntryReader&& aOther) = delete;
-
-#ifdef DEBUG
-    ~EntryReader() {
-      // Expect reader to stay within the entry.
-      MOZ_ASSERT(CurrentIndex() >= mEntryStart);
-      MOZ_ASSERT(CurrentIndex() <= mEntryStart + mEntryBytes);
-      // No EntryReader should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-#endif  // DEBUG
-
-    // All BufferReader (aka ModuloBuffer<uint32_t, ProfileBufferIndex>::Reader)
-    // APIs are available to read data from this entry.
-    // Note that there are no bound checks! So this should not be used with
-    // external untrusted data.
-
-    // Total number of bytes in this entry.
-    Length EntryBytes() const { return mEntryBytes; }
-
-    // Current position of this iterator in the entry.
-    Length IndexInEntry() const {
-      return static_cast<Length>(CurrentIndex() - mEntryStart);
-    }
-
-    // Number of bytes left in this entry after this iterator.
-    Length RemainingBytes() const {
-      return static_cast<Length>(mEntryStart + mEntryBytes - CurrentIndex());
-    }
-
-    template <typename T>
-    void ReadIntoObject(T& aObject) {
-      DebugOnly<Length> start = IndexInEntry();
-      Deserializer<T>::ReadInto(*this, aObject);
-      // Make sure the helper moved the iterator as expected.
-      MOZ_ASSERT(IndexInEntry() == start + SumBytes(aObject));
-    }
-
-    // Allow `EntryReader::ReadIntoObjects()` with nothing, this could be
-    // useful for generic programming.
-    void ReadIntoObjects() {}
-
-    // Read into one or more objects, sequentially.
-    template <typename T0, typename... Ts>
-    void ReadIntoObjects(T0& aT0, Ts&... aTs) {
-      ReadIntoObject(aT0);
-      ReadIntoObjects(aTs...);
-    }
-
-    // Read data as an object and move iterator ahead.
-    template <typename T>
-    T ReadObject() {
-      DebugOnly<Length> start = IndexInEntry();
-      T ob = Deserializer<T>::Read(*this);
-      // Make sure the helper moved the iterator as expected.
-      MOZ_ASSERT(IndexInEntry() == start + SumBytes(ob));
-      return ob;
-    }
-
-    // Can be used as reference to come back to this entry with GetEntryAt().
-    ProfileBufferBlockIndex CurrentBlockIndex() const {
-      return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(
-          mEntryStart - BufferReader::ULEB128Size(mEntryBytes));
-    }
-
-    // Index past the end of this block, which is the start of the next block.
-    ProfileBufferBlockIndex NextBlockIndex() const {
-      return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(mEntryStart +
-                                                                   mEntryBytes);
-    }
-
-    // Index of the first block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeStart() const {
-      return mRing.mFirstReadIndex;
-    }
-
-    // Index past the last block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeEnd() const {
-      return mRing.mNextWriteIndex;
-    }
-
-    // Get another entry based on a {Current,Next}ProfileBufferBlockIndex().
-    // This may fail if the buffer has already looped around and cleared that
-    // block, or for the one-past-the-end index.
-    Maybe<EntryReader> GetEntryAt(ProfileBufferBlockIndex aBlockIndex) {
-      // Don't accept a not-yet-written index.
-      MOZ_ASSERT(aBlockIndex <= BufferRangeEnd());
-      if (aBlockIndex >= BufferRangeStart() && aBlockIndex < BufferRangeEnd()) {
-        // Block is still alive -> Return reader for it.
-        mRing.AssertBlockIndexIsValid(aBlockIndex);
-        return Some(EntryReader(mRing, aBlockIndex));
-      }
-      // Block has been overwritten/cleared.
-      return Nothing();
-    }
-
-    // Get the next entry. This may fail if this is already the last entry.
-    Maybe<EntryReader> GetNextEntry() {
-      const ProfileBufferBlockIndex nextBlockIndex = NextBlockIndex();
-      if (nextBlockIndex < BufferRangeEnd()) {
-        return Some(EntryReader(mRing, nextBlockIndex));
-      }
-      return Nothing();
-    }
-
-   private:
-    // Only a BlocksRingBuffer can instantiate an EntryReader.
-    friend class BlocksRingBuffer;
-
-    explicit EntryReader(const BlocksRingBuffer& aRing,
-                         ProfileBufferBlockIndex aBlockIndex)
-        : BufferReader(aRing.mMaybeUnderlyingBuffer->mBuffer.ReaderAt(
-              aBlockIndex.ConvertToProfileBufferIndex())),
-          mRing(aRing),
-          mEntryBytes(BufferReader::ReadULEB128<Length>()),
-          mEntryStart(CurrentIndex()) {
-      // No EntryReader should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-
-    // Using a non-null pointer instead of a reference, to allow copying.
-    // This EntryReader should only live inside one of the thread-safe
-    // BlocksRingBuffer functions, for this reference to stay valid.
-    const BlocksRingBuffer& mRing;
-    const Length mEntryBytes;
-    const ProfileBufferIndex mEntryStart;
-  };
-
-  class Reader;
-
-  // Class that can iterate through blocks and provide `EntryReader`s.
-  // Created through `Reader`, lives within a lock guard lifetime.
-  class BlockIterator {
-   public:
-#ifdef DEBUG
-    ~BlockIterator() {
-      // No BlockIterator should live outside of a mutexed call.
-      mRing->mMutex.AssertCurrentThreadOwns();
-    }
-#endif  // DEBUG
-
-    // Comparison with other iterator, mostly used in range-for loops.
-    bool operator==(const BlockIterator aRhs) const {
-      MOZ_ASSERT(mRing == aRhs.mRing);
-      return mBlockIndex == aRhs.mBlockIndex;
-    }
-    bool operator!=(const BlockIterator aRhs) const {
-      MOZ_ASSERT(mRing == aRhs.mRing);
-      return mBlockIndex != aRhs.mBlockIndex;
-    }
-
-    // Advance to next BlockIterator.
-    BlockIterator& operator++() {
-      mBlockIndex = NextBlockIndex();
-      return *this;
-    }
-
-    // Dereferencing creates an `EntryReader` for the entry inside this block.
-    EntryReader operator*() const {
-      return mRing->ReaderInBlockAt(mBlockIndex);
-    }
-
-    // True if this iterator is just past the last entry.
-    bool IsAtEnd() const {
-      MOZ_ASSERT(mBlockIndex <= BufferRangeEnd());
-      return mBlockIndex == BufferRangeEnd();
-    }
-
-    // Can be used as reference to come back to this entry with `GetEntryAt()`.
-    ProfileBufferBlockIndex CurrentBlockIndex() const { return mBlockIndex; }
-
-    // Index past the end of this block, which is the start of the next block.
-    ProfileBufferBlockIndex NextBlockIndex() const {
-      MOZ_ASSERT(!IsAtEnd());
-      BufferReader reader = mRing->mMaybeUnderlyingBuffer->mBuffer.ReaderAt(
-          mBlockIndex.ConvertToProfileBufferIndex());
-      Length entrySize = reader.ReadULEB128<Length>();
-      return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(
-          reader.CurrentIndex() + entrySize);
-    }
-
-    // Index of the first block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeStart() const {
-      return mRing->mFirstReadIndex;
-    }
-
-    // Index past the last block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeEnd() const {
-      return mRing->mNextWriteIndex;
-    }
-
-   private:
-    // Only a Reader can instantiate a BlockIterator.
-    friend class Reader;
-
-    BlockIterator(const BlocksRingBuffer& aRing,
-                  ProfileBufferBlockIndex aBlockIndex)
-        : mRing(WrapNotNull(&aRing)), mBlockIndex(aBlockIndex) {
-      // No BlockIterator should live outside of a mutexed call.
-      mRing->mMutex.AssertCurrentThreadOwns();
-    }
-
-    // Using a non-null pointer instead of a reference, to allow copying.
-    // This BlockIterator should only live inside one of the thread-safe
-    // BlocksRingBuffer functions, for this reference to stay valid.
-    NotNull<const BlocksRingBuffer*> mRing;
-    ProfileBufferBlockIndex mBlockIndex;
-  };
-
-  // Class that can create `BlockIterator`s (e.g., for range-for), or just
-  // iterate through entries; lives within a lock guard lifetime.
-  class MOZ_RAII Reader {
-   public:
-    Reader(const Reader&) = delete;
-    Reader& operator=(const Reader&) = delete;
-    Reader(Reader&&) = delete;
-    Reader& operator=(Reader&&) = delete;
-
-#ifdef DEBUG
-    ~Reader() {
-      // No Reader should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-#endif  // DEBUG
-
-    // Index of the first block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeStart() const {
-      return mRing.mFirstReadIndex;
-    }
-
-    // Index past the last block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeEnd() const {
-      return mRing.mNextWriteIndex;
-    }
-
-    // Iterators to the first and past-the-last blocks.
-    // Compatible with range-for (see `ForEach` below as example).
-    BlockIterator begin() const {
-      return BlockIterator(mRing, BufferRangeStart());
-    }
-    // Note that a `BlockIterator` at the `end()` should not be dereferenced, as
-    // there is no actual block there!
-    BlockIterator end() const { return BlockIterator(mRing, BufferRangeEnd()); }
-
-    // Get a `BlockIterator` at the given `ProfileBufferBlockIndex`, clamped to
-    // the stored range. Note that a `BlockIterator` at the `end()` should not
-    // be dereferenced, as there is no actual block there!
-    BlockIterator At(ProfileBufferBlockIndex aBlockIndex) const {
-      if (aBlockIndex < BufferRangeStart()) {
-        // Anything before the range (including null ProfileBufferBlockIndex) is
-        // clamped at the beginning.
-        return begin();
-      }
-      // Otherwise we at least expect the index to be valid (pointing exactly at
-      // a live block, or just past the end.)
-      mRing.AssertBlockIndexIsValidOrEnd(aBlockIndex);
-      return BlockIterator(mRing, aBlockIndex);
-    }
-
-    // Run `aCallback(EntryReader&)` on each entry from first to last.
-    // Callback should not store `EntryReader`, as it may become invalid after
-    // this thread-safe call.
-    template <typename Callback>
-    void ForEach(Callback&& aCallback) const {
-      for (EntryReader reader : *this) {
-        aCallback(reader);
-      }
-    }
-
-   private:
-    friend class BlocksRingBuffer;
-
-    explicit Reader(const BlocksRingBuffer& aRing) : mRing(aRing) {
-      // No Reader should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-
-    // This Reader should only live inside one of the thread-safe
-    // BlocksRingBuffer functions, for this reference to stay valid.
-    const BlocksRingBuffer& mRing;
-  };
-
-  // Call `aCallback(BlocksRingBuffer::Reader*)` (nullptr when out-of-session),
-  // and return whatever `aCallback` returns. Callback should not store
-  // `Reader`, because it may become invalid after this call.
-  template <typename Callback>
-  auto Read(Callback&& aCallback) const {
-    {
-      baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-      if (MOZ_LIKELY(mMaybeUnderlyingBuffer)) {
-        Reader reader(*this);
-        return std::forward<Callback>(aCallback)(&reader);
-      }
-    }
-    return std::forward<Callback>(aCallback)(nullptr);
-  }
-
-  // Call `aCallback(BlocksRingBuffer::EntryReader&)` on each item.
-  // Callback should not store `EntryReader`, because it may become invalid
-  // after this call.
-  template <typename Callback>
-  void ReadEach(Callback&& aCallback) const {
-    Read([&](Reader* aReader) {
-      if (MOZ_LIKELY(aReader)) {
-        aReader->ForEach(aCallback);
-      }
-    });
-  }
-
-  // Call `aCallback(Maybe<BlocksRingBuffer::EntryReader>&&)` on the entry at
-  // the given ProfileBufferBlockIndex; The `Maybe` will be `Nothing` if
-  // out-of-session, or if that entry doesn't exist anymore, or if we've reached
-  // just past the last entry. Return whatever `aCallback` returns. Callback
-  // should not store `EntryReader`, because it may become invalid after this
-  // call.
-  template <typename Callback>
-  auto ReadAt(ProfileBufferBlockIndex aBlockIndex, Callback&& aCallback) const {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    MOZ_ASSERT(aBlockIndex <= mNextWriteIndex);
-    Maybe<EntryReader> maybeEntryReader;
-    if (MOZ_LIKELY(mMaybeUnderlyingBuffer) && aBlockIndex >= mFirstReadIndex &&
-        aBlockIndex < mNextWriteIndex) {
-      AssertBlockIndexIsValid(aBlockIndex);
-      maybeEntryReader.emplace(ReaderInBlockAt(aBlockIndex));
-    }
-    return std::forward<Callback>(aCallback)(std::move(maybeEntryReader));
-  }
-
-  // Class used to write an entry contents.
-  // Created through `Put()`, lives within a lock guard lifetime.
-  class MOZ_RAII EntryWriter : public BufferWriter {
-   public:
-    // Disallow copying, moving, and assignments.
-    EntryWriter(const EntryWriter& aOther) = delete;
-    EntryWriter& operator=(const EntryWriter& aOther) = delete;
-    EntryWriter(EntryWriter&& aOther) = delete;
-    EntryWriter& operator=(EntryWriter&& aOther) = delete;
-
-#ifdef DEBUG
-    ~EntryWriter() {
-      // We expect the caller to completely fill the entry.
-      // (Or at least pretend to, by moving this iterator to the end.)
-      MOZ_ASSERT(RemainingBytes() == 0);
-      // No EntryWriter should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-#endif  // DEBUG
-
-    // All BufferWriter (aka ModuloBuffer<uint32_t, ProfileBufferIndex>::Writer)
-    // APIs are available to read/write data from/to this entry.
-    // Note that there are no bound checks! So this should not be used with
-    // external data.
-
-    // Total number of bytes in this entry.
-    Length EntryBytes() const { return mEntryBytes; }
-
-    // Current position of this iterator in the entry.
-    Length IndexInEntry() const {
-      return static_cast<Length>(CurrentIndex() - mEntryStart);
-    }
-
-    // Number of bytes left in this entry after this iterator.
-    Length RemainingBytes() const {
-      return static_cast<Length>(mEntryStart + mEntryBytes - CurrentIndex());
-    }
-
-    template <typename T>
-    void WriteObject(const T& aObject) {
-      DebugOnly<Length> start = IndexInEntry();
-      Serializer<T>::Write(*this, aObject);
-      // Make sure the helper moved the iterator as expected.
-      MOZ_ASSERT(IndexInEntry() == start + SumBytes(aObject));
-    }
-
-    // Allow `EntryWrite::WriteObjects()` with nothing, this could be useful
-    // for generic programming.
-    void WriteObjects() {}
-
-    // Write one or more objects, sequentially.
-    template <typename T0, typename... Ts>
-    void WriteObjects(const T0& aT0, const Ts&... aTs) {
-      WriteObject(aT0);
-      WriteObjects(aTs...);
-    }
-
-    // Can be used as reference to come back to this entry with `GetEntryAt()`.
-    ProfileBufferBlockIndex CurrentBlockIndex() const {
-      return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(
-          mEntryStart - BufferWriter::ULEB128Size(mEntryBytes));
-    }
-
-    // Index past the end of this block, which will be the start of the next
-    // block to be written.
-    ProfileBufferBlockIndex BlockEndIndex() const {
-      return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(mEntryStart +
-                                                                   mEntryBytes);
-    }
-
-    // Index of the first block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeStart() const {
-      return mRing.mFirstReadIndex;
-    }
-
-    // Index past the last block in the whole buffer.
-    ProfileBufferBlockIndex BufferRangeEnd() const {
-      return mRing.mNextWriteIndex;
-    }
-
-    // Get another entry based on a {Current,Next}BlockIndex(). This may fail if
-    // the buffer has already looped around and cleared that block.
-    Maybe<EntryReader> GetEntryAt(ProfileBufferBlockIndex aBlockIndex) {
-      // Don't accept a not-yet-written index.
-      MOZ_RELEASE_ASSERT(aBlockIndex < BufferRangeEnd());
-      if (aBlockIndex >= BufferRangeStart()) {
-        // Block is still alive -> Return reader for it.
-        mRing.AssertBlockIndexIsValid(aBlockIndex);
-        return Some(EntryReader(mRing, aBlockIndex));
-      }
-      // Block has been overwritten/cleared.
-      return Nothing();
-    }
-
-   private:
-    // Only a BlocksRingBuffer can instantiate an EntryWriter.
-    friend class BlocksRingBuffer;
-
-    // Compute space needed for a block that can contain an entry of size
-    // `aEntryBytes`.
-    static Length BlockSizeForEntrySize(Length aEntryBytes) {
-      return aEntryBytes +
-             static_cast<Length>(BufferWriter::ULEB128Size(aEntryBytes));
-    }
-
-    EntryWriter(BlocksRingBuffer& aRing, ProfileBufferBlockIndex aBlockIndex,
-                Length aEntryBytes)
-        : BufferWriter(aRing.mMaybeUnderlyingBuffer->mBuffer.WriterAt(
-              aBlockIndex.ConvertToProfileBufferIndex())),
-          mRing(aRing),
-          mEntryBytes(aEntryBytes),
-          mEntryStart([&]() {
-            // BufferWriter is at `aBlockIndex`. Write the entry size...
-            BufferWriter::WriteULEB128(aEntryBytes);
-            // ... BufferWriter now at start of entry section.
-            return CurrentIndex();
-          }()) {
-      // No EntryWriter should live outside of a mutexed call.
-      mRing.mMutex.AssertCurrentThreadOwns();
-    }
-
-    // This EntryWriter should only live inside one of the thread-safe
-    // BlocksRingBuffer functions, for this reference to stay valid.
-    BlocksRingBuffer& mRing;
-    const Length mEntryBytes;
-    const ProfileBufferIndex mEntryStart;
-  };
-
-  // Main function to write entries.
-  // Reserve `aCallbackBytes()` bytes, call `aCallback()` with a pointer to an
-  // on-stack temporary EntryWriter (nullptr when out-of-session), and return
-  // whatever `aCallback` returns. Callback should not store `EntryWriter`,
-  // because it may become invalid after this thread-safe call.
-  // Note: `aCallbackBytes` is a callback instead of a simple value, to delay
-  // this potentially-expensive computation until after we're checked that we're
-  // in-session; use `Put(Length, Callback)` below if you know the size already.
-  template <typename CallbackBytes, typename Callback>
-  auto ReserveAndPut(CallbackBytes aCallbackBytes, Callback&& aCallback) {
-    {  // Locked block.
-      baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-      if (MOZ_LIKELY(mMaybeUnderlyingBuffer)) {
-        const Length entryBytes = std::forward<CallbackBytes>(aCallbackBytes)();
-        const Length bufferBytes =
-            mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value();
-        MOZ_RELEASE_ASSERT(
-            entryBytes <= bufferBytes - BufferWriter::ULEB128Size(entryBytes),
-            "Entry would wrap and overwrite itself");
-        // COmpute block size from the requested entry size.
-        const Length blockBytes =
-            EntryWriter::BlockSizeForEntrySize(entryBytes);
-        // We will put this new block at the end of the current buffer.
-        const ProfileBufferBlockIndex blockIndex = mNextWriteIndex;
-        // Compute the end of this new block...
-        const ProfileBufferIndex blockEnd =
-            blockIndex.ConvertToProfileBufferIndex() + blockBytes;
-        // ... which is where the following block will go.
-        mNextWriteIndex =
-            ProfileBufferBlockIndex::CreateFromProfileBufferIndex(blockEnd);
-        while (blockEnd >
-               mFirstReadIndex.ConvertToProfileBufferIndex() + bufferBytes) {
-          // About to trample on an old block.
-          EntryReader reader = ReaderInBlockAt(mFirstReadIndex);
-          mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
-          MOZ_ASSERT(reader.CurrentIndex() <=
-                     reader.NextBlockIndex().ConvertToProfileBufferIndex());
-          // Move the buffer reading start past this cleared block.
-          mFirstReadIndex = reader.NextBlockIndex();
-        }
-        mMaybeUnderlyingBuffer->mPushedBlockCount += 1;
-        // Finally, let aCallback write into the entry.
-        EntryWriter entryWriter(*this, blockIndex, entryBytes);
-        return std::forward<Callback>(aCallback)(&entryWriter);
-      }
-    }  // End of locked block.
-    // Out-of-session, just invoke the callback with nullptr, no need to hold
-    // the lock.
-    return std::forward<Callback>(aCallback)(nullptr);
-  }
-
-  // Add a new entry of known size, call `aCallback` with a pointer to a
-  // temporary EntryWriter (can be null when out-of-session), and return
-  // whatever `aCallback` returns. Callback should not store the `EntryWriter`,
-  // as it may become invalid after this thread-safe call.
-  template <typename Callback>
-  auto Put(Length aBytes, Callback&& aCallback) {
-    return ReserveAndPut([aBytes]() { return aBytes; },
-                         std::forward<Callback>(aCallback));
-  }
-
-  // Add a new entry copied from the given buffer, return block index.
-  ProfileBufferBlockIndex PutFrom(const void* aSrc, Length aBytes) {
-    return ReserveAndPut([aBytes]() { return aBytes; },
-                         [&](EntryWriter* aEntryWriter) {
-                           if (MOZ_LIKELY(aEntryWriter)) {
-                             aEntryWriter->Write(aSrc, aBytes);
-                             return aEntryWriter->CurrentBlockIndex();
-                           }
-                           // Out-of-session, return "empty" index.
-                           return ProfileBufferBlockIndex{};
-                         });
-  }
-
-  // Add a new single entry with *all* given object (using a Serializer for
-  // each), return block index.
-  template <typename... Ts>
-  ProfileBufferBlockIndex PutObjects(const Ts&... aTs) {
-    static_assert(sizeof...(Ts) > 0,
-                  "PutObjects must be given at least one object.");
-    return ReserveAndPut([&]() { return SumBytes(aTs...); },
-                         [&](EntryWriter* aEntryWriter) {
-                           if (MOZ_LIKELY(aEntryWriter)) {
-                             aEntryWriter->WriteObjects(aTs...);
-                             return aEntryWriter->CurrentBlockIndex();
-                           }
-                           // Out-of-session, return "empty" index.
-                           return ProfileBufferBlockIndex{};
-                         });
-  }
-
-  // Add a new entry copied from the given object, return block index.
-  template <typename T>
-  ProfileBufferBlockIndex PutObject(const T& aOb) {
-    return PutObjects(aOb);
-  }
-
-  // Append the contents of another BlocksRingBuffer to this one.
-  ProfileBufferBlockIndex AppendContents(const BlocksRingBuffer& aSrc) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-
-    if (MOZ_UNLIKELY(!mMaybeUnderlyingBuffer)) {
-      // We are out-of-session, could not append contents.
-      return ProfileBufferBlockIndex{};
-    }
-
-    baseprofiler::detail::BaseProfilerMaybeAutoLock srcLock(aSrc.mMutex);
-
-    if (MOZ_UNLIKELY(!aSrc.mMaybeUnderlyingBuffer)) {
-      // The other BRB is out-of-session, nothing to copy, we're done.
-      return ProfileBufferBlockIndex{};
-    }
-
-    const ProfileBufferIndex srcStartIndex =
-        aSrc.mFirstReadIndex.ConvertToProfileBufferIndex();
-    const ProfileBufferIndex srcEndIndex =
-        aSrc.mNextWriteIndex.ConvertToProfileBufferIndex();
-    const Length bytesToCopy = static_cast<Length>(srcEndIndex - srcStartIndex);
-
-    if (MOZ_UNLIKELY(bytesToCopy == 0)) {
-      // The other BRB is empty, nothing to copy, we're done.
-      return ProfileBufferBlockIndex{};
-    }
-
-    const Length bufferBytes =
-        mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value();
-
-    MOZ_RELEASE_ASSERT(bytesToCopy <= bufferBytes,
-                       "Entry would wrap and overwrite itself");
-
-    // We will put all copied blocks at the end of the current buffer.
-    const ProfileBufferIndex dstStartIndex =
-        mNextWriteIndex.ConvertToProfileBufferIndex();
-    // Compute where the copy will end...
-    const ProfileBufferIndex dstEndIndex = dstStartIndex + bytesToCopy;
-    // ... which is where the following block will go.
-    mNextWriteIndex =
-        ProfileBufferBlockIndex::CreateFromProfileBufferIndex(dstEndIndex);
-
-    while (dstEndIndex >
-           mFirstReadIndex.ConvertToProfileBufferIndex() + bufferBytes) {
-      // About to trample on an old block.
-      EntryReader reader = ReaderInBlockAt(mFirstReadIndex);
-      mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
-      MOZ_ASSERT(reader.CurrentIndex() <=
-                 reader.NextBlockIndex().ConvertToProfileBufferIndex());
-      // Move the buffer reading start past this cleared block.
-      mFirstReadIndex = reader.NextBlockIndex();
-    }
-
-    // Update our pushed count with the number of live blocks we are copying.
-    mMaybeUnderlyingBuffer->mPushedBlockCount +=
-        aSrc.mMaybeUnderlyingBuffer->mPushedBlockCount -
-        aSrc.mMaybeUnderlyingBuffer->mClearedBlockCount;
-
-    const auto readerEnd =
-        aSrc.mMaybeUnderlyingBuffer->mBuffer.ReaderAt(srcEndIndex);
-    auto writer = mMaybeUnderlyingBuffer->mBuffer.WriterAt(dstStartIndex);
-    // Copy all the bytes. TODO: Optimize with memcpy's?
-    for (auto reader =
-             aSrc.mMaybeUnderlyingBuffer->mBuffer.ReaderAt(srcStartIndex);
-         reader != readerEnd; ++reader, ++writer) {
-      *writer = *reader;
-    }
-    MOZ_ASSERT(writer == mMaybeUnderlyingBuffer->mBuffer.WriterAt(
-                             mNextWriteIndex.ConvertToProfileBufferIndex()));
-
-    return ProfileBufferBlockIndex::CreateFromProfileBufferIndex(dstStartIndex);
-  }
-
-  // Clear all entries: Move read index to the end so that these entries cannot
-  // be read anymore.
-  void Clear() {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    ClearAllEntries();
-  }
-
-  // Clear all entries strictly before aBlockIndex, and move read index to the
-  // end so that these entries cannot be read anymore.
-  void ClearBefore(ProfileBufferBlockIndex aBlockIndex) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    if (!mMaybeUnderlyingBuffer) {
-      return;
-    }
-    // Don't accept a not-yet-written index. One-past-the-end is ok.
-    MOZ_ASSERT(aBlockIndex <= mNextWriteIndex);
-    if (aBlockIndex <= mFirstReadIndex) {
-      // Already cleared.
-      return;
-    }
-    if (aBlockIndex == mNextWriteIndex) {
-      // Right past the end, just clear everything.
-      ClearAllEntries();
-      return;
-    }
-    // Otherwise we need to clear a subset of entries.
-    AssertBlockIndexIsValid(aBlockIndex);
-    // Just count skipped entries.
-    Reader reader(*this);
-    BlockIterator it = reader.begin();
-    for (; it.CurrentBlockIndex() < aBlockIndex; ++it) {
-      MOZ_ASSERT(it.CurrentBlockIndex() < reader.end().CurrentBlockIndex());
-      mMaybeUnderlyingBuffer->mClearedBlockCount += 1;
-    }
-    MOZ_ASSERT(it.CurrentBlockIndex() == aBlockIndex);
-    // Move read index to given index, so there's effectively no more entries
-    // before.
-    mFirstReadIndex = aBlockIndex;
-  }
-
-#ifdef DEBUG
-  void Dump() const {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(mMutex);
-    if (!mMaybeUnderlyingBuffer) {
-      printf("empty BlocksRingBuffer\n");
-      return;
-    }
-    using ULL = unsigned long long;
-    printf("start=%llu (%llu) end=%llu (%llu) - ",
-           ULL(mFirstReadIndex.ConvertToProfileBufferIndex()),
-           ULL(mFirstReadIndex.ConvertToProfileBufferIndex() &
-               (mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() - 1)),
-           ULL(mNextWriteIndex.ConvertToProfileBufferIndex()),
-           ULL(mNextWriteIndex.ConvertToProfileBufferIndex() &
-               (mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() - 1)));
-    mMaybeUnderlyingBuffer->mBuffer.Dump();
-  }
-#endif  // DEBUG
-
- private:
-  // In DEBUG mode, assert that `aBlockIndex` is a valid index for a live block.
-  // (Not just in range, but points exactly at the start of a block.)
-  // Slow, so avoid it for internal checks; this is more to check what callers
-  // provide us.
-  void AssertBlockIndexIsValid(ProfileBufferBlockIndex aBlockIndex) const {
-#ifdef DEBUG
-    mMutex.AssertCurrentThreadOwns();
-    MOZ_ASSERT(aBlockIndex >= mFirstReadIndex);
-    MOZ_ASSERT(aBlockIndex < mNextWriteIndex);
-    // Quick check (default), or slow check (change '1' to '0') below:
-#  if 1
-    // Quick check that this looks like a valid block start.
-    // Read the entry size at the start of the block.
-    BufferReader br = mMaybeUnderlyingBuffer->mBuffer.ReaderAt(
-        aBlockIndex.ConvertToProfileBufferIndex());
-    Length entryBytes = br.ReadULEB128<Length>();
-    MOZ_ASSERT(entryBytes > 0, "Empty entries are not allowed");
-    MOZ_ASSERT(
-        entryBytes < mMaybeUnderlyingBuffer->mBuffer.BufferLength().Value() -
-                         BufferReader::ULEB128Size(entryBytes),
-        "Entry would wrap and overwrite itself");
-    // The end of the block should be inside the live buffer range.
-    MOZ_ASSERT(aBlockIndex.ConvertToProfileBufferIndex() +
-                   BufferReader::ULEB128Size(entryBytes) + entryBytes <=
-               mNextWriteIndex.ConvertToProfileBufferIndex());
-#  else
-    // Slow check that the index is really the start of the block.
-    // This kills performances, as it reads from the first index until
-    // aBlockIndex. Only use to debug issues locally.
-    Reader reader(*this);
-    BlockIterator it = reader.begin();
-    for (; it.CurrentBlockIndex() < aBlockIndex; ++it) {
-      MOZ_ASSERT(it.CurrentBlockIndex() < reader.end().CurrentBlockIndex());
-    }
-    MOZ_ASSERT(it.CurrentBlockIndex() == aBlockIndex);
-#  endif
-#endif  // DEBUG
-  }
-
-  // In DEBUG mode, assert that `aBlockIndex` is a valid index for a live block,
-  // or is just past-the-end. (Not just in range, but points exactly at the
-  // start of a block.) Slow, so avoid it for internal checks; this is more to
-  // check what callers provide us.
-  void AssertBlockIndexIsValidOrEnd(ProfileBufferBlockIndex aBlockIndex) const {
-#ifdef DEBUG
-    mMutex.AssertCurrentThreadOwns();
-    if (aBlockIndex == mNextWriteIndex) {
-      return;
-    }
-    AssertBlockIndexIsValid(aBlockIndex);
-#endif  // DEBUG
-  }
-
-  // Create a reader for the block starting at aBlockIndex.
-  EntryReader ReaderInBlockAt(ProfileBufferBlockIndex aBlockIndex) const {
-    mMutex.AssertCurrentThreadOwns();
-    MOZ_ASSERT(aBlockIndex >= mFirstReadIndex);
-    MOZ_ASSERT(aBlockIndex < mNextWriteIndex);
-    return EntryReader(*this, aBlockIndex);
-  }
-
-  // Clear all entries: Move read index to the end so that these entries cannot
-  // be read anymore.
-  void ClearAllEntries() {
-    mMutex.AssertCurrentThreadOwns();
-    if (!mMaybeUnderlyingBuffer) {
-      return;
-    }
-    // Mark all entries pushed so far as cleared.
-    mMaybeUnderlyingBuffer->mClearedBlockCount =
-        mMaybeUnderlyingBuffer->mPushedBlockCount;
-    // Move read index to write index, so there's effectively no more entries
-    // that can be read. (Not setting both to 0, in case user is keeping
-    // `ProfileBufferBlockIndex`'es to old entries.)
-    mFirstReadIndex = mNextWriteIndex;
-  }
-
-  // If there is an underlying buffer, clear all entries, and discard the
-  // buffer. This BlocksRingBuffer will now gracefully reject all API calls, and
-  // is in a state where a new underlying buffer may be set.
-  void ResetUnderlyingBuffer() {
-    mMutex.AssertCurrentThreadOwns();
-    if (!mMaybeUnderlyingBuffer) {
-      return;
-    }
-    ClearAllEntries();
-    mMaybeUnderlyingBuffer.reset();
-  }
-
-  // Used to de/serialize a BlocksRingBuffer (e.g., containing a backtrace).
-  friend struct Serializer<BlocksRingBuffer>;
-  friend struct Deserializer<BlocksRingBuffer>;
-  friend struct Serializer<UniquePtr<BlocksRingBuffer>>;
-  friend struct Deserializer<UniquePtr<BlocksRingBuffer>>;
-
-  // Mutex guarding the following members.
-  mutable baseprofiler::detail::BaseProfilerMaybeMutex mMutex;
-
-  struct UnderlyingBuffer {
-    // Create a buffer of the given length.
-    explicit UnderlyingBuffer(PowerOfTwo<Length> aLength) : mBuffer(aLength) {
-      MOZ_ASSERT(aLength.Value() > ULEB128MaxSize<Length>(),
-                 "Buffer should be able to contain more than a block size");
-    }
-
-    // Take ownership of an existing buffer.
-    UnderlyingBuffer(UniquePtr<Buffer::Byte[]> aExistingBuffer,
-                     PowerOfTwo<Length> aLength)
-        : mBuffer(std::move(aExistingBuffer), aLength) {
-      MOZ_ASSERT(aLength.Value() > ULEB128MaxSize<Length>(),
-                 "Buffer should be able to contain more than a block size");
-    }
-
-    // Use an externally-owned buffer.
-    UnderlyingBuffer(Buffer::Byte* aExternalBuffer, PowerOfTwo<Length> aLength)
-        : mBuffer(aExternalBuffer, aLength) {
-      MOZ_ASSERT(aLength.Value() > ULEB128MaxSize<Length>(),
-                 "Buffer should be able to contain more than a block size");
-    }
-
-    // Only allow move-construction.
-    UnderlyingBuffer(UnderlyingBuffer&&) = default;
-
-    // Copies and move-assignment are explictly disallowed.
-    UnderlyingBuffer(const UnderlyingBuffer&) = delete;
-    UnderlyingBuffer& operator=(const UnderlyingBuffer&) = delete;
-    UnderlyingBuffer& operator=(UnderlyingBuffer&&) = delete;
-
-    // Underlying circular byte buffer.
-    Buffer mBuffer;
-
-    // Statistics.
-    uint64_t mPushedBlockCount = 0;
-    uint64_t mClearedBlockCount = 0;
-  };
-
-  // Underlying buffer, with stats.
-  // Only valid during in-session period.
-  Maybe<UnderlyingBuffer> mMaybeUnderlyingBuffer;
-
-  // Index to the first block to be read (or cleared). Initialized to 1 because
-  // 0 is reserved for the "empty" ProfileBufferBlockIndex value. Kept between
-  // sessions, so that stored indices from one session will be gracefully denied
-  // in future sessions.
-  ProfileBufferBlockIndex mFirstReadIndex =
-      ProfileBufferBlockIndex::CreateFromProfileBufferIndex(1);
-  // Index where the next new block should be allocated. Initialized to 1
-  // because 0 is reserved for the "empty" ProfileBufferBlockIndex value. Kept
-  // between sessions, so that stored indices from one session will be
-  // gracefully denied in future sessions.
-  ProfileBufferBlockIndex mNextWriteIndex =
-      ProfileBufferBlockIndex::CreateFromProfileBufferIndex(1);
 };
 
 // ============================================================================
 // Serializer and Deserializer ready-to-use specializations.
 
 // ----------------------------------------------------------------------------
 // Trivially-copyable types (default)
 
 // The default implementation works for all trivially-copyable types (e.g.,
 // PODs).
 //
 // Usage: `aEW.WriteObject(123);`.
 //
 // Raw pointers, though trivially-copyable, are explictly forbidden when writing
 // (to avoid unexpected leaks/UAFs), instead use one of
-// `WrapBlocksRingBufferLiteralCStringPointer`,
-// `WrapBlocksRingBufferUnownedCString`, or `WrapBlocksRingBufferRawPointer` as
-// needed.
+// `WrapProfileBufferLiteralCStringPointer`, `WrapProfileBufferUnownedCString`,
+// or `WrapProfileBufferRawPointer` as needed.
 template <typename T>
-struct BlocksRingBuffer::Serializer {
+struct ProfileBufferEntryWriter::Serializer {
   static_assert(std::is_trivially_copyable<T>::value,
                 "Serializer only works with trivially-copyable types by "
                 "default, use/add specialization for other types.");
 
   static constexpr Length Bytes(const T&) { return sizeof(T); }
 
-  static void Write(EntryWriter& aEW, const T& aT) {
+  static void Write(ProfileBufferEntryWriter& aEW, const T& aT) {
     static_assert(!std::is_pointer<T>::value,
                   "Serializer won't write raw pointers by default, use "
-                  "WrapBlocksRingBufferRawPointer or other.");
-    aEW.Write(&aT, sizeof(T));
+                  "WrapProfileBufferRawPointer or other.");
+    aEW.WriteBytes(&aT, sizeof(T));
   }
 };
 
 // Usage: `aER.ReadObject<int>();` or `int x; aER.ReadIntoObject(x);`.
 template <typename T>
-struct BlocksRingBuffer::Deserializer {
+struct ProfileBufferEntryReader::Deserializer {
   static_assert(std::is_trivially_copyable<T>::value,
                 "Deserializer only works with trivially-copyable types by "
                 "default, use/add specialization for other types.");
 
-  static void ReadInto(EntryReader& aER, T& aT) { aER.Read(&aT, sizeof(T)); }
+  static void ReadInto(ProfileBufferEntryReader& aER, T& aT) {
+    aER.ReadBytes(&aT, sizeof(T));
+  }
 
-  static T Read(EntryReader& aER) {
+  static T Read(ProfileBufferEntryReader& aER) {
     // Note that this creates a default `T` first, and then overwrites it with
     // bytes from the buffer. Trivially-copyable types support this without UB.
     T ob;
     ReadInto(aER, ob);
     return ob;
   }
 };
 
 // ----------------------------------------------------------------------------
 // Strip const/volatile/reference from types.
 
 // Automatically strip `const`.
 template <typename T>
-struct BlocksRingBuffer::Serializer<const T>
-    : public BlocksRingBuffer::Serializer<T> {};
+struct ProfileBufferEntryWriter::Serializer<const T>
+    : public ProfileBufferEntryWriter::Serializer<T> {};
 
 template <typename T>
-struct BlocksRingBuffer::Deserializer<const T>
-    : public BlocksRingBuffer::Deserializer<T> {};
+struct ProfileBufferEntryReader::Deserializer<const T>
+    : public ProfileBufferEntryReader::Deserializer<T> {};
 
 // Automatically strip `volatile`.
 template <typename T>
-struct BlocksRingBuffer::Serializer<volatile T>
-    : public BlocksRingBuffer::Serializer<T> {};
+struct ProfileBufferEntryWriter::Serializer<volatile T>
+    : public ProfileBufferEntryWriter::Serializer<T> {};
 
 template <typename T>
-struct BlocksRingBuffer::Deserializer<volatile T>
-    : public BlocksRingBuffer::Deserializer<T> {};
+struct ProfileBufferEntryReader::Deserializer<volatile T>
+    : public ProfileBufferEntryReader::Deserializer<T> {};
 
 // Automatically strip `lvalue-reference`.
 template <typename T>
-struct BlocksRingBuffer::Serializer<T&>
-    : public BlocksRingBuffer::Serializer<T> {};
+struct ProfileBufferEntryWriter::Serializer<T&>
+    : public ProfileBufferEntryWriter::Serializer<T> {};
 
 template <typename T>
-struct BlocksRingBuffer::Deserializer<T&>
-    : public BlocksRingBuffer::Deserializer<T> {};
+struct ProfileBufferEntryReader::Deserializer<T&>
+    : public ProfileBufferEntryReader::Deserializer<T> {};
 
 // Automatically strip `rvalue-reference`.
 template <typename T>
-struct BlocksRingBuffer::Serializer<T&&>
-    : public BlocksRingBuffer::Serializer<T> {};
+struct ProfileBufferEntryWriter::Serializer<T&&>
+    : public ProfileBufferEntryWriter::Serializer<T> {};
 
 template <typename T>
-struct BlocksRingBuffer::Deserializer<T&&>
-    : public BlocksRingBuffer::Deserializer<T> {};
+struct ProfileBufferEntryReader::Deserializer<T&&>
+    : public ProfileBufferEntryReader::Deserializer<T> {};
 
 // ----------------------------------------------------------------------------
 // ProfileBufferBlockIndex
 
 // ProfileBufferBlockIndex, serialized as the underlying value.
 template <>
-struct BlocksRingBuffer::Serializer<ProfileBufferBlockIndex> {
+struct ProfileBufferEntryWriter::Serializer<ProfileBufferBlockIndex> {
   static constexpr Length Bytes(const ProfileBufferBlockIndex& aBlockIndex) {
     return sizeof(ProfileBufferBlockIndex);
   }
 
-  static void Write(EntryWriter& aEW,
+  static void Write(ProfileBufferEntryWriter& aEW,
                     const ProfileBufferBlockIndex& aBlockIndex) {
-    aEW.Write(&aBlockIndex, sizeof(aBlockIndex));
+    aEW.WriteBytes(&aBlockIndex, sizeof(aBlockIndex));
   }
 };
 
 template <>
-struct BlocksRingBuffer::Deserializer<ProfileBufferBlockIndex> {
-  static void ReadInto(EntryReader& aER, ProfileBufferBlockIndex& aBlockIndex) {
-    aER.Read(&aBlockIndex, sizeof(aBlockIndex));
+struct ProfileBufferEntryReader::Deserializer<ProfileBufferBlockIndex> {
+  static void ReadInto(ProfileBufferEntryReader& aER,
+                       ProfileBufferBlockIndex& aBlockIndex) {
+    aER.ReadBytes(&aBlockIndex, sizeof(aBlockIndex));
   }
 
-  static ProfileBufferBlockIndex Read(EntryReader& aER) {
+  static ProfileBufferBlockIndex Read(ProfileBufferEntryReader& aER) {
     ProfileBufferBlockIndex blockIndex;
     ReadInto(aER, blockIndex);
     return blockIndex;
   }
 };
 
 // ----------------------------------------------------------------------------
 // Literal C string pointer
 
 // Wrapper around a pointer to a literal C string.
-template <BlocksRingBuffer::Length NonTerminalCharacters>
-struct BlocksRingBufferLiteralCStringPointer {
+template <size_t NonTerminalCharacters>
+struct ProfileBufferLiteralCStringPointer {
   const char* mCString;
 };
 
 // Wrap a pointer to a literal C string.
-template <BlocksRingBuffer::Length CharactersIncludingTerminal>
-BlocksRingBufferLiteralCStringPointer<CharactersIncludingTerminal - 1>
-WrapBlocksRingBufferLiteralCStringPointer(
+template <size_t CharactersIncludingTerminal>
+ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal - 1>
+WrapProfileBufferLiteralCStringPointer(
     const char (&aCString)[CharactersIncludingTerminal]) {
   return {aCString};
 }
 
 // Literal C strings, serialized as the raw pointer because it is unique and
 // valid for the whole program lifetime.
 //
-// Usage: `aEW.WriteObject(WrapBlocksRingBufferLiteralCStringPointer("hi"));`.
+// Usage: `aEW.WriteObject(WrapProfileBufferLiteralCStringPointer("hi"));`.
 //
 // No deserializer is provided for this type, instead it must be deserialized as
 // a raw pointer: `aER.ReadObject<const char*>();`
-template <BlocksRingBuffer::Length CharactersIncludingTerminal>
-struct BlocksRingBuffer::Deserializer<
-    BlocksRingBufferLiteralCStringPointer<CharactersIncludingTerminal>> {
-  static constexpr Length Bytes(const BlocksRingBufferLiteralCStringPointer<
-                                CharactersIncludingTerminal>&) {
+template <size_t CharactersIncludingTerminal>
+struct ProfileBufferEntryReader::Deserializer<
+    ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>> {
+  static constexpr Length Bytes(
+      const ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>&) {
     // We're only storing a pointer, its size is independent from the pointer
     // value.
     return sizeof(const char*);
   }
 
   static void Write(
-      EntryWriter& aEW,
-      const BlocksRingBufferLiteralCStringPointer<CharactersIncludingTerminal>&
+      ProfileBufferEntryWriter& aEW,
+      const ProfileBufferLiteralCStringPointer<CharactersIncludingTerminal>&
           aWrapper) {
     // Write the pointer *value*, not the string contents.
-    aEW.Write(&aWrapper.mCString, sizeof(aWrapper.mCString));
+    aEW.WriteBytes(aWrapper.mCString, sizeof(aWrapper.mCString));
   }
 };
 
 // ----------------------------------------------------------------------------
 // C string contents
 
 // Wrapper around a pointer to a C string whose contents will be serialized.
-struct BlocksRingBufferUnownedCString {
+struct ProfileBufferUnownedCString {
   const char* mCString;
 };
 
 // Wrap a pointer to a C string whose contents will be serialized.
-inline BlocksRingBufferUnownedCString WrapBlocksRingBufferUnownedCString(
+inline ProfileBufferUnownedCString WrapProfileBufferUnownedCString(
     const char* aCString) {
   return {aCString};
 }
 
 // The contents of a (probably) unowned C string are serialized as the number of
 // characters (encoded as ULEB128) and all the characters in the string. The
 // terminal '\0' is omitted.
 //
-// Usage: `aEW.WriteObject(WrapBlocksRingBufferUnownedCString(str.c_str()))`.
+// Usage: `aEW.WriteObject(WrapProfileBufferUnownedCString(str.c_str()))`.
 //
 // No deserializer is provided for this pointer type, instead it must be
 // deserialized as one of the other string types that manages its contents,
 // e.g.: `aER.ReadObject<std::string>();`
 template <>
-struct BlocksRingBuffer::Serializer<BlocksRingBufferUnownedCString> {
-  static Length Bytes(const BlocksRingBufferUnownedCString& aS) {
-    const auto len = static_cast<Length>(strlen(aS.mCString));
-    return EntryWriter::ULEB128Size(len) + len;
+struct ProfileBufferEntryWriter::Serializer<ProfileBufferUnownedCString> {
+  static Length Bytes(const ProfileBufferUnownedCString& aS) {
+    const auto len = strlen(aS.mCString);
+    return ULEB128Size(len) + len;
   }
 
-  static void Write(EntryWriter& aEW,
-                    const BlocksRingBufferUnownedCString& aS) {
-    const auto len = static_cast<Length>(strlen(aS.mCString));
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const ProfileBufferUnownedCString& aS) {
+    const auto len = strlen(aS.mCString);
     aEW.WriteULEB128(len);
-    aEW.Write(aS.mCString, len);
+    aEW.WriteBytes(aS.mCString, len);
   }
 };
 
 // ----------------------------------------------------------------------------
 // Raw pointers
 
 // Wrapper around a pointer to be serialized as the raw pointer value.
 template <typename T>
-struct BlocksRingBufferRawPointer {
+struct ProfileBufferRawPointer {
   T* mRawPointer;
 };
 
 // Wrap a pointer to be serialized as the raw pointer value.
 template <typename T>
-BlocksRingBufferRawPointer<T> WrapBlocksRingBufferRawPointer(T* aRawPointer) {
+ProfileBufferRawPointer<T> WrapProfileBufferRawPointer(T* aRawPointer) {
   return {aRawPointer};
 }
 
 // Raw pointers are serialized as the raw pointer value.
 //
-// Usage: `aEW.WriteObject(WrapBlocksRingBufferRawPointer(ptr));`
+// Usage: `aEW.WriteObject(WrapProfileBufferRawPointer(ptr));`
 //
 // The wrapper is compulsory when writing pointers (to avoid unexpected
 // leaks/UAFs), but reading can be done straight into a raw pointer object,
 // e.g.: `aER.ReadObject<Foo*>;`.
 template <typename T>
-struct BlocksRingBuffer::Serializer<BlocksRingBufferRawPointer<T>> {
+struct ProfileBufferEntryWriter::Serializer<ProfileBufferRawPointer<T>> {
   template <typename U>
   static constexpr Length Bytes(const U&) {
     return sizeof(T*);
   }
 
-  static void Write(EntryWriter& aEW,
-                    const BlocksRingBufferRawPointer<T>& aWrapper) {
-    aEW.Write(&aWrapper.mRawPointer, sizeof(aWrapper.mRawPointer));
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const ProfileBufferRawPointer<T>& aWrapper) {
+    aEW.WriteBytes(&aWrapper.mRawPointer, sizeof(aWrapper.mRawPointer));
   }
 };
 
 // Usage: `aER.ReadObject<Foo*>;` or `Foo* p; aER.ReadIntoObject(p);`, no
 // wrapper necessary.
 template <typename T>
-struct BlocksRingBuffer::Deserializer<BlocksRingBufferRawPointer<T>> {
-  static void ReadInto(EntryReader& aER, T*& aPtr) {
-    aER.Read(&aPtr, sizeof(aPtr));
+struct ProfileBufferEntryReader::Deserializer<ProfileBufferRawPointer<T>> {
+  static void ReadInto(ProfileBufferEntryReader& aER, T*& aPtr) {
+    aER.ReadBytes(&aPtr, sizeof(aPtr));
   }
 
-  static T* Read(EntryReader& aER) {
+  static T* Read(ProfileBufferEntryReader& aER) {
     T* ptr;
     ReadInto(aER, ptr);
     return ptr;
   }
 };
 
 // ----------------------------------------------------------------------------
 // std::string contents
 
 // std::string contents are serialized as the number of characters (encoded as
 // ULEB128) and all the characters in the string. The terminal '\0' is omitted.
 //
 // Usage: `std::string s = ...; aEW.WriteObject(s);`
 template <>
-struct BlocksRingBuffer::Serializer<std::string> {
+struct ProfileBufferEntryWriter::Serializer<std::string> {
   static Length Bytes(const std::string& aS) {
-    const auto len = aS.length();
-    return EntryWriter::ULEB128Size(len) + static_cast<Length>(len);
+    const Length len = static_cast<Length>(aS.length());
+    return ULEB128Size(len) + len;
   }
 
-  static void Write(EntryWriter& aEW, const std::string& aS) {
-    const auto len = aS.length();
+  static void Write(ProfileBufferEntryWriter& aEW, const std::string& aS) {
+    const Length len = static_cast<Length>(aS.length());
     aEW.WriteULEB128(len);
-    aEW.Write(aS.c_str(), len);
+    aEW.WriteBytes(aS.c_str(), len);
   }
 };
 
 // Usage: `std::string s = aEW.ReadObject<std::string>(s);` or
 // `std::string s; aER.ReadIntoObject(s);`
 template <>
-struct BlocksRingBuffer::Deserializer<std::string> {
-  static void ReadInto(EntryReader& aER, std::string& aS) {
+struct ProfileBufferEntryReader::Deserializer<std::string> {
+  static void ReadInto(ProfileBufferEntryReader& aER, std::string& aS) {
     const auto len = aER.ReadULEB128<std::string::size_type>();
     // Assign to `aS` by using iterators.
     // (`aER+0` so we get the same iterator type as `aER+len`.)
-    aS.assign(aER + 0, aER + len);
+    aS.assign(aER, aER.EmptyIteratorAtOffset(len));
     aER += len;
   }
 
-  static std::string Read(EntryReader& aER) {
+  static std::string Read(ProfileBufferEntryReader& aER) {
     const auto len = aER.ReadULEB128<std::string::size_type>();
     // Construct a string by using iterators.
     // (`aER+0` so we get the same iterator type as `aER+len`.)
-    std::string s(aER + 0, aER + len);
+    std::string s(aER, aER.EmptyIteratorAtOffset(len));
     aER += len;
     return s;
   }
 };
 
 // ----------------------------------------------------------------------------
 // mozilla::UniqueFreePtr<CHAR>
 
@@ -1393,59 +363,58 @@ struct BlocksRingBuffer::Deserializer<st
 // null terminator is omitted.
 // `CHAR` can be any type that has a specialization for
 // `std::char_traits<CHAR>::length(const CHAR*)`.
 //
 // Note: A nullptr pointer will be serialized like an empty string, so when
 // deserializing it will result in an allocated buffer only containing a
 // single null terminator.
 template <typename CHAR>
-struct BlocksRingBuffer::Serializer<UniqueFreePtr<CHAR>> {
+struct ProfileBufferEntryWriter::Serializer<UniqueFreePtr<CHAR>> {
   static Length Bytes(const UniqueFreePtr<CHAR>& aS) {
     if (!aS) {
       // Null pointer, store it as if it was an empty string (so: 0 bytes).
-      return EntryWriter::ULEB128Size(0u);
+      return ULEB128Size(0u);
     }
     // Note that we store the size in *bytes*, not in number of characters.
-    const auto bytes = static_cast<Length>(
-        std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR));
-    return EntryWriter::ULEB128Size(bytes) + bytes;
+    const auto bytes = std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR);
+    return ULEB128Size(bytes) + bytes;
   }
 
-  static void Write(EntryWriter& aEW, const UniqueFreePtr<CHAR>& aS) {
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const UniqueFreePtr<CHAR>& aS) {
     if (!aS) {
       // Null pointer, store it as if it was an empty string (so we write a
       // length of 0 bytes).
       aEW.WriteULEB128(0u);
       return;
     }
     // Note that we store the size in *bytes*, not in number of characters.
-    const auto bytes = static_cast<Length>(
-        std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR));
+    const auto bytes = std::char_traits<CHAR>::length(aS.get()) * sizeof(CHAR);
     aEW.WriteULEB128(bytes);
-    aEW.Write(aS.get(), bytes);
+    aEW.WriteBytes(aS.get(), bytes);
   }
 };
 
 template <typename CHAR>
-struct BlocksRingBuffer::Deserializer<UniqueFreePtr<CHAR>> {
-  static void ReadInto(EntryReader& aER, UniqueFreePtr<CHAR>& aS) {
+struct ProfileBufferEntryReader::Deserializer<UniqueFreePtr<CHAR>> {
+  static void ReadInto(ProfileBufferEntryReader& aER, UniqueFreePtr<CHAR>& aS) {
     aS = Read(aER);
   }
 
-  static UniqueFreePtr<CHAR> Read(EntryReader& aER) {
+  static UniqueFreePtr<CHAR> Read(ProfileBufferEntryReader& aER) {
     // Read the number of *bytes* that follow.
-    const auto bytes = aER.ReadULEB128<Length>();
+    const auto bytes = aER.ReadULEB128<size_t>();
     // We need a buffer of the non-const character type.
     using NC_CHAR = std::remove_const_t<CHAR>;
     // We allocate the required number of bytes, plus one extra character for
     // the null terminator.
     NC_CHAR* buffer = static_cast<NC_CHAR*>(malloc(bytes + sizeof(NC_CHAR)));
     // Copy the characters into the buffer.
-    aER.Read(buffer, bytes);
+    aER.ReadBytes(buffer, bytes);
     // And append a null terminator.
     buffer[bytes / sizeof(NC_CHAR)] = NC_CHAR(0);
     return UniqueFreePtr<CHAR>(buffer);
   }
 };
 
 // ----------------------------------------------------------------------------
 // std::tuple
@@ -1460,17 +429,17 @@ struct BlocksRingBuffer::Deserializer<Un
 // aEW.WriteObject(/* int */ std::get<0>(is), /* string */ std::get<1>(is));
 // ...
 // // Reading back can be done directly into a tuple:
 // auto is = aER.ReadObject<std::tuple<int, std::string>>();
 // // Or each item could be read separately:
 // auto i = aER.ReadObject<int>(); auto s = aER.ReadObject<std::string>();
 // ```
 template <typename... Ts>
-struct BlocksRingBuffer::Serializer<std::tuple<Ts...>> {
+struct ProfileBufferEntryWriter::Serializer<std::tuple<Ts...>> {
  private:
   template <size_t... Is>
   static Length TupleBytes(const std::tuple<Ts...>& aTuple,
                            std::index_sequence<Is...>) {
     Length bytes = 0;
     // This is pre-C++17 fold trick, using `initializer_list` to unpack the `Is`
     // variadic pack, and the comma operator to:
     // (work on each tuple item, generate an int for the list). Note that the
@@ -1485,42 +454,45 @@ struct BlocksRingBuffer::Serializer<std:
     // See https://articles.emptycrate.com/2016/05/14/folds_in_cpp11_ish.html
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (bytes += SumBytes(std::get<Is>(aTuple)), 0)...};
     return bytes;
   }
 
   template <size_t... Is>
-  static void TupleWrite(EntryWriter& aEW, const std::tuple<Ts...>& aTuple,
+  static void TupleWrite(ProfileBufferEntryWriter& aEW,
+                         const std::tuple<Ts...>& aTuple,
                          std::index_sequence<Is...>) {
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (aEW.WriteObject(std::get<Is>(aTuple)), 0)...};
   }
 
  public:
   static Length Bytes(const std::tuple<Ts...>& aTuple) {
     // Generate a 0..N-1 index pack, we'll add the sizes of each item.
     return TupleBytes(aTuple, std::index_sequence_for<Ts...>());
   }
 
-  static void Write(EntryWriter& aEW, const std::tuple<Ts...>& aTuple) {
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const std::tuple<Ts...>& aTuple) {
     // Generate a 0..N-1 index pack, we'll write each item.
     TupleWrite(aEW, aTuple, std::index_sequence_for<Ts...>());
   }
 };
 
 template <typename... Ts>
-struct BlocksRingBuffer::Deserializer<std::tuple<Ts...>> {
-  static void ReadInto(EntryReader& aER, std::tuple<Ts...>& aTuple) {
-    aER.Read(&aTuple, Bytes(aTuple));
+struct ProfileBufferEntryReader::Deserializer<std::tuple<Ts...>> {
+  static void ReadInto(ProfileBufferEntryReader& aER,
+                       std::tuple<Ts...>& aTuple) {
+    aER.ReadBytes(&aTuple, Bytes(aTuple));
   }
 
-  static std::tuple<Ts...> Read(EntryReader& aER) {
+  static std::tuple<Ts...> Read(ProfileBufferEntryReader& aER) {
     // Note that this creates default `Ts` first, and then overwrites them.
     std::tuple<Ts...> ob;
     ReadInto(aER, ob);
     return ob;
   }
 };
 
 // ----------------------------------------------------------------------------
@@ -1537,143 +509,144 @@ struct BlocksRingBuffer::Deserializer<st
 // aEW.WriteObject(/* int */ std::get<0>(is), /* string */ std::get<1>(is));
 // ...
 // // Reading back can be done directly into a Tuple:
 // auto is = aER.ReadObject<Tuple<int, std::string>>();
 // // Or each item could be read separately:
 // auto i = aER.ReadObject<int>(); auto s = aER.ReadObject<std::string>();
 // ```
 template <typename... Ts>
-struct BlocksRingBuffer::Serializer<Tuple<Ts...>> {
+struct ProfileBufferEntryWriter::Serializer<Tuple<Ts...>> {
  private:
   template <size_t... Is>
   static Length TupleBytes(const Tuple<Ts...>& aTuple,
                            std::index_sequence<Is...>) {
     Length bytes = 0;
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (bytes += SumBytes(Get<Is>(aTuple)), 0)...};
     return bytes;
   }
 
   template <size_t... Is>
-  static void TupleWrite(EntryWriter& aEW, const Tuple<Ts...>& aTuple,
+  static void TupleWrite(ProfileBufferEntryWriter& aEW,
+                         const Tuple<Ts...>& aTuple,
                          std::index_sequence<Is...>) {
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (aEW.WriteObject(Get<Is>(aTuple)), 0)...};
   }
 
  public:
   static Length Bytes(const Tuple<Ts...>& aTuple) {
     // Generate a 0..N-1 index pack, we'll add the sizes of each item.
     return TupleBytes(aTuple, std::index_sequence_for<Ts...>());
   }
 
-  static void Write(EntryWriter& aEW, const Tuple<Ts...>& aTuple) {
+  static void Write(ProfileBufferEntryWriter& aEW, const Tuple<Ts...>& aTuple) {
     // Generate a 0..N-1 index pack, we'll write each item.
     TupleWrite(aEW, aTuple, std::index_sequence_for<Ts...>());
   }
 };
 
 template <typename... Ts>
-struct BlocksRingBuffer::Deserializer<Tuple<Ts...>> {
-  static void ReadInto(EntryReader& aER, Tuple<Ts...>& aTuple) {
-    aER.Read(&aTuple, Bytes(aTuple));
+struct ProfileBufferEntryReader::Deserializer<Tuple<Ts...>> {
+  static void ReadInto(ProfileBufferEntryReader& aER, Tuple<Ts...>& aTuple) {
+    aER.ReadBytes(&aTuple, Bytes(aTuple));
   }
 
-  static Tuple<Ts...> Read(EntryReader& aER) {
+  static Tuple<Ts...> Read(ProfileBufferEntryReader& aER) {
     // Note that this creates default `Ts` first, and then overwrites them.
     Tuple<Ts...> ob;
     ReadInto(aER, ob);
     return ob;
   }
 };
 
 // ----------------------------------------------------------------------------
 // mozilla::Span
 
 // Span. All elements are serialized in sequence.
 // The caller is assumed to know the number of elements (they may manually
 // write&read it before the span if needed).
 // Similar to tuples, reading/writing spans is equivalent to reading/writing
 // their elements in order.
 template <class T, size_t N>
-struct BlocksRingBuffer::Serializer<Span<T, N>> {
+struct ProfileBufferEntryWriter::Serializer<Span<T, N>> {
   static Length Bytes(const Span<T, N>& aSpan) {
     Length bytes = 0;
     for (const T& element : aSpan) {
       bytes += SumBytes(element);
     }
     return bytes;
   }
 
-  static void Write(EntryWriter& aEW, const Span<T, N>& aSpan) {
+  static void Write(ProfileBufferEntryWriter& aEW, const Span<T, N>& aSpan) {
     for (const T& element : aSpan) {
       aEW.WriteObject(element);
     }
   }
 };
 
 template <class T, size_t N>
-struct BlocksRingBuffer::Deserializer<Span<T, N>> {
+struct ProfileBufferEntryReader::Deserializer<Span<T, N>> {
   // Read elements back into span pointing at a pre-allocated buffer.
-  static void ReadInto(EntryReader& aER, Span<T, N>& aSpan) {
+  static void ReadInto(ProfileBufferEntryReader& aER, Span<T, N>& aSpan) {
     for (T& element : aSpan) {
       aER.ReadIntoObject(element);
     }
   }
 
   // A Span does not own its data, this would probably leak so we forbid this.
-  static Span<T, N> Read(EntryReader& aER) = delete;
+  static Span<T, N> Read(ProfileBufferEntryReader& aER) = delete;
 };
 
 // ----------------------------------------------------------------------------
 // mozilla::Maybe
 
 // Maybe<T> is serialized as one byte containing either 'm' (Nothing),
 // or 'M' followed by the recursively-serialized `T` object.
 template <typename T>
-struct BlocksRingBuffer::Serializer<Maybe<T>> {
+struct ProfileBufferEntryWriter::Serializer<Maybe<T>> {
   static Length Bytes(const Maybe<T>& aMaybe) {
     // 1 byte to store nothing/something flag, then object size if present.
     return aMaybe.isNothing() ? 1 : (1 + SumBytes(aMaybe.ref()));
   }
 
-  static void Write(EntryWriter& aEW, const Maybe<T>& aMaybe) {
+  static void Write(ProfileBufferEntryWriter& aEW, const Maybe<T>& aMaybe) {
     // 'm'/'M' is just an arbitrary 1-byte value to distinguish states.
     if (aMaybe.isNothing()) {
       aEW.WriteObject<char>('m');
     } else {
       aEW.WriteObject<char>('M');
       // Use the Serializer for the contained type.
       aEW.WriteObject(aMaybe.ref());
     }
   }
 };
 
 template <typename T>
-struct BlocksRingBuffer::Deserializer<Maybe<T>> {
-  static void ReadInto(EntryReader& aER, Maybe<T>& aMaybe) {
+struct ProfileBufferEntryReader::Deserializer<Maybe<T>> {
+  static void ReadInto(ProfileBufferEntryReader& aER, Maybe<T>& aMaybe) {
     char c = aER.ReadObject<char>();
     if (c == 'm') {
       aMaybe.reset();
     } else {
       MOZ_ASSERT(c == 'M');
       // If aMaybe is empty, create a default `T` first, to be overwritten.
       // Otherwise we'll just overwrite whatever was already there.
       if (aMaybe.isNothing()) {
         aMaybe.emplace();
       }
       // Use the Deserializer for the contained type.
       aER.ReadIntoObject(aMaybe.ref());
     }
   }
 
-  static Maybe<T> Read(EntryReader& aER) {
+  static Maybe<T> Read(ProfileBufferEntryReader& aER) {
     Maybe<T> maybe;
     char c = aER.ReadObject<char>();
     MOZ_ASSERT(c == 'M' || c == 'm');
     if (c == 'M') {
       // Note that this creates a default `T` inside the Maybe first, and then
       // overwrites it.
       maybe = Some(T{});
       // Use the Deserializer for the contained type.
@@ -1684,26 +657,26 @@ struct BlocksRingBuffer::Deserializer<Ma
 };
 
 // ----------------------------------------------------------------------------
 // mozilla::Variant
 
 // Variant is serialized as the tag (0-based index of the stored type, encoded
 // as ULEB128), and the recursively-serialized object.
 template <typename... Ts>
-struct BlocksRingBuffer::Serializer<Variant<Ts...>> {
+struct ProfileBufferEntryWriter::Serializer<Variant<Ts...>> {
  private:
   // Called from the fold expression in `VariantBytes()`, only the selected
   // variant will write into `aOutBytes`.
   template <size_t I>
   static void VariantIBytes(const Variant<Ts...>& aVariantTs,
                             Length& aOutBytes) {
     if (aVariantTs.template is<I>()) {
-      aOutBytes =
-          EntryReader::ULEB128Size(I) + SumBytes(aVariantTs.template as<I>());
+      aOutBytes = ProfileBufferEntryWriter::ULEB128Size(I) +
+                  SumBytes(aVariantTs.template as<I>());
     }
   }
 
   // Go through all variant tags, and let the selected one write the correct
   // number of bytes.
   template <size_t... Is>
   static Length VariantBytes(const Variant<Ts...>& aVariantTs,
                              std::index_sequence<Is...>) {
@@ -1713,246 +686,89 @@ struct BlocksRingBuffer::Serializer<Vari
         (VariantIBytes<Is>(aVariantTs, bytes), 0)...};
     MOZ_ASSERT(bytes != 0);
     return bytes;
   }
 
   // Called from the fold expression in `VariantWrite()`, only the selected
   // variant will serialize the tag and object.
   template <size_t I>
-  static void VariantIWrite(EntryWriter& aEW,
+  static void VariantIWrite(ProfileBufferEntryWriter& aEW,
                             const Variant<Ts...>& aVariantTs) {
     if (aVariantTs.template is<I>()) {
       aEW.WriteULEB128(I);
       // Use the Serializer for the contained type.
       aEW.WriteObject(aVariantTs.template as<I>());
     }
   }
 
   // Go through all variant tags, and let the selected one serialize the correct
   // tag and object.
   template <size_t... Is>
-  static void VariantWrite(EntryWriter& aEW, const Variant<Ts...>& aVariantTs,
+  static void VariantWrite(ProfileBufferEntryWriter& aEW,
+                           const Variant<Ts...>& aVariantTs,
                            std::index_sequence<Is...>) {
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (VariantIWrite<Is>(aEW, aVariantTs), 0)...};
   }
 
  public:
   static Length Bytes(const Variant<Ts...>& aVariantTs) {
     // Generate a 0..N-1 index pack, the selected variant will return its size.
     return VariantBytes(aVariantTs, std::index_sequence_for<Ts...>());
   }
 
-  static void Write(EntryWriter& aEW, const Variant<Ts...>& aVariantTs) {
+  static void Write(ProfileBufferEntryWriter& aEW,
+                    const Variant<Ts...>& aVariantTs) {
     // Generate a 0..N-1 index pack, the selected variant will serialize itself.
     VariantWrite(aEW, aVariantTs, std::index_sequence_for<Ts...>());
   }
 };
 
 template <typename... Ts>
-struct BlocksRingBuffer::Deserializer<Variant<Ts...>> {
+struct ProfileBufferEntryReader::Deserializer<Variant<Ts...>> {
  private:
   // Called from the fold expression in `VariantReadInto()`, only the selected
   // variant will deserialize the object.
   template <size_t I>
-  static void VariantIReadInto(EntryReader& aER, Variant<Ts...>& aVariantTs,
-                               unsigned aTag) {
+  static void VariantIReadInto(ProfileBufferEntryReader& aER,
+                               Variant<Ts...>& aVariantTs, unsigned aTag) {
     if (I == aTag) {
       // Ensure the variant contains the target type. Note that this may create
       // a default object.
       if (!aVariantTs.template is<I>()) {
         aVariantTs = Variant<Ts...>(VariantIndex<I>{});
       }
       aER.ReadIntoObject(aVariantTs.template as<I>());
     }
   }
 
   template <size_t... Is>
-  static void VariantReadInto(EntryReader& aER, Variant<Ts...>& aVariantTs,
+  static void VariantReadInto(ProfileBufferEntryReader& aER,
+                              Variant<Ts...>& aVariantTs,
                               std::index_sequence<Is...>) {
     unsigned tag = aER.ReadULEB128<unsigned>();
     // TODO: Replace with C++17 fold.
     Unused << std::initializer_list<int>{
         (VariantIReadInto<Is>(aER, aVariantTs, tag), 0)...};
   }
 
  public:
-  static void ReadInto(EntryReader& aER, Variant<Ts...>& aVariantTs) {
+  static void ReadInto(ProfileBufferEntryReader& aER,
+                       Variant<Ts...>& aVariantTs) {
     // Generate a 0..N-1 index pack, the selected variant will deserialize
     // itself.
     VariantReadInto(aER, aVariantTs, std::index_sequence_for<Ts...>());
   }
 
-  static Variant<Ts...> Read(EntryReader& aER) {
+  static Variant<Ts...> Read(ProfileBufferEntryReader& aER) {
     // Note that this creates a default `Variant` of the first type, and then
     // overwrites it. Consider using `ReadInto` for more control if needed.
     Variant<Ts...> variant(VariantIndex<0>{});
     ReadInto(aER, variant);
     return variant;
   }
 };
 
-// ----------------------------------------------------------------------------
-// BlocksRingBuffer
-
-// A BlocksRingBuffer can hide another one!
-// This will be used to store marker backtraces; They can be read back into a
-// UniquePtr<BlocksRingBuffer>.
-// Format: len (ULEB128) | start | end | buffer (len bytes) | pushed | cleared
-// len==0 marks an out-of-session buffer, or empty buffer.
-template <>
-struct BlocksRingBuffer::Serializer<BlocksRingBuffer> {
-  static Length Bytes(const BlocksRingBuffer& aBuffer) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(aBuffer.mMutex);
-    if (aBuffer.mMaybeUnderlyingBuffer.isNothing()) {
-      // Out-of-session, we only need 1 byte to store a length of 0.
-      return ULEB128Size<Length>(0);
-    }
-    const auto start = aBuffer.mFirstReadIndex.ConvertToProfileBufferIndex();
-    const auto end = aBuffer.mNextWriteIndex.ConvertToProfileBufferIndex();
-    const auto len = end - start;
-    if (len == 0) {
-      // In-session but empty, also store a length of 0.
-      return ULEB128Size<Length>(0);
-    }
-    return ULEB128Size(len) + sizeof(start) + sizeof(end) + len +
-           sizeof(aBuffer.mMaybeUnderlyingBuffer->mPushedBlockCount) +
-           sizeof(aBuffer.mMaybeUnderlyingBuffer->mClearedBlockCount);
-  }
-
-  static void Write(EntryWriter& aEW, const BlocksRingBuffer& aBuffer) {
-    baseprofiler::detail::BaseProfilerMaybeAutoLock lock(aBuffer.mMutex);
-    if (aBuffer.mMaybeUnderlyingBuffer.isNothing()) {
-      // Out-of-session, only store a length of 0.
-      aEW.WriteULEB128<Length>(0);
-      return;
-    }
-    const auto start = aBuffer.mFirstReadIndex.ConvertToProfileBufferIndex();
-    const auto end = aBuffer.mNextWriteIndex.ConvertToProfileBufferIndex();
-    MOZ_ASSERT(end - start <= std::numeric_limits<Length>::max());
-    const auto len = static_cast<Length>(end - start);
-    if (len == 0) {
-      // In-session but empty, only store a length of 0.
-      aEW.WriteULEB128<Length>(0);
-      return;
-    }
-    // In-session.
-    // Store buffer length, start and end indices.
-    aEW.WriteULEB128<Length>(len);
-    aEW.WriteObject(start);
-    aEW.WriteObject(end);
-    // Write all the bytes.
-    aBuffer.mMaybeUnderlyingBuffer->mBuffer.ReaderAt(start).ReadInto(aEW, len);
-    // And write stats.
-    aEW.WriteObject(aBuffer.mMaybeUnderlyingBuffer->mPushedBlockCount);
-    aEW.WriteObject(aBuffer.mMaybeUnderlyingBuffer->mClearedBlockCount);
-  }
-};
-
-// A serialized BlocksRingBuffer can be read into an empty buffer (either
-// out-of-session, or in-session with enough room).
-template <>
-struct BlocksRingBuffer::Deserializer<BlocksRingBuffer> {
-  static void ReadInto(EntryReader& aER, BlocksRingBuffer& aBuffer) {
-    // Expect an empty buffer, as we're going to overwrite it.
-    MOZ_ASSERT(aBuffer.GetState().mRangeStart == aBuffer.GetState().mRangeEnd);
-    // Read the stored buffer length.
-    const auto len = aER.ReadULEB128<BlocksRingBuffer::Length>();
-    if (len == 0) {
-      // 0-length means an "uninteresting" buffer, just return now.
-      return;
-    }
-    // We have a non-empty buffer to read.
-    if (aBuffer.BufferLength().isSome()) {
-      // Output buffer is in-session (i.e., it already has a memory buffer
-      // attached). Make sure the caller allocated enough space.
-      MOZ_RELEASE_ASSERT(aBuffer.BufferLength()->Value() >= len);
-    } else {
-      // Output buffer is out-of-session, attach a new memory buffer.
-      aBuffer.Set(PowerOfTwo<BlocksRingBuffer::Length>(len));
-      MOZ_ASSERT(aBuffer.BufferLength()->Value() >= len);
-    }
-    // Read start and end indices.
-    const auto start = aER.ReadObject<ProfileBufferIndex>();
-    aBuffer.mFirstReadIndex =
-        ProfileBufferBlockIndex::CreateFromProfileBufferIndex(start);
-    const auto end = aER.ReadObject<ProfileBufferIndex>();
-    aBuffer.mNextWriteIndex =
-        ProfileBufferBlockIndex::CreateFromProfileBufferIndex(end);
-    MOZ_ASSERT(end - start == len);
-    // Copy bytes into the buffer.
-    auto writer = aBuffer.mMaybeUnderlyingBuffer->mBuffer.WriterAt(start);
-    aER.ReadInto(writer, len);
-    MOZ_ASSERT(writer.CurrentIndex() == end);
-    // Finally copy stats.
-    aBuffer.mMaybeUnderlyingBuffer->mPushedBlockCount = aER.ReadObject<decltype(
-        aBuffer.mMaybeUnderlyingBuffer->mPushedBlockCount)>();
-    aBuffer.mMaybeUnderlyingBuffer->mClearedBlockCount =
-        aER.ReadObject<decltype(
-            aBuffer.mMaybeUnderlyingBuffer->mClearedBlockCount)>();
-  }
-
-  // We cannot output a BlocksRingBuffer object (not copyable), use `ReadInto()`
-  // or `aER.ReadObject<UniquePtr<BlocksRinbBuffer>>()` instead.
-  static BlocksRingBuffer Read(BlocksRingBuffer::EntryReader& aER) = delete;
-};
-
-// A BlocksRingBuffer is usually refererenced through a UniquePtr, for
-// convenience we support (de)serializing that UniquePtr directly.
-// This is compatible with the non-UniquePtr serialization above, with a null
-// pointer being treated like an out-of-session or empty buffer; and any of
-// these would be deserialized into a null pointer.
-template <>
-struct BlocksRingBuffer::Serializer<UniquePtr<BlocksRingBuffer>> {
-  static Length Bytes(const UniquePtr<BlocksRingBuffer>& aBufferUPtr) {
-    if (!aBufferUPtr) {
-      // Null pointer, treat it like an empty buffer, i.e., write length of 0.
-      return ULEB128Size<Length>(0);
-    }
-    // Otherwise write the pointed-at BlocksRingBuffer (which could be
-    // out-of-session or empty.)
-    return SumBytes(*aBufferUPtr);
-  }
-
-  static void Write(EntryWriter& aEW,
-                    const UniquePtr<BlocksRingBuffer>& aBufferUPtr) {
-    if (!aBufferUPtr) {
-      // Null pointer, treat it like an empty buffer, i.e., write length of 0.
-      aEW.WriteULEB128<Length>(0);
-      return;
-    }
-    // Otherwise write the pointed-at BlocksRingBuffer (which could be
-    // out-of-session or empty.)
-    aEW.WriteObject(*aBufferUPtr);
-  }
-};
-
-template <>
-struct BlocksRingBuffer::Deserializer<UniquePtr<BlocksRingBuffer>> {
-  static void ReadInto(EntryReader& aER, UniquePtr<BlocksRingBuffer>& aBuffer) {
-    aBuffer = Read(aER);
-  }
-
-  static UniquePtr<BlocksRingBuffer> Read(BlocksRingBuffer::EntryReader& aER) {
-    UniquePtr<BlocksRingBuffer> bufferUPtr;
-    // Read the stored buffer length.
-    const auto len = aER.ReadULEB128<BlocksRingBuffer::Length>();
-    if (len == 0) {
-      // 0-length means an "uninteresting" buffer, just return nullptr.
-      return bufferUPtr;
-    }
-    // We have a non-empty buffer.
-    // allocate an empty BlocksRingBuffer without mutex.
-    bufferUPtr = MakeUnique<BlocksRingBuffer>(
-        BlocksRingBuffer::ThreadSafety::WithoutMutex);
-    // Rewind the reader before the length and deserialize the contents, using
-    // the non-UniquePtr Deserializer.
-    aER -= ULEB128Size(len);
-    aER.ReadIntoObject(*bufferUPtr);
-    return bufferUPtr;
-  }
-};
-
 }  // namespace mozilla
 
-#endif  // BlocksRingBuffer_h
+#endif  // ProfileBufferEntrySerialization_h