layout/base/StackArena.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "StackArena.h"
michael@0 6 #include "nsAlgorithm.h"
michael@0 7 #include "nsDebug.h"
michael@0 8
michael@0 9 namespace mozilla {
michael@0 10
michael@0 11 // A block of memory that the stack will chop up and hand out.
michael@0 12 struct StackBlock {
michael@0 13 // Subtract sizeof(StackBlock*) to give space for the |mNext| field.
michael@0 14 static const size_t MAX_USABLE_SIZE = 4096 - sizeof(StackBlock*);
michael@0 15
michael@0 16 // A block of memory.
michael@0 17 char mBlock[MAX_USABLE_SIZE];
michael@0 18
michael@0 19 // Another block of memory that would only be created if our stack
michael@0 20 // overflowed.
michael@0 21 StackBlock* mNext;
michael@0 22
michael@0 23 StackBlock() : mNext(nullptr) { }
michael@0 24 ~StackBlock() { }
michael@0 25 };
michael@0 26
michael@0 27 static_assert(sizeof(StackBlock) == 4096, "StackBlock must be 4096 bytes");
michael@0 28
michael@0 29 // We hold an array of marks. A push pushes a mark on the stack.
michael@0 30 // A pop pops it off.
michael@0 31 struct StackMark {
michael@0 32 // The block of memory from which we are currently handing out chunks.
michael@0 33 StackBlock* mBlock;
michael@0 34
michael@0 35 // Our current position in the block.
michael@0 36 size_t mPos;
michael@0 37 };
michael@0 38
michael@0 39 StackArena* AutoStackArena::gStackArena;
michael@0 40
michael@0 41 StackArena::StackArena()
michael@0 42 {
michael@0 43 mMarkLength = 0;
michael@0 44 mMarks = nullptr;
michael@0 45
michael@0 46 // Allocate our stack memory.
michael@0 47 mBlocks = new StackBlock();
michael@0 48 mCurBlock = mBlocks;
michael@0 49
michael@0 50 mStackTop = 0;
michael@0 51 mPos = 0;
michael@0 52 }
michael@0 53
michael@0 54 StackArena::~StackArena()
michael@0 55 {
michael@0 56 // Free up our data.
michael@0 57 delete [] mMarks;
michael@0 58 while (mBlocks) {
michael@0 59 StackBlock* toDelete = mBlocks;
michael@0 60 mBlocks = mBlocks->mNext;
michael@0 61 delete toDelete;
michael@0 62 }
michael@0 63 }
michael@0 64
michael@0 65 size_t
michael@0 66 StackArena::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
michael@0 67 {
michael@0 68 size_t n = 0;
michael@0 69 StackBlock *block = mBlocks;
michael@0 70 while (block) {
michael@0 71 n += aMallocSizeOf(block);
michael@0 72 block = block->mNext;
michael@0 73 }
michael@0 74 n += aMallocSizeOf(mMarks);
michael@0 75 return n;
michael@0 76 }
michael@0 77
michael@0 78 static const int STACK_ARENA_MARK_INCREMENT = 50;
michael@0 79
michael@0 80 void
michael@0 81 StackArena::Push()
michael@0 82 {
michael@0 83 // Resize the mark array if we overrun it. Failure to allocate the
michael@0 84 // mark array is not fatal; we just won't free to that mark. This
michael@0 85 // allows callers not to worry about error checking.
michael@0 86 if (mStackTop >= mMarkLength) {
michael@0 87 uint32_t newLength = mStackTop + STACK_ARENA_MARK_INCREMENT;
michael@0 88 StackMark* newMarks = new StackMark[newLength];
michael@0 89 if (newMarks) {
michael@0 90 if (mMarkLength) {
michael@0 91 memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
michael@0 92 }
michael@0 93 // Fill in any marks that we couldn't allocate during a prior call
michael@0 94 // to Push().
michael@0 95 for (; mMarkLength < mStackTop; ++mMarkLength) {
michael@0 96 NS_NOTREACHED("should only hit this on out-of-memory");
michael@0 97 newMarks[mMarkLength].mBlock = mCurBlock;
michael@0 98 newMarks[mMarkLength].mPos = mPos;
michael@0 99 }
michael@0 100 delete [] mMarks;
michael@0 101 mMarks = newMarks;
michael@0 102 mMarkLength = newLength;
michael@0 103 }
michael@0 104 }
michael@0 105
michael@0 106 // Set a mark at the top (if we can).
michael@0 107 NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
michael@0 108 if (mStackTop < mMarkLength) {
michael@0 109 mMarks[mStackTop].mBlock = mCurBlock;
michael@0 110 mMarks[mStackTop].mPos = mPos;
michael@0 111 }
michael@0 112
michael@0 113 mStackTop++;
michael@0 114 }
michael@0 115
michael@0 116 void*
michael@0 117 StackArena::Allocate(size_t aSize)
michael@0 118 {
michael@0 119 NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
michael@0 120
michael@0 121 // Align to a multiple of 8.
michael@0 122 aSize = NS_ROUNDUP<size_t>(aSize, 8);
michael@0 123
michael@0 124 // On stack overflow, grab another block.
michael@0 125 if (mPos + aSize >= StackBlock::MAX_USABLE_SIZE) {
michael@0 126 NS_ASSERTION(aSize <= StackBlock::MAX_USABLE_SIZE,
michael@0 127 "Requested memory is greater that our block size!!");
michael@0 128 if (mCurBlock->mNext == nullptr) {
michael@0 129 mCurBlock->mNext = new StackBlock();
michael@0 130 }
michael@0 131
michael@0 132 mCurBlock = mCurBlock->mNext;
michael@0 133 mPos = 0;
michael@0 134 }
michael@0 135
michael@0 136 // Return the chunk they need.
michael@0 137 void *result = mCurBlock->mBlock + mPos;
michael@0 138 mPos += aSize;
michael@0 139
michael@0 140 return result;
michael@0 141 }
michael@0 142
michael@0 143 void
michael@0 144 StackArena::Pop()
michael@0 145 {
michael@0 146 // Pop off the mark.
michael@0 147 NS_ASSERTION(mStackTop > 0, "unmatched pop");
michael@0 148 mStackTop--;
michael@0 149
michael@0 150 if (mStackTop >= mMarkLength) {
michael@0 151 // We couldn't allocate the marks array at the time of the push, so
michael@0 152 // we don't know where we're freeing to.
michael@0 153 NS_NOTREACHED("out of memory");
michael@0 154 if (mStackTop == 0) {
michael@0 155 // But we do know if we've completely pushed the stack.
michael@0 156 mCurBlock = mBlocks;
michael@0 157 mPos = 0;
michael@0 158 }
michael@0 159 return;
michael@0 160 }
michael@0 161
michael@0 162 #ifdef DEBUG
michael@0 163 // Mark the "freed" memory with 0xdd to help with debugging of memory
michael@0 164 // allocation problems.
michael@0 165 {
michael@0 166 StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
michael@0 167 size_t pos = mMarks[mStackTop].mPos;
michael@0 168 for (; block != block_end; block = block->mNext, pos = 0) {
michael@0 169 memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
michael@0 170 }
michael@0 171 memset(block->mBlock + pos, 0xdd, mPos - pos);
michael@0 172 }
michael@0 173 #endif
michael@0 174
michael@0 175 mCurBlock = mMarks[mStackTop].mBlock;
michael@0 176 mPos = mMarks[mStackTop].mPos;
michael@0 177 }
michael@0 178
michael@0 179 } // namespace mozilla

mercurial