Home | History | Annotate | Download | only in util
      1 // Copyright 2000 The RE2 Authors.  All Rights Reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 #include "util/util.h"
      6 
      7 namespace re2 {
      8 
      9 // ----------------------------------------------------------------------
     10 // UnsafeArena::UnsafeArena()
     11 // UnsafeArena::~UnsafeArena()
     12 //    Destroying the arena automatically calls Reset()
     13 // ----------------------------------------------------------------------
     14 
     15 
     16 UnsafeArena::UnsafeArena(const size_t block_size)
     17   : block_size_(block_size),
     18     freestart_(NULL),                   // set for real in Reset()
     19     last_alloc_(NULL),
     20     remaining_(0),
     21     blocks_alloced_(1),
     22     overflow_blocks_(NULL) {
     23   assert(block_size > kDefaultAlignment);
     24 
     25   first_blocks_[0].mem = reinterpret_cast<char*>(malloc(block_size_));
     26   first_blocks_[0].size = block_size_;
     27 
     28   Reset();
     29 }
     30 
     31 UnsafeArena::~UnsafeArena() {
     32   FreeBlocks();
     33   assert(overflow_blocks_ == NULL);    // FreeBlocks() should do that
     34   // The first X blocks stay allocated always by default.  Delete them now.
     35   for (int i = 0; i < blocks_alloced_; i++)
     36     free(first_blocks_[i].mem);
     37 }
     38 
     39 // ----------------------------------------------------------------------
     40 // UnsafeArena::Reset()
     41 //    Clears all the memory an arena is using.
     42 // ----------------------------------------------------------------------
     43 
     44 void UnsafeArena::Reset() {
     45   FreeBlocks();
     46   freestart_ = first_blocks_[0].mem;
     47   remaining_ = first_blocks_[0].size;
     48   last_alloc_ = NULL;
     49 
     50   // We do not know for sure whether or not the first block is aligned,
     51   // so we fix that right now.
     52   const int overage = reinterpret_cast<uintptr_t>(freestart_) &
     53                       (kDefaultAlignment-1);
     54   if (overage > 0) {
     55     const int waste = kDefaultAlignment - overage;
     56     freestart_ += waste;
     57     remaining_ -= waste;
     58   }
     59   freestart_when_empty_ = freestart_;
     60   assert(!(reinterpret_cast<uintptr_t>(freestart_)&(kDefaultAlignment-1)));
     61 }
     62 
     63 // -------------------------------------------------------------
     64 // UnsafeArena::AllocNewBlock()
     65 //    Adds and returns an AllocatedBlock.
     66 //    The returned AllocatedBlock* is valid until the next call
     67 //    to AllocNewBlock or Reset.  (i.e. anything that might
     68 //    affect overflow_blocks_).
     69 // -------------------------------------------------------------
     70 
     71 UnsafeArena::AllocatedBlock* UnsafeArena::AllocNewBlock(const size_t block_size) {
     72   AllocatedBlock *block;
     73   // Find the next block.
     74   if ( blocks_alloced_ < arraysize(first_blocks_) ) {
     75     // Use one of the pre-allocated blocks
     76     block = &first_blocks_[blocks_alloced_++];
     77   } else {                   // oops, out of space, move to the vector
     78     if (overflow_blocks_ == NULL) overflow_blocks_ = new vector<AllocatedBlock>;
     79     // Adds another block to the vector.
     80     overflow_blocks_->resize(overflow_blocks_->size()+1);
     81     // block points to the last block of the vector.
     82     block = &overflow_blocks_->back();
     83   }
     84 
     85   block->mem = reinterpret_cast<char*>(malloc(block_size));
     86   block->size = block_size;
     87 
     88   return block;
     89 }
     90 
     91 // ----------------------------------------------------------------------
     92 // UnsafeArena::GetMemoryFallback()
     93 //    We take memory out of our pool, aligned on the byte boundary
     94 //    requested.  If we don't have space in our current pool, we
     95 //    allocate a new block (wasting the remaining space in the
     96 //    current block) and give you that.  If your memory needs are
     97 //    too big for a single block, we make a special your-memory-only
     98 //    allocation -- this is equivalent to not using the arena at all.
     99 // ----------------------------------------------------------------------
    100 
    101 void* UnsafeArena::GetMemoryFallback(const size_t size, const int align) {
    102   if (size == 0)
    103     return NULL;             // stl/stl_alloc.h says this is okay
    104 
    105   assert(align > 0 && 0 == (align & (align - 1)));  // must be power of 2
    106 
    107   // If the object is more than a quarter of the block size, allocate
    108   // it separately to avoid wasting too much space in leftover bytes
    109   if (block_size_ == 0 || size > block_size_/4) {
    110     // then it gets its own block in the arena
    111     assert(align <= kDefaultAlignment);   // because that's what new gives us
    112     // This block stays separate from the rest of the world; in particular
    113     // we don't update last_alloc_ so you can't reclaim space on this block.
    114     return AllocNewBlock(size)->mem;
    115   }
    116 
    117   const int overage =
    118     (reinterpret_cast<uintptr_t>(freestart_) & (align-1));
    119   if (overage) {
    120     const int waste = align - overage;
    121     freestart_ += waste;
    122     if (waste < remaining_) {
    123       remaining_ -= waste;
    124     } else {
    125       remaining_ = 0;
    126     }
    127   }
    128   if (size > remaining_) {
    129     AllocatedBlock *block = AllocNewBlock(block_size_);
    130     freestart_ = block->mem;
    131     remaining_ = block->size;
    132   }
    133   remaining_ -= size;
    134   last_alloc_ = freestart_;
    135   freestart_ += size;
    136   assert((reinterpret_cast<uintptr_t>(last_alloc_) & (align-1)) == 0);
    137   return reinterpret_cast<void*>(last_alloc_);
    138 }
    139 
    140 // ----------------------------------------------------------------------
    141 // UnsafeArena::FreeBlocks()
    142 //    Unlike GetMemory(), which does actual work, ReturnMemory() is a
    143 //    no-op: we don't "free" memory until Reset() is called.  We do
    144 //    update some stats, though.  Note we do no checking that the
    145 //    pointer you pass in was actually allocated by us, or that it
    146 //    was allocated for the size you say, so be careful here!
    147 //       FreeBlocks() does the work for Reset(), actually freeing all
    148 //    memory allocated in one fell swoop.
    149 // ----------------------------------------------------------------------
    150 
    151 void UnsafeArena::FreeBlocks() {
    152   for ( int i = 1; i < blocks_alloced_; ++i ) {  // keep first block alloced
    153     free(first_blocks_[i].mem);
    154     first_blocks_[i].mem = NULL;
    155     first_blocks_[i].size = 0;
    156   }
    157   blocks_alloced_ = 1;
    158   if (overflow_blocks_ != NULL) {
    159     vector<AllocatedBlock>::iterator it;
    160     for (it = overflow_blocks_->begin(); it != overflow_blocks_->end(); ++it) {
    161       free(it->mem);
    162     }
    163     delete overflow_blocks_;             // These should be used very rarely
    164     overflow_blocks_ = NULL;
    165   }
    166 }
    167 
    168 }  // namespace re2
    169