Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/core/templates/pooled_list.h
9973 views
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
#pragma once
32
33
// Simple template to provide a pool with O(1) allocate and free.
34
// The freelist could alternatively be a linked list placed within the unused elements
35
// to use less memory, however a separate freelist is probably more cache friendly.
36
37
// NOTE : Take great care when using this with non POD types. The construction and destruction
38
// is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee
39
// a constructor is run, and free does not guarantee a destructor.
40
// You should generally handle clearing
41
// an item explicitly after a request, as it may contain 'leftovers'.
42
// This is by design for fastest use in the BVH. If you want a more general pool
43
// that does call constructors / destructors on request / free, this should probably be
44
// a separate template.
45
46
// The zero_on_first_request feature is optional and is useful for e.g. pools of handles,
47
// which may use a ref count which we want to be initialized to zero the first time a handle is created,
48
// but left alone on subsequent allocations (as will typically be incremented).
49
50
// Note that there is no function to compact the pool - this would
51
// invalidate any existing pool IDs held externally.
52
// Compaction can be done but would rely on a more complex method
53
// of preferentially giving out lower IDs in the freelist first.
54
55
#include "core/templates/local_vector.h"
56
57
template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>
58
class PooledList {
59
LocalVector<T, U> list;
60
LocalVector<U, U> freelist;
61
62
// not all list members are necessarily used
63
U _used_size;
64
65
public:
66
PooledList() {
67
_used_size = 0;
68
}
69
70
// Use with care, in most cases you should make sure to
71
// free all elements first (i.e. _used_size would be zero),
72
// although it could also be used without this as an optimization
73
// in some cases.
74
void clear() {
75
list.clear();
76
freelist.clear();
77
_used_size = 0;
78
}
79
80
uint64_t estimate_memory_use() const {
81
return ((uint64_t)list.size() * sizeof(T)) + ((uint64_t)freelist.size() * sizeof(U));
82
}
83
84
const T &operator[](U p_index) const {
85
return list[p_index];
86
}
87
T &operator[](U p_index) {
88
return list[p_index];
89
}
90
91
// To be explicit in a pool there is a distinction
92
// between the number of elements that are currently
93
// in use, and the number of elements that have been reserved.
94
// Using size() would be vague.
95
U used_size() const { return _used_size; }
96
U reserved_size() const { return list.size(); }
97
98
T *request(U &r_id) {
99
_used_size++;
100
101
if (freelist.size()) {
102
// pop from freelist
103
int new_size = freelist.size() - 1;
104
r_id = freelist[new_size];
105
freelist.resize_uninitialized(new_size);
106
107
return &list[r_id];
108
}
109
110
r_id = list.size();
111
if constexpr (force_trivial || std::is_trivially_constructible_v<T>) {
112
list.resize_uninitialized(r_id + 1);
113
} else {
114
list.resize_initialized(r_id + 1);
115
}
116
117
static_assert((!zero_on_first_request) || (__is_pod(T)), "zero_on_first_request requires trivial type");
118
if constexpr (zero_on_first_request && __is_pod(T)) {
119
list[r_id] = {};
120
}
121
122
return &list[r_id];
123
}
124
void free(const U &p_id) {
125
// should not be on free list already
126
ERR_FAIL_UNSIGNED_INDEX(p_id, list.size());
127
freelist.push_back(p_id);
128
ERR_FAIL_COND_MSG(!_used_size, "_used_size has become out of sync, have you double freed an item?");
129
_used_size--;
130
}
131
};
132
133
// a pooled list which automatically keeps a list of the active members
134
template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>
135
class TrackedPooledList {
136
public:
137
U pool_used_size() const { return _pool.used_size(); }
138
U pool_reserved_size() const { return _pool.reserved_size(); }
139
U active_size() const { return _active_list.size(); }
140
141
// use with care, see the earlier notes in the PooledList clear()
142
void clear() {
143
_pool.clear();
144
_active_list.clear();
145
_active_map.clear();
146
}
147
148
U get_active_id(U p_index) const {
149
return _active_list[p_index];
150
}
151
152
const T &get_active(U p_index) const {
153
return _pool[get_active_id(p_index)];
154
}
155
156
T &get_active(U p_index) {
157
return _pool[get_active_id(p_index)];
158
}
159
160
const T &operator[](U p_index) const {
161
return _pool[p_index];
162
}
163
T &operator[](U p_index) {
164
return _pool[p_index];
165
}
166
167
T *request(U &r_id) {
168
T *item = _pool.request(r_id);
169
170
// add to the active list
171
U active_list_id = _active_list.size();
172
_active_list.push_back(r_id);
173
174
// expand the active map (this should be in sync with the pool list
175
if (_pool.used_size() > _active_map.size()) {
176
_active_map.resize_uninitialized(_pool.used_size());
177
}
178
179
// store in the active map
180
_active_map[r_id] = active_list_id;
181
182
return item;
183
}
184
185
void free(const U &p_id) {
186
_pool.free(p_id);
187
188
// remove from the active list.
189
U list_id = _active_map[p_id];
190
191
// zero the _active map to detect bugs (only in debug?)
192
_active_map[p_id] = -1;
193
194
_active_list.remove_unordered(list_id);
195
196
// keep the replacement in sync with the correct list Id
197
if (list_id < _active_list.size()) {
198
// which pool id has been replaced in the active list
199
U replacement_id = _active_list[list_id];
200
201
// keep that replacements map up to date with the new position
202
_active_map[replacement_id] = list_id;
203
}
204
}
205
206
const LocalVector<U, U> &get_active_list() const { return _active_list; }
207
208
private:
209
PooledList<T, U, force_trivial, zero_on_first_request> _pool;
210
LocalVector<U, U> _active_map;
211
LocalVector<U, U> _active_list;
212
};
213
214