// Copyright 2000 The RE2 Authors. All Rights Reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. #include "util/util.h" namespace re2 { // ---------------------------------------------------------------------- // UnsafeArena::UnsafeArena() // UnsafeArena::~UnsafeArena() // Destroying the arena automatically calls Reset() // ---------------------------------------------------------------------- UnsafeArena::UnsafeArena(const size_t block_size) : block_size_(block_size), freestart_(NULL), // set for real in Reset() last_alloc_(NULL), remaining_(0), blocks_alloced_(1), overflow_blocks_(NULL) { assert(block_size > kDefaultAlignment); first_blocks_[0].mem = reinterpret_cast<char*>(malloc(block_size_)); first_blocks_[0].size = block_size_; Reset(); } UnsafeArena::~UnsafeArena() { FreeBlocks(); assert(overflow_blocks_ == NULL); // FreeBlocks() should do that // The first X blocks stay allocated always by default. Delete them now. for (int i = 0; i < blocks_alloced_; i++) free(first_blocks_[i].mem); } // ---------------------------------------------------------------------- // UnsafeArena::Reset() // Clears all the memory an arena is using. // ---------------------------------------------------------------------- void UnsafeArena::Reset() { FreeBlocks(); freestart_ = first_blocks_[0].mem; remaining_ = first_blocks_[0].size; last_alloc_ = NULL; // We do not know for sure whether or not the first block is aligned, // so we fix that right now. const int overage = reinterpret_cast<uintptr_t>(freestart_) & (kDefaultAlignment-1); if (overage > 0) { const int waste = kDefaultAlignment - overage; freestart_ += waste; remaining_ -= waste; } freestart_when_empty_ = freestart_; assert(!(reinterpret_cast<uintptr_t>(freestart_)&(kDefaultAlignment-1))); } // ------------------------------------------------------------- // UnsafeArena::AllocNewBlock() // Adds and returns an AllocatedBlock. // The returned AllocatedBlock* is valid until the next call // to AllocNewBlock or Reset. (i.e. anything that might // affect overflow_blocks_). // ------------------------------------------------------------- UnsafeArena::AllocatedBlock* UnsafeArena::AllocNewBlock(const size_t block_size) { AllocatedBlock *block; // Find the next block. if ( blocks_alloced_ < arraysize(first_blocks_) ) { // Use one of the pre-allocated blocks block = &first_blocks_[blocks_alloced_++]; } else { // oops, out of space, move to the vector if (overflow_blocks_ == NULL) overflow_blocks_ = new vector<AllocatedBlock>; // Adds another block to the vector. overflow_blocks_->resize(overflow_blocks_->size()+1); // block points to the last block of the vector. block = &overflow_blocks_->back(); } block->mem = reinterpret_cast<char*>(malloc(block_size)); block->size = block_size; return block; } // ---------------------------------------------------------------------- // UnsafeArena::GetMemoryFallback() // We take memory out of our pool, aligned on the byte boundary // requested. If we don't have space in our current pool, we // allocate a new block (wasting the remaining space in the // current block) and give you that. If your memory needs are // too big for a single block, we make a special your-memory-only // allocation -- this is equivalent to not using the arena at all. // ---------------------------------------------------------------------- void* UnsafeArena::GetMemoryFallback(const size_t size, const int align) { if (size == 0) return NULL; // stl/stl_alloc.h says this is okay assert(align > 0 && 0 == (align & (align - 1))); // must be power of 2 // If the object is more than a quarter of the block size, allocate // it separately to avoid wasting too much space in leftover bytes if (block_size_ == 0 || size > block_size_/4) { // then it gets its own block in the arena assert(align <= kDefaultAlignment); // because that's what new gives us // This block stays separate from the rest of the world; in particular // we don't update last_alloc_ so you can't reclaim space on this block. return AllocNewBlock(size)->mem; } const int overage = (reinterpret_cast<uintptr_t>(freestart_) & (align-1)); if (overage) { const int waste = align - overage; freestart_ += waste; if (waste < remaining_) { remaining_ -= waste; } else { remaining_ = 0; } } if (size > remaining_) { AllocatedBlock *block = AllocNewBlock(block_size_); freestart_ = block->mem; remaining_ = block->size; } remaining_ -= size; last_alloc_ = freestart_; freestart_ += size; assert((reinterpret_cast<uintptr_t>(last_alloc_) & (align-1)) == 0); return reinterpret_cast<void*>(last_alloc_); } // ---------------------------------------------------------------------- // UnsafeArena::FreeBlocks() // Unlike GetMemory(), which does actual work, ReturnMemory() is a // no-op: we don't "free" memory until Reset() is called. We do // update some stats, though. Note we do no checking that the // pointer you pass in was actually allocated by us, or that it // was allocated for the size you say, so be careful here! // FreeBlocks() does the work for Reset(), actually freeing all // memory allocated in one fell swoop. // ---------------------------------------------------------------------- void UnsafeArena::FreeBlocks() { for ( int i = 1; i < blocks_alloced_; ++i ) { // keep first block alloced free(first_blocks_[i].mem); first_blocks_[i].mem = NULL; first_blocks_[i].size = 0; } blocks_alloced_ = 1; if (overflow_blocks_ != NULL) { vector<AllocatedBlock>::iterator it; for (it = overflow_blocks_->begin(); it != overflow_blocks_->end(); ++it) { free(it->mem); } delete overflow_blocks_; // These should be used very rarely overflow_blocks_ = NULL; } } } // namespace re2