| 1 | /**************************************************************************/ | 
|---|
| 2 | /*  pooled_list.h                                                         */ | 
|---|
| 3 | /**************************************************************************/ | 
|---|
| 4 | /*                         This file is part of:                          */ | 
|---|
| 5 | /*                             GODOT ENGINE                               */ | 
|---|
| 6 | /*                        https://godotengine.org                         */ | 
|---|
| 7 | /**************************************************************************/ | 
|---|
| 8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ | 
|---|
| 9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */ | 
|---|
| 10 | /*                                                                        */ | 
|---|
| 11 | /* Permission is hereby granted, free of charge, to any person obtaining  */ | 
|---|
| 12 | /* a copy of this software and associated documentation files (the        */ | 
|---|
| 13 | /* "Software"), to deal in the Software without restriction, including    */ | 
|---|
| 14 | /* without limitation the rights to use, copy, modify, merge, publish,    */ | 
|---|
| 15 | /* distribute, sublicense, and/or sell copies of the Software, and to     */ | 
|---|
| 16 | /* permit persons to whom the Software is furnished to do so, subject to  */ | 
|---|
| 17 | /* the following conditions:                                              */ | 
|---|
| 18 | /*                                                                        */ | 
|---|
| 19 | /* The above copyright notice and this permission notice shall be         */ | 
|---|
| 20 | /* included in all copies or substantial portions of the Software.        */ | 
|---|
| 21 | /*                                                                        */ | 
|---|
| 22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */ | 
|---|
| 23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */ | 
|---|
| 24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ | 
|---|
| 25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */ | 
|---|
| 26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */ | 
|---|
| 27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */ | 
|---|
| 28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */ | 
|---|
| 29 | /**************************************************************************/ | 
|---|
| 30 |  | 
|---|
| 31 | #ifndef POOLED_LIST_H | 
|---|
| 32 | #define POOLED_LIST_H | 
|---|
| 33 |  | 
|---|
| 34 | // Simple template to provide a pool with O(1) allocate and free. | 
|---|
| 35 | // The freelist could alternatively be a linked list placed within the unused elements | 
|---|
| 36 | // to use less memory, however a separate freelist is probably more cache friendly. | 
|---|
| 37 |  | 
|---|
| 38 | // NOTE : Take great care when using this with non POD types. The construction and destruction | 
|---|
| 39 | // is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee | 
|---|
| 40 | // a constructor is run, and free does not guarantee a destructor. | 
|---|
| 41 | // You should generally handle clearing | 
|---|
| 42 | // an item explicitly after a request, as it may contain 'leftovers'. | 
|---|
| 43 | // This is by design for fastest use in the BVH. If you want a more general pool | 
|---|
| 44 | // that does call constructors / destructors on request / free, this should probably be | 
|---|
| 45 | // a separate template. | 
|---|
| 46 |  | 
|---|
| 47 | // The zero_on_first_request feature is optional and is useful for e.g. pools of handles, | 
|---|
| 48 | // which may use a ref count which we want to be initialized to zero the first time a handle is created, | 
|---|
| 49 | // but left alone on subsequent allocations (as will typically be incremented). | 
|---|
| 50 |  | 
|---|
| 51 | // Note that there is no function to compact the pool - this would | 
|---|
| 52 | // invalidate any existing pool IDs held externally. | 
|---|
| 53 | // Compaction can be done but would rely on a more complex method | 
|---|
| 54 | // of preferentially giving out lower IDs in the freelist first. | 
|---|
| 55 |  | 
|---|
| 56 | #include "core/templates/local_vector.h" | 
|---|
| 57 |  | 
|---|
| 58 | template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> | 
|---|
| 59 | class PooledList { | 
|---|
| 60 | LocalVector<T, U, force_trivial> list; | 
|---|
| 61 | LocalVector<U, U, true> freelist; | 
|---|
| 62 |  | 
|---|
| 63 | // not all list members are necessarily used | 
|---|
| 64 | U _used_size; | 
|---|
| 65 |  | 
|---|
| 66 | public: | 
|---|
| 67 | PooledList() { | 
|---|
| 68 | _used_size = 0; | 
|---|
| 69 | } | 
|---|
| 70 |  | 
|---|
| 71 | // Use with care, in most cases you should make sure to | 
|---|
| 72 | // free all elements first (i.e. _used_size would be zero), | 
|---|
| 73 | // although it could also be used without this as an optimization | 
|---|
| 74 | // in some cases. | 
|---|
| 75 | void clear() { | 
|---|
| 76 | list.clear(); | 
|---|
| 77 | freelist.clear(); | 
|---|
| 78 | _used_size = 0; | 
|---|
| 79 | } | 
|---|
| 80 |  | 
|---|
| 81 | uint64_t estimate_memory_use() const { | 
|---|
| 82 | return ((uint64_t)list.size() * sizeof(T)) + ((uint64_t)freelist.size() * sizeof(U)); | 
|---|
| 83 | } | 
|---|
| 84 |  | 
|---|
| 85 | const T &operator[](U p_index) const { | 
|---|
| 86 | return list[p_index]; | 
|---|
| 87 | } | 
|---|
| 88 | T &operator[](U p_index) { | 
|---|
| 89 | return list[p_index]; | 
|---|
| 90 | } | 
|---|
| 91 |  | 
|---|
| 92 | // To be explicit in a pool there is a distinction | 
|---|
| 93 | // between the number of elements that are currently | 
|---|
| 94 | // in use, and the number of elements that have been reserved. | 
|---|
| 95 | // Using size() would be vague. | 
|---|
| 96 | U used_size() const { return _used_size; } | 
|---|
| 97 | U reserved_size() const { return list.size(); } | 
|---|
| 98 |  | 
|---|
| 99 | T *request(U &r_id) { | 
|---|
| 100 | _used_size++; | 
|---|
| 101 |  | 
|---|
| 102 | if (freelist.size()) { | 
|---|
| 103 | // pop from freelist | 
|---|
| 104 | int new_size = freelist.size() - 1; | 
|---|
| 105 | r_id = freelist[new_size]; | 
|---|
| 106 | freelist.resize(new_size); | 
|---|
| 107 |  | 
|---|
| 108 | return &list[r_id]; | 
|---|
| 109 | } | 
|---|
| 110 |  | 
|---|
| 111 | r_id = list.size(); | 
|---|
| 112 | list.resize(r_id + 1); | 
|---|
| 113 |  | 
|---|
| 114 | static_assert((!zero_on_first_request) || (__is_pod(T)), "zero_on_first_request requires trivial type"); | 
|---|
| 115 | if constexpr (zero_on_first_request && __is_pod(T)) { | 
|---|
| 116 | list[r_id] = {}; | 
|---|
| 117 | } | 
|---|
| 118 |  | 
|---|
| 119 | return &list[r_id]; | 
|---|
| 120 | } | 
|---|
| 121 | void free(const U &p_id) { | 
|---|
| 122 | // should not be on free list already | 
|---|
| 123 | ERR_FAIL_UNSIGNED_INDEX(p_id, list.size()); | 
|---|
| 124 | freelist.push_back(p_id); | 
|---|
| 125 | ERR_FAIL_COND_MSG(!_used_size, "_used_size has become out of sync, have you double freed an item?"); | 
|---|
| 126 | _used_size--; | 
|---|
| 127 | } | 
|---|
| 128 | }; | 
|---|
| 129 |  | 
|---|
| 130 | // a pooled list which automatically keeps a list of the active members | 
|---|
| 131 | template <class T, class U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false> | 
|---|
| 132 | class TrackedPooledList { | 
|---|
| 133 | public: | 
|---|
| 134 | U pool_used_size() const { return _pool.used_size(); } | 
|---|
| 135 | U pool_reserved_size() const { return _pool.reserved_size(); } | 
|---|
| 136 | U active_size() const { return _active_list.size(); } | 
|---|
| 137 |  | 
|---|
| 138 | // use with care, see the earlier notes in the PooledList clear() | 
|---|
| 139 | void clear() { | 
|---|
| 140 | _pool.clear(); | 
|---|
| 141 | _active_list.clear(); | 
|---|
| 142 | _active_map.clear(); | 
|---|
| 143 | } | 
|---|
| 144 |  | 
|---|
| 145 | U get_active_id(U p_index) const { | 
|---|
| 146 | return _active_list[p_index]; | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | const T &get_active(U p_index) const { | 
|---|
| 150 | return _pool[get_active_id(p_index)]; | 
|---|
| 151 | } | 
|---|
| 152 |  | 
|---|
| 153 | T &get_active(U p_index) { | 
|---|
| 154 | return _pool[get_active_id(p_index)]; | 
|---|
| 155 | } | 
|---|
| 156 |  | 
|---|
| 157 | const T &operator[](U p_index) const { | 
|---|
| 158 | return _pool[p_index]; | 
|---|
| 159 | } | 
|---|
| 160 | T &operator[](U p_index) { | 
|---|
| 161 | return _pool[p_index]; | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|
| 164 | T *request(U &r_id) { | 
|---|
| 165 | T *item = _pool.request(r_id); | 
|---|
| 166 |  | 
|---|
| 167 | // add to the active list | 
|---|
| 168 | U active_list_id = _active_list.size(); | 
|---|
| 169 | _active_list.push_back(r_id); | 
|---|
| 170 |  | 
|---|
| 171 | // expand the active map (this should be in sync with the pool list | 
|---|
| 172 | if (_pool.used_size() > _active_map.size()) { | 
|---|
| 173 | _active_map.resize(_pool.used_size()); | 
|---|
| 174 | } | 
|---|
| 175 |  | 
|---|
| 176 | // store in the active map | 
|---|
| 177 | _active_map[r_id] = active_list_id; | 
|---|
| 178 |  | 
|---|
| 179 | return item; | 
|---|
| 180 | } | 
|---|
| 181 |  | 
|---|
| 182 | void free(const U &p_id) { | 
|---|
| 183 | _pool.free(p_id); | 
|---|
| 184 |  | 
|---|
| 185 | // remove from the active list. | 
|---|
| 186 | U list_id = _active_map[p_id]; | 
|---|
| 187 |  | 
|---|
| 188 | // zero the _active map to detect bugs (only in debug?) | 
|---|
| 189 | _active_map[p_id] = -1; | 
|---|
| 190 |  | 
|---|
| 191 | _active_list.remove_unordered(list_id); | 
|---|
| 192 |  | 
|---|
| 193 | // keep the replacement in sync with the correct list Id | 
|---|
| 194 | if (list_id < _active_list.size()) { | 
|---|
| 195 | // which pool id has been replaced in the active list | 
|---|
| 196 | U replacement_id = _active_list[list_id]; | 
|---|
| 197 |  | 
|---|
| 198 | // keep that replacements map up to date with the new position | 
|---|
| 199 | _active_map[replacement_id] = list_id; | 
|---|
| 200 | } | 
|---|
| 201 | } | 
|---|
| 202 |  | 
|---|
| 203 | const LocalVector<U, U> &get_active_list() const { return _active_list; } | 
|---|
| 204 |  | 
|---|
| 205 | private: | 
|---|
| 206 | PooledList<T, U, force_trivial, zero_on_first_request> _pool; | 
|---|
| 207 | LocalVector<U, U> _active_map; | 
|---|
| 208 | LocalVector<U, U> _active_list; | 
|---|
| 209 | }; | 
|---|
| 210 |  | 
|---|
| 211 | #endif // POOLED_LIST_H | 
|---|
| 212 |  | 
|---|