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 | |