2 * This file is part of OpenTTD.
3 * 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.
4 * 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.
5 * 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 <http://www.gnu.org/licenses/>.
8 /** @file pool_func.hpp Some methods of Pool are placed here in order to reduce compilation time and binary size. */
13 #include "alloc_func.hpp"
14 #include "bitmath_func.hpp"
15 #include "mem_func.hpp"
16 #include "pool_type.hpp"
17 #include "../error_func.h"
19 #include "../saveload/saveload_error.hpp" // SlErrorCorruptFmt
22 * Helper for defining the method's signature.
23 * @param type The return type of the method.
25 #define DEFINE_POOL_METHOD(type) \
26 template <class Titem, typename Tindex, size_t Tgrowth_step, size_t Tmax_size, PoolType Tpool_type, bool Tcache, bool Tzero> \
27 type Pool<Titem, Tindex, Tgrowth_step, Tmax_size, Tpool_type, Tcache, Tzero>
30 * Create a clean pool.
31 * @param name The name for the pool.
33 DEFINE_POOL_METHOD(inline)::Pool(const char *name
) :
42 #endif /* WITH_ASSERT */
49 * Resizes the pool so 'index' can be addressed
50 * @param index index we will allocate later
51 * @pre index >= this->size
52 * @pre index < Tmax_size
54 DEFINE_POOL_METHOD(inline void)::ResizeFor(size_t index
)
56 assert(index
>= this->size
);
57 assert(index
< Tmax_size
);
59 size_t new_size
= std::min(Tmax_size
, Align(index
+ 1, Tgrowth_step
));
61 this->data
= ReallocT(this->data
, new_size
);
62 MemSetT(this->data
+ this->size
, 0, new_size
- this->size
);
64 this->used_bitmap
.resize(Align(new_size
, BITMAP_SIZE
) / BITMAP_SIZE
);
65 if (this->size
% BITMAP_SIZE
!= 0) {
66 /* Already-allocated bits above old size are now unused. */
67 this->used_bitmap
[this->size
/ BITMAP_SIZE
] &= ~((~static_cast<BitmapStorage
>(0)) << (this->size
% BITMAP_SIZE
));
69 if (new_size
% BITMAP_SIZE
!= 0) {
70 /* Bits above new size are considered used. */
71 this->used_bitmap
[new_size
/ BITMAP_SIZE
] |= (~static_cast<BitmapStorage
>(0)) << (new_size
% BITMAP_SIZE
);
74 this->size
= new_size
;
78 * Searches for first free index
79 * @return first free index, NO_FREE_ITEM on failure
81 DEFINE_POOL_METHOD(inline size_t)::FindFirstFree()
83 for (auto it
= std::next(std::begin(this->used_bitmap
), this->first_free
/ BITMAP_SIZE
); it
!= std::end(this->used_bitmap
); ++it
) {
84 BitmapStorage available
= ~(*it
);
85 if (available
== 0) continue;
86 return std::distance(std::begin(this->used_bitmap
), it
) * BITMAP_SIZE
+ FindFirstBit(available
);
89 assert(this->first_unused
== this->size
);
91 if (this->first_unused
< Tmax_size
) {
92 this->ResizeFor(this->first_unused
);
93 return this->first_unused
;
96 assert(this->first_unused
== Tmax_size
);
102 * Makes given index valid
103 * @param size size of item
104 * @param index index of item
105 * @pre index < this->size
106 * @pre this->Get(index) == nullptr
108 DEFINE_POOL_METHOD(inline void *)::AllocateItem(size_t size
, size_t index
)
110 assert(this->data
[index
] == nullptr);
112 this->first_unused
= std::max(this->first_unused
, index
+ 1);
116 if (Tcache
&& this->alloc_cache
!= nullptr) {
117 assert(sizeof(Titem
) == size
);
118 item
= reinterpret_cast<Titem
*>(this->alloc_cache
);
119 this->alloc_cache
= this->alloc_cache
->next
;
121 /* Explicitly casting to (void *) prevents a clang warning -
122 * we are actually memsetting a (not-yet-constructed) object */
123 memset(static_cast<void *>(item
), 0, sizeof(Titem
));
126 item
= reinterpret_cast<Titem
*>(CallocT
<uint8_t>(size
));
128 item
= reinterpret_cast<Titem
*>(MallocT
<uint8_t>(size
));
130 this->data
[index
] = item
;
131 SetBit(this->used_bitmap
[index
/ BITMAP_SIZE
], index
% BITMAP_SIZE
);
132 item
->index
= (Tindex
)(uint
)index
;
138 * @param size size of item
139 * @return pointer to allocated item
140 * @note FatalError() on failure! (no free item)
142 DEFINE_POOL_METHOD(void *)::GetNew(size_t size
)
144 size_t index
= this->FindFirstFree();
147 assert(this->checked
!= 0);
149 #endif /* WITH_ASSERT */
150 if (index
== NO_FREE_ITEM
) {
151 FatalError("{}: no more free items", this->name
);
154 this->first_free
= index
+ 1;
155 return this->AllocateItem(size
, index
);
159 * Allocates new item with given index
160 * @param size size of item
161 * @param index index of item
162 * @return pointer to allocated item
163 * @note SlErrorCorruptFmt() on failure! (index out of range or already used)
165 DEFINE_POOL_METHOD(void *)::GetNew(size_t size
, size_t index
)
167 if (index
>= Tmax_size
) {
168 SlErrorCorruptFmt("{} index {} out of range ({})", this->name
, index
, Tmax_size
);
171 if (index
>= this->size
) this->ResizeFor(index
);
173 if (this->data
[index
] != nullptr) {
174 SlErrorCorruptFmt("{} index {} already in use", this->name
, index
);
177 return this->AllocateItem(size
, index
);
181 * Deallocates memory used by this index and marks item as free
182 * @param index item to deallocate
183 * @pre unit is allocated (non-nullptr)
184 * @note 'delete nullptr' doesn't cause call of this function, so it is safe
186 DEFINE_POOL_METHOD(void)::FreeItem(size_t index
)
188 assert(index
< this->size
);
189 assert(this->data
[index
] != nullptr);
191 AllocCache
*ac
= reinterpret_cast<AllocCache
*>(this->data
[index
]);
192 ac
->next
= this->alloc_cache
;
193 this->alloc_cache
= ac
;
195 free(this->data
[index
]);
197 this->data
[index
] = nullptr;
198 this->first_free
= std::min(this->first_free
, index
);
200 if (!this->cleaning
) {
201 ClrBit(this->used_bitmap
[index
/ BITMAP_SIZE
], index
% BITMAP_SIZE
);
202 Titem::PostDestructor(index
);
206 /** Destroys all items in the pool and resets all member variables. */
207 DEFINE_POOL_METHOD(void)::CleanPool()
209 this->cleaning
= true;
210 for (size_t i
= 0; i
< this->first_unused
; i
++) {
211 delete this->Get(i
); // 'delete nullptr;' is very valid
213 assert(this->items
== 0);
215 this->used_bitmap
.clear();
216 this->used_bitmap
.shrink_to_fit();
217 this->first_unused
= this->first_free
= this->size
= 0;
218 this->data
= nullptr;
219 this->cleaning
= false;
222 while (this->alloc_cache
!= nullptr) {
223 AllocCache
*ac
= this->alloc_cache
;
224 this->alloc_cache
= ac
->next
;
230 #undef DEFINE_POOL_METHOD
233 * Force instantiation of pool methods so we don't get linker errors.
234 * Only methods accessed from methods defined in pool.hpp need to be
235 * forcefully instantiated.
237 #define INSTANTIATE_POOL_METHODS(name) \
238 template void * name ## Pool::GetNew(size_t size); \
239 template void * name ## Pool::GetNew(size_t size, size_t index); \
240 template void name ## Pool::FreeItem(size_t index); \
241 template void name ## Pool::CleanPool();
243 #endif /* POOL_FUNC_HPP */