summaryrefslogtreecommitdiffstats
path: root/layout/base/StackArena.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--layout/base/StackArena.cpp172
1 files changed, 172 insertions, 0 deletions
diff --git a/layout/base/StackArena.cpp b/layout/base/StackArena.cpp
new file mode 100644
index 0000000000..3c4717ef02
--- /dev/null
+++ b/layout/base/StackArena.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 8; 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/. */
+
+#include "StackArena.h"
+#include "nsAlgorithm.h"
+#include "nsDebug.h"
+
+namespace mozilla {
+
+// A block of memory that the stack will chop up and hand out.
+struct StackBlock {
+ // Subtract sizeof(StackBlock*) to give space for the |mNext| field.
+ static const size_t MAX_USABLE_SIZE = 4096 - sizeof(StackBlock*);
+
+ // A block of memory.
+ char mBlock[MAX_USABLE_SIZE];
+
+ // Another block of memory that would only be created if our stack
+ // overflowed.
+ StackBlock* mNext;
+
+ StackBlock() : mNext(nullptr) {}
+ ~StackBlock() = default;
+};
+
+static_assert(sizeof(StackBlock) == 4096, "StackBlock must be 4096 bytes");
+
+// We hold an array of marks. A push pushes a mark on the stack.
+// A pop pops it off.
+struct StackMark {
+ // The block of memory from which we are currently handing out chunks.
+ StackBlock* mBlock;
+
+ // Our current position in the block.
+ size_t mPos;
+};
+
+StackArena* AutoStackArena::gStackArena;
+
+StackArena::StackArena() {
+ mMarkLength = 0;
+ mMarks = nullptr;
+
+ // Allocate our stack memory.
+ mBlocks = new StackBlock();
+ mCurBlock = mBlocks;
+
+ mStackTop = 0;
+ mPos = 0;
+}
+
+StackArena::~StackArena() {
+ // Free up our data.
+ delete[] mMarks;
+ while (mBlocks) {
+ StackBlock* toDelete = mBlocks;
+ mBlocks = mBlocks->mNext;
+ delete toDelete;
+ }
+}
+
+size_t StackArena::SizeOfExcludingThis(
+ mozilla::MallocSizeOf aMallocSizeOf) const {
+ size_t n = 0;
+ StackBlock* block = mBlocks;
+ while (block) {
+ n += aMallocSizeOf(block);
+ block = block->mNext;
+ }
+ n += aMallocSizeOf(mMarks);
+ return n;
+}
+
+static const int STACK_ARENA_MARK_INCREMENT = 50;
+
+void StackArena::Push() {
+ // Resize the mark array if we overrun it. Failure to allocate the
+ // mark array is not fatal; we just won't free to that mark. This
+ // allows callers not to worry about error checking.
+ if (mStackTop >= mMarkLength) {
+ uint32_t newLength = mStackTop + STACK_ARENA_MARK_INCREMENT;
+ StackMark* newMarks = new StackMark[newLength];
+ if (newMarks) {
+ if (mMarkLength) {
+ memcpy(newMarks, mMarks, sizeof(StackMark) * mMarkLength);
+ }
+ // Fill in any marks that we couldn't allocate during a prior call
+ // to Push().
+ for (; mMarkLength < mStackTop; ++mMarkLength) {
+ MOZ_ASSERT_UNREACHABLE("should only hit this on out-of-memory");
+ newMarks[mMarkLength].mBlock = mCurBlock;
+ newMarks[mMarkLength].mPos = mPos;
+ }
+ delete[] mMarks;
+ mMarks = newMarks;
+ mMarkLength = newLength;
+ }
+ }
+
+ // Set a mark at the top (if we can).
+ NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
+ if (mStackTop < mMarkLength) {
+ mMarks[mStackTop].mBlock = mCurBlock;
+ mMarks[mStackTop].mPos = mPos;
+ }
+
+ mStackTop++;
+}
+
+void* StackArena::Allocate(size_t aSize) {
+ NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
+
+ // Align to a multiple of 8.
+ aSize = NS_ROUNDUP<size_t>(aSize, 8);
+
+ // On stack overflow, grab another block.
+ if (mPos + aSize >= StackBlock::MAX_USABLE_SIZE) {
+ NS_ASSERTION(aSize <= StackBlock::MAX_USABLE_SIZE,
+ "Requested memory is greater that our block size!!");
+ if (mCurBlock->mNext == nullptr) {
+ mCurBlock->mNext = new StackBlock();
+ }
+
+ mCurBlock = mCurBlock->mNext;
+ mPos = 0;
+ }
+
+ // Return the chunk they need.
+ void* result = mCurBlock->mBlock + mPos;
+ mPos += aSize;
+
+ return result;
+}
+
+void StackArena::Pop() {
+ // Pop off the mark.
+ NS_ASSERTION(mStackTop > 0, "unmatched pop");
+ mStackTop--;
+
+ if (mStackTop >= mMarkLength) {
+ // We couldn't allocate the marks array at the time of the push, so
+ // we don't know where we're freeing to.
+ MOZ_ASSERT_UNREACHABLE("out of memory");
+ if (mStackTop == 0) {
+ // But we do know if we've completely pushed the stack.
+ mCurBlock = mBlocks;
+ mPos = 0;
+ }
+ return;
+ }
+
+#ifdef DEBUG
+ // Mark the "freed" memory with 0xdd to help with debugging of memory
+ // allocation problems.
+ {
+ StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
+ size_t pos = mMarks[mStackTop].mPos;
+ for (; block != block_end; block = block->mNext, pos = 0) {
+ memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
+ }
+ memset(block->mBlock + pos, 0xdd, mPos - pos);
+ }
+#endif
+
+ mCurBlock = mMarks[mStackTop].mBlock;
+ mPos = mMarks[mStackTop].mPos;
+}
+
+} // namespace mozilla