/**************************************************************************/1/* pooled_list.h */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#pragma once3132// Simple template to provide a pool with O(1) allocate and free.33// The freelist could alternatively be a linked list placed within the unused elements34// to use less memory, however a separate freelist is probably more cache friendly.3536// NOTE : Take great care when using this with non POD types. The construction and destruction37// is done in the LocalVector, NOT as part of the pool. So requesting a new item does not guarantee38// a constructor is run, and free does not guarantee a destructor.39// You should generally handle clearing40// an item explicitly after a request, as it may contain 'leftovers'.41// This is by design for fastest use in the BVH. If you want a more general pool42// that does call constructors / destructors on request / free, this should probably be43// a separate template.4445// The zero_on_first_request feature is optional and is useful for e.g. pools of handles,46// which may use a ref count which we want to be initialized to zero the first time a handle is created,47// but left alone on subsequent allocations (as will typically be incremented).4849// Note that there is no function to compact the pool - this would50// invalidate any existing pool IDs held externally.51// Compaction can be done but would rely on a more complex method52// of preferentially giving out lower IDs in the freelist first.5354#include "core/templates/local_vector.h"5556template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>57class PooledList {58LocalVector<T, U> list;59LocalVector<U, U> freelist;6061// not all list members are necessarily used62U _used_size;6364public:65PooledList() {66_used_size = 0;67}6869// Use with care, in most cases you should make sure to70// free all elements first (i.e. _used_size would be zero),71// although it could also be used without this as an optimization72// in some cases.73void clear() {74list.clear();75freelist.clear();76_used_size = 0;77}7879uint64_t estimate_memory_use() const {80return ((uint64_t)list.size() * sizeof(T)) + ((uint64_t)freelist.size() * sizeof(U));81}8283const T &operator[](U p_index) const {84return list[p_index];85}86T &operator[](U p_index) {87return list[p_index];88}8990// To be explicit in a pool there is a distinction91// between the number of elements that are currently92// in use, and the number of elements that have been reserved.93// Using size() would be vague.94U used_size() const { return _used_size; }95U reserved_size() const { return list.size(); }9697T *request(U &r_id) {98_used_size++;99100if (freelist.size()) {101// pop from freelist102int new_size = freelist.size() - 1;103r_id = freelist[new_size];104freelist.resize_uninitialized(new_size);105106return &list[r_id];107}108109r_id = list.size();110if constexpr (force_trivial || std::is_trivially_constructible_v<T>) {111list.resize_uninitialized(r_id + 1);112} else {113list.resize_initialized(r_id + 1);114}115116static_assert((!zero_on_first_request) || (__is_pod(T)), "zero_on_first_request requires trivial type");117if constexpr (zero_on_first_request && __is_pod(T)) {118list[r_id] = {};119}120121return &list[r_id];122}123void free(const U &p_id) {124// should not be on free list already125ERR_FAIL_UNSIGNED_INDEX(p_id, list.size());126freelist.push_back(p_id);127ERR_FAIL_COND_MSG(!_used_size, "_used_size has become out of sync, have you double freed an item?");128_used_size--;129}130};131132// a pooled list which automatically keeps a list of the active members133template <typename T, typename U = uint32_t, bool force_trivial = false, bool zero_on_first_request = false>134class TrackedPooledList {135public:136U pool_used_size() const { return _pool.used_size(); }137U pool_reserved_size() const { return _pool.reserved_size(); }138U active_size() const { return _active_list.size(); }139140// use with care, see the earlier notes in the PooledList clear()141void clear() {142_pool.clear();143_active_list.clear();144_active_map.clear();145}146147U get_active_id(U p_index) const {148return _active_list[p_index];149}150151const T &get_active(U p_index) const {152return _pool[get_active_id(p_index)];153}154155T &get_active(U p_index) {156return _pool[get_active_id(p_index)];157}158159const T &operator[](U p_index) const {160return _pool[p_index];161}162T &operator[](U p_index) {163return _pool[p_index];164}165166T *request(U &r_id) {167T *item = _pool.request(r_id);168169// add to the active list170U active_list_id = _active_list.size();171_active_list.push_back(r_id);172173// expand the active map (this should be in sync with the pool list174if (_pool.used_size() > _active_map.size()) {175_active_map.resize_uninitialized(_pool.used_size());176}177178// store in the active map179_active_map[r_id] = active_list_id;180181return item;182}183184void free(const U &p_id) {185_pool.free(p_id);186187// remove from the active list.188U list_id = _active_map[p_id];189190// zero the _active map to detect bugs (only in debug?)191_active_map[p_id] = -1;192193_active_list.remove_unordered(list_id);194195// keep the replacement in sync with the correct list Id196if (list_id < _active_list.size()) {197// which pool id has been replaced in the active list198U replacement_id = _active_list[list_id];199200// keep that replacements map up to date with the new position201_active_map[replacement_id] = list_id;202}203}204205const LocalVector<U, U> &get_active_list() const { return _active_list; }206207private:208PooledList<T, U, force_trivial, zero_on_first_request> _pool;209LocalVector<U, U> _active_map;210LocalVector<U, U> _active_list;211};212213214