/* $Id$ */ /* * This file is part of OpenTTD. * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . */ /** @file pool_type.hpp Defintion of Pool, structure used to access PoolItems, and PoolItem, base structure for Vehicle, Town, and other indexed items. */ #ifndef POOL_TYPE_HPP #define POOL_TYPE_HPP #include "smallvec_type.hpp" #include "enum_type.hpp" /** Various types of a pool. */ enum PoolType { PT_NONE = 0x00, ///< No pool is selected. PT_NORMAL = 0x01, ///< Normal pool containing game objects. PT_NCLIENT = 0x02, ///< Network client pools. PT_NADMIN = 0x04, ///< Network admin pool. PT_DATA = 0x08, ///< NewGRF or other data, that is not reset together with normal pools. PT_ALL = 0x0F, ///< All pool types. }; DECLARE_ENUM_AS_BIT_SET(PoolType) typedef std::vector PoolVector; ///< Vector of pointers to PoolBase /** Base class for base of all pools. */ struct PoolBase { const PoolType type; ///< Type of this pool. /** * Function used to access the vector of all pools. * @return pointer to vector of all pools */ static PoolVector *GetPools() { static PoolVector *pools = new PoolVector(); return pools; } static void Clean(PoolType); /** * Constructor registers this object in the pool vector. * @param pt type of this pool. */ PoolBase(PoolType pt) : type(pt) { PoolBase::GetPools()->push_back(this); } virtual ~PoolBase(); /** * Virtual method that deletes all items in the pool. */ virtual void CleanPool() = 0; private: /** * Dummy private copy constructor to prevent compilers from * copying the structure, which fails due to GetPools(). */ PoolBase(const PoolBase &other); }; /** * Base class for all pools. * @tparam Titem Type of the class/struct that is going to be pooled * @tparam Tindex Type of the index for this pool * @tparam Tgrowth_step Size of growths; if the pool is full increase the size by this amount * @tparam Tmax_size Maximum size of the pool * @tparam Tpool_type Type of this pool * @tparam Tcache Whether to perform 'alloc' caching, i.e. don't actually free/malloc just reuse the memory * @tparam Tzero Whether to zero the memory * @warning when Tcache is enabled *all* instances of this pool's item must be of the same size. */ template struct Pool : PoolBase { /* Ensure Tmax_size is within the bounds of Tindex. */ assert_compile((uint64)(Tmax_size - 1) >> 8 * sizeof(Tindex) == 0); static const size_t MAX_SIZE = Tmax_size; ///< Make template parameter accessible from outside const char * const name; ///< Name of this pool size_t size; ///< Current allocated size size_t first_free; ///< No item with index lower than this is free (doesn't say anything about this one!) size_t first_unused; ///< This and all higher indexes are free (doesn't say anything about first_unused-1 !) size_t items; ///< Number of used indexes (non-NULL) #ifdef OTTD_ASSERT size_t checked; ///< Number of items we checked for #endif /* OTTD_ASSERT */ bool cleaning; ///< True if cleaning pool (deleting all items) Titem **data; ///< Pointer to array of pointers to Titem Pool(const char *name); virtual void CleanPool(); /** * Returns Titem with given index * @param index of item to get * @return pointer to Titem * @pre index < this->first_unused */ inline Titem *Get(size_t index) { assert_msg(index < this->first_unused, "index: " PRINTF_SIZE ", first_unused: " PRINTF_SIZE ", name: %s", index, this->first_unused, this->name); return this->data[index]; } /** * Tests whether given index can be used to get valid (non-NULL) Titem * @param index index to examine * @return true if PoolItem::Get(index) will return non-NULL pointer */ inline bool IsValidID(size_t index) { return index < this->first_unused && this->Get(index) != NULL; } /** * Tests whether we can allocate 'n' items * @param n number of items we want to allocate * @return true if 'n' items can be allocated */ inline bool CanAllocate(size_t n = 1) { bool ret = this->items <= Tmax_size - n; #ifdef OTTD_ASSERT this->checked = ret ? n : 0; #endif /* OTTD_ASSERT */ return ret; } /** * Base class for all PoolItems * @tparam Tpool The pool this item is going to be part of */ template *Tpool> struct PoolItem { Tindex index; ///< Index of this pool item /** * Allocates space for new Titem * @param size size of Titem * @return pointer to allocated memory * @note can never fail (return NULL), use CanAllocate() to check first! */ inline void *operator new(size_t size) { return Tpool->GetNew(size); } /** * Marks Titem as free. Its memory is released * @param p memory to free * @note the item has to be allocated in the pool! */ inline void operator delete(void *p) { if (p == NULL) return; Titem *pn = (Titem *)p; assert_msg(pn == Tpool->Get(pn->index), "name: %s", Tpool->name); Tpool->FreeItem(pn->index); } /** * Allocates space for new Titem with given index * @param size size of Titem * @param index index of item * @return pointer to allocated memory * @note can never fail (return NULL), use CanAllocate() to check first! * @pre index has to be unused! Else it will crash */ inline void *operator new(size_t size, size_t index) { return Tpool->GetNew(size, index); } /** * Allocates space for new Titem at given memory address * @param size size of Titem * @param ptr where are we allocating the item? * @return pointer to allocated memory (== ptr) * @note use of this is strongly discouraged * @pre the memory must not be allocated in the Pool! */ inline void *operator new(size_t size, void *ptr) { for (size_t i = 0; i < Tpool->first_unused; i++) { /* Don't allow creating new objects over existing. * Even if we called the destructor and reused this memory, * we don't know whether 'size' and size of currently allocated * memory are the same (because of possible inheritance). * Use { size_t index = item->index; delete item; new (index) item; } * instead to make sure destructor is called and no memory leaks. */ assert_msg(ptr != Tpool->data[i], "name: %s", Tpool->name); } return ptr; } /** Helper functions so we can use PoolItem::Function() instead of _poolitem_pool.Function() */ /** * Tests whether we can allocate 'n' items * @param n number of items we want to allocate * @return true if 'n' items can be allocated */ static inline bool CanAllocateItem(size_t n = 1) { return Tpool->CanAllocate(n); } /** * Returns current state of pool cleaning - yes or no * @return true iff we are cleaning the pool now */ static inline bool CleaningPool() { return Tpool->cleaning; } /** * Tests whether given index can be used to get valid (non-NULL) Titem * @param index index to examine * @return true if PoolItem::Get(index) will return non-NULL pointer */ static inline bool IsValidID(size_t index) { return Tpool->IsValidID(index); } /** * Returns Titem with given index * @param index of item to get * @return pointer to Titem * @pre index < this->first_unused */ static inline Titem *Get(size_t index) { return Tpool->Get(index); } /** * Returns Titem with given index * @param index of item to get * @return pointer to Titem * @note returns NULL for invalid index */ static inline Titem *GetIfValid(size_t index) { return index < Tpool->first_unused ? Tpool->Get(index) : NULL; } /** * Returns first unused index. Useful when iterating over * all pool items. * @return first unused index */ static inline size_t GetPoolSize() { return Tpool->first_unused; } /** * Returns number of valid items in the pool * @return number of valid items in the pool */ static inline size_t GetNumItems() { return Tpool->items; } /** * Dummy function called after destructor of each member. * If you want to use it, override it in PoolItem's subclass. * @param index index of deleted item * @note when this function is called, PoolItem::Get(index) == NULL. * @note it's called only when !CleaningPool() */ static inline void PostDestructor(size_t index) { } /** * Dummy function called before a pool is about to be cleaned. * If you want to use it, override it in PoolItem's subclass. * @note it's called only when CleaningPool() */ static inline void PreCleanPool() { } }; private: static const size_t NO_FREE_ITEM = MAX_UVALUE(size_t); ///< Constant to indicate we can't allocate any more items /** * Helper struct to cache 'freed' PoolItems so we * do not need to allocate them again. */ struct AllocCache { /** The next in our 'cache' */ AllocCache *next; }; /** Cache of freed pointers */ AllocCache *alloc_cache; void *AllocateItem(size_t size, size_t index); void ResizeFor(size_t index); size_t FindFirstFree(); void *GetNew(size_t size); void *GetNew(size_t size, size_t index); void FreeItem(size_t index); }; #define FOR_ALL_ITEMS_FROM(type, iter, var, start) \ for (size_t iter = start; var = NULL, iter < type::GetPoolSize(); iter++) \ if ((var = type::Get(iter)) != NULL) #define FOR_ALL_ITEMS(type, iter, var) FOR_ALL_ITEMS_FROM(type, iter, var, 0) #endif /* POOL_TYPE_HPP */