tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

StackArena.cpp (4733B)


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