DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (d8847129d134)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
/* 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() { }
};

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) {
        NS_NOTREACHED("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.
    NS_NOTREACHED("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