diff options
Diffstat (limited to 'third_party/re2/util/arena.cc')
-rw-r--r-- | third_party/re2/util/arena.cc | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/third_party/re2/util/arena.cc b/third_party/re2/util/arena.cc new file mode 100644 index 0000000..25753c5 --- /dev/null +++ b/third_party/re2/util/arena.cc @@ -0,0 +1,168 @@ +// 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 |