/* Copyright (c) 2010 Wildfire Games * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * pool allocator */ #ifndef INCLUDED_ALLOCATORS_POOL #define INCLUDED_ALLOCATORS_POOL #include "lib/allocators/dynarray.h" /** * allocator design parameters: * - O(1) alloc and free; * - either fixed- or variable-sized blocks; * - doesn't preallocate the entire pool; * - returns sequential addresses. * * opaque! do not read/write any fields! **/ struct Pool { DynArray da; /** * size of elements. = 0 if pool set up for variable-sized * elements, otherwise rounded up to pool alignment. **/ size_t el_size; /** * pointer to freelist (opaque); see freelist_*. * never used (remains 0) if elements are of variable size. **/ void* freelist; }; /** * pass as pool_create's \ param to indicate variable-sized allocs * are required (see below). **/ const size_t POOL_VARIABLE_ALLOCS = ~(size_t)0u; /** * Ready Pool for use. * * @param p Pool* * @param max_size Max size [bytes] of the Pool; this much * (rounded up to next page multiple) virtual address space is reserved. * no virtual memory is actually committed until calls to pool_alloc. * @param el_size Number of bytes that will be returned by each * pool_alloc (whose size parameter is then ignored). Can be 0 to * allow variable-sized allocations, but pool_free is then unusable. * @return Status **/ LIB_API Status pool_create(Pool* p, size_t max_size, size_t el_size); /** * free all memory (address space + physical) that constitutes the * given Pool. * * future alloc and free calls on this pool will fail. * continued use of the allocated memory (*) is * impossible because it is marked not-present via MMU. * (* no matter if in freelist or unused or "allocated" to user) * * @param p Pool* * @return Status. **/ LIB_API Status pool_destroy(Pool* p); /** * indicate whether a pointer was allocated from the given pool. * * this is useful for callers that use several types of allocators. * * @param p Pool* * @param el * @return bool. **/ LIB_API bool pool_contains(const Pool* p, void* el); /** * Dole out memory from the pool. * exhausts the freelist before returning new entries to improve locality. * * @param p Pool* * @param size bytes to allocate; ignored if pool_create's el_size was not 0. * @return allocated memory, or 0 if the Pool would have to be expanded and * there isn't enough memory to do so. **/ LIB_API void* pool_alloc(Pool* p, size_t size); /** * Make a fixed-size element available for reuse in the given Pool. * * this is not allowed if the Pool was created for variable-size elements. * rationale: avoids having to pass el_size here and compare with size when * allocating; also prevents fragmentation and leaking memory. * * @param p Pool* * @param el Element returned by pool_alloc. **/ LIB_API void pool_free(Pool* p, void* el); /** * "free" all user allocations that ensued from the given Pool. * * this resets it as if freshly pool_create-d, but doesn't release the * underlying reserved virtual memory. * * @param p Pool* **/ LIB_API void pool_free_all(Pool* p); /** * Return the number of bytes committed in the pool's backing array. * * This is roughly the number of bytes allocated in this pool plus the * unused freelist entries. * * @param p Pool* * @return number of bytes **/ LIB_API size_t pool_committed(Pool* p); /** * C++ wrapper on top of pool_alloc for fixed-size allocations (determined by sizeof(T)) * * T must be POD (Plain Old Data) because it is memset to 0! **/ template class PoolAllocator { public: explicit PoolAllocator(size_t maxElements) { (void)pool_create(&m_pool, maxElements*sizeof(T), sizeof(T)); } ~PoolAllocator() { (void)pool_destroy(&m_pool); } T* AllocateZeroedMemory() { T* t = (T*)pool_alloc(&m_pool, 0); if(!t) throw std::bad_alloc(); memset(t, 0, sizeof(T)); return t; } void Free(T* t) { pool_free(&m_pool, t); } private: Pool m_pool; }; /** * C++ wrapper on top of pool_alloc for variable-sized allocations. * Memory is returned uninitialised. */ class RawPoolAllocator { public: /** * @param maxSize maximum size of pool in bytes */ explicit RawPoolAllocator(size_t maxSize) { const size_t el_size = 0; // sizes will be passed to each pool_alloc (void)pool_create(&m_pool, maxSize, el_size); } ~RawPoolAllocator() { (void)pool_destroy(&m_pool); } /** * @param count number of elements of type T to allocate space for */ template T* AllocateMemory(size_t count) { T* t = (T*)pool_alloc(&m_pool, count*sizeof(T)); if(!t) throw std::bad_alloc(); return t; } size_t GetCommittedSize() { return pool_committed(&m_pool); } private: Pool m_pool; }; /** * STL-compatible allocator based on a RawPoolAllocator. * (Allocated memory is never freed, until the RawPoolAllocator is destroyed.) */ template class pool_allocator { private: // No default constructor pool_allocator() throw (); public: RawPoolAllocator& p; typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; template struct rebind { typedef pool_allocator other; }; explicit pool_allocator(RawPoolAllocator& pool) throw () : p(pool) { } template pool_allocator(const pool_allocator& alloc) throw () : p(alloc.p) { } pool_allocator& operator=(const pool_allocator&) throw () { } pointer address(reference r) { return &r; } const_pointer address(const_reference s) { return &s; } size_type max_size() const throw () { return std::numeric_limits::max() / sizeof(T); } void construct(const pointer ptr, const value_type& t) { new (ptr) T(t); } void destroy(const pointer ptr) { ptr->~T(); UNUSED2(ptr); // silence MSVC warnings } pointer allocate(size_type n) { return p.AllocateMemory (n); } pointer allocate(size_type n, const void* const) { return allocate(n); } void deallocate(const pointer UNUSED(ptr), const size_type UNUSED(n)) { // ignore deallocations } }; template bool operator==(const pool_allocator&, const pool_allocator&) throw () { return true; } template bool operator!=(const pool_allocator&, const pool_allocator&) throw () { return false; } #endif // #ifndef INCLUDED_ALLOCATORS_POOL