// 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. // Sometimes it is necessary to allocate a large number of small // objects. Doing this the usual way (malloc, new) is slow, // especially for multithreaded programs. An UnsafeArena provides a // mark/release method of memory management: it asks for a large chunk // from the operating system and doles it out bit by bit as required. // Then you free all the memory at once by calling UnsafeArena::Reset(). // The "Unsafe" refers to the fact that UnsafeArena is not safe to // call from multiple threads. // // The global operator new that can be used as follows: // // #include "lib/arena-inl.h" // // UnsafeArena arena(1000); // Foo* foo = new (AllocateInArena, &arena) Foo; // #ifndef RE2_UTIL_ARENA_H_ #define RE2_UTIL_ARENA_H_ namespace re2 { // This class is thread-compatible. class UnsafeArena { public: UnsafeArena(const size_t block_size); virtual ~UnsafeArena(); void Reset(); // This should be the worst-case alignment for any type. This is // good for IA-32, SPARC version 7 (the last one I know), and // supposedly Alpha. i386 would be more time-efficient with a // default alignment of 8, but ::operator new() uses alignment of 4, // and an assertion will fail below after the call to MakeNewBlock() // if you try to use a larger alignment. #ifdef __i386__ static const int kDefaultAlignment = 4; #else static const int kDefaultAlignment = 8; #endif private: void* GetMemoryFallback(const size_t size, const int align); public: void* GetMemory(const size_t size, const int align) { if ( size > 0 && size < remaining_ && align == 1 ) { // common case last_alloc_ = freestart_; freestart_ += size; remaining_ -= size; return reinterpret_cast<void*>(last_alloc_); } return GetMemoryFallback(size, align); } private: struct AllocatedBlock { char *mem; size_t size; }; // The returned AllocatedBlock* is valid until the next call to AllocNewBlock // or Reset (i.e. anything that might affect overflow_blocks_). AllocatedBlock *AllocNewBlock(const size_t block_size); const AllocatedBlock *IndexToBlock(int index) const; const size_t block_size_; char* freestart_; // beginning of the free space in most recent block char* freestart_when_empty_; // beginning of the free space when we're empty char* last_alloc_; // used to make sure ReturnBytes() is safe size_t remaining_; // STL vector isn't as efficient as it could be, so we use an array at first int blocks_alloced_; // how many of the first_blocks_ have been alloced AllocatedBlock first_blocks_[16]; // the length of this array is arbitrary // if the first_blocks_ aren't enough, expand into overflow_blocks_. vector<AllocatedBlock>* overflow_blocks_; void FreeBlocks(); // Frees all except first block DISALLOW_EVIL_CONSTRUCTORS(UnsafeArena); }; // Operators for allocation on the arena // Syntax: new (AllocateInArena, arena) MyClass; // STL containers, etc. enum AllocateInArenaType { AllocateInArena }; } // namespace re2 inline void* operator new(size_t size, re2::AllocateInArenaType /* unused */, re2::UnsafeArena *arena) { return reinterpret_cast<char*>(arena->GetMemory(size, 1)); } #endif // RE2_UTIL_ARENA_H_