diff --git a/core/os/block_allocator.cpp b/core/os/block_allocator.cpp new file mode 100644 index 00000000000..fb45c1e56f6 --- /dev/null +++ b/core/os/block_allocator.cpp @@ -0,0 +1,126 @@ +/**************************************************************************/ +/* block_allocator.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "block_allocator.h" + +#include "core/os/thread.h" +#include "core/string/print_string.h" + +uint32_t BlockAllocator::get_total_blocks() { + return blocks_count; +} + +void BlockAllocator::init(uint32_t p_structure_size, uint32_t p_start_size) { + ERR_FAIL_COND_MSG(is_initialized(), "Allocator is in use!!!"); + if (p_structure_size < sizeof(FreeListElement)) { + structure_size = sizeof(FreeListElement); + } else { + structure_size = p_structure_size; + } + cur_block_size = p_start_size; + allocate_new_block(); +} + +void BlockAllocator::init(uint32_t p_start_size) { + CRASH_COND(structure_size == 0 || is_initialized()); + init(structure_size, p_start_size); +} + +void *BlockAllocator::alloc() { + void *pointer = 0; + uint8_t *block = blocks[blocks_count - 1]; + size_t left = current_pointer - block; + total_elements++; + if (free_list != nullptr) { + pointer = free_list; + free_list = free_list->next; + return pointer; + } + + if (unlikely(left >= cur_block_size * (size_t)structure_size)) { + allocate_new_block(); + } + pointer = current_pointer; + current_pointer += structure_size; + + return pointer; +} + +uint32_t BlockAllocator::get_blocks_capacity(uint32_t p_blocks_size) { + return next_power_of_2(p_blocks_size); +} + +void BlockAllocator::allocate_new_block() { + uint32_t blocks_capacity = get_blocks_capacity(blocks_count); + blocks_count++; + if (blocks_count > blocks_capacity) { + blocks = reinterpret_cast(memrealloc(blocks, get_blocks_capacity(blocks_count) * sizeof(uint8_t *))); + } + cur_block_size *= 2; + blocks[blocks_count - 1] = reinterpret_cast(memalloc(cur_block_size * (size_t)structure_size)); + current_pointer = blocks[blocks_count - 1]; +} + +size_t BlockAllocator::get_total_elements() { + return total_elements; +} + +uint32_t BlockAllocator::get_structure_size() { + return structure_size; +} + +void BlockAllocator::free(void *p_ptr) { + FreeListElement *new_head = (FreeListElement *)p_ptr; + new_head->next = free_list; + free_list = new_head; + total_elements--; +} + +void BlockAllocator::set_structure_size(uint32_t p_structure_size) { + CRASH_COND(is_initialized()); + structure_size = p_structure_size; +} + +void BlockAllocator::reset() { + if (blocks == nullptr) { + return; + } + for (uint32_t i = 0; i < blocks_count; i++) { + memfree(blocks[i]); + } + memfree(blocks); + blocks_count = 0; + blocks = nullptr; + free_list = nullptr; +} + +BlockAllocator::~BlockAllocator() { + reset(); +} diff --git a/core/os/block_allocator.h b/core/os/block_allocator.h new file mode 100644 index 00000000000..5b40daff1ff --- /dev/null +++ b/core/os/block_allocator.h @@ -0,0 +1,76 @@ +/**************************************************************************/ +/* block_allocator.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef BLOCK_ALLOCATOR_H +#define BLOCK_ALLOCATOR_H + +#include "core/templates/local_vector.h" + +/** + * + * [ 1 2 3 4 ] + * | | | | + * 1 2 3 4 5 6 7 + * | * || * * || * * * * | | ... | + * + */ + +class BlockAllocator { + struct FreeListElement { + FreeListElement *next = nullptr; + }; + + size_t cur_block_size = 1; + uint8_t *current_pointer = nullptr; + uint8_t **blocks = nullptr; + + FreeListElement *free_list = nullptr; + size_t total_elements = 0; + uint32_t structure_size = 0; + uint32_t blocks_count = 0; + void allocate_new_block(); + static uint32_t get_blocks_capacity(uint32_t p_blocks_size); + +public: + _FORCE_INLINE_ bool is_initialized() { return blocks != nullptr; } + size_t get_total_elements(); + uint32_t get_structure_size(); + uint32_t get_total_blocks(); + + void init(uint32_t p_structure_size, uint32_t p_start_size); + void init(uint32_t p_start_size); + void set_structure_size(uint32_t p_structure_size); + void *alloc(); + void free(void *p_ptr); + void reset(); + ~BlockAllocator(); +}; + +#endif // BLOCK_ALLOCATOR_H diff --git a/core/os/static_block_allocator.cpp b/core/os/static_block_allocator.cpp new file mode 100644 index 00000000000..c04bb33e165 --- /dev/null +++ b/core/os/static_block_allocator.cpp @@ -0,0 +1,171 @@ +/**************************************************************************/ +/* static_block_allocator.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "static_block_allocator.h" + +#include "core/os/block_allocator.h" +#include "core/os/os.h" +#include "core/os/thread.h" +#include "core/templates/oa_hash_map.h" +#include + +typedef OAHashMap SizeToIdMap; + +struct ThreadData { + uint8_t _offset[64]; + LocalVector allocs; + SizeToIdMap size_to_id = SizeToIdMap(32); + BinaryMutex mtx; +}; + +uint32_t total_num_allocators; +int physics_threads_count; +ThreadData *threads_data = nullptr; +bool is_set_num_threads_by_OS = false; + +int32_t StaticBlockAllocator::_get_thread_id() { + return (Thread::get_caller_id() - Thread::MAIN_ID) % physics_threads_count; +} + +int64_t StaticBlockAllocator::_create_id(int32_t p_size_pos, int32_t p_thread_id) { + union { + int64_t id; + struct { + int32_t pos; + int32_t thread_id; + }; + } id_data; + id_data.pos = p_size_pos; + id_data.thread_id = p_thread_id; + return id_data.id; +} + +void StaticBlockAllocator::_get_from_id(int64_t p_id, int32_t &r_size_pos, int32_t &r_thread_id) { + union { + int64_t id; + struct { + int32_t pos; + int32_t thread_id; + }; + } id_data; + id_data.id = p_id; + r_size_pos = id_data.pos; + r_thread_id = id_data.thread_id; +} + +void StaticBlockAllocator::_init() { + physics_threads_count = 1; + threads_data = memnew(ThreadData); +} + +int64_t StaticBlockAllocator::_get_allocator_id_for_size(size_t p_size) { + if (unlikely(!is_set_num_threads_by_OS)) { + if (threads_data == nullptr) { + _init(); + } + if (OS::get_singleton() != nullptr) { + physics_threads_count = OS::get_singleton()->get_processor_count(); + if (physics_threads_count > 1) { + threads_data = (ThreadData *)memrealloc(threads_data, physics_threads_count * sizeof(ThreadData)); + for (int i = 1; i < physics_threads_count; i++) { + memnew_placement(&threads_data[i], ThreadData); + } + } + is_set_num_threads_by_OS = true; + } + } + int32_t thread_id = _get_thread_id(); + ThreadData &t = threads_data[thread_id]; + t.mtx.lock(); + BlockAllocator *ptr = t.allocs.ptr(); + int64_t pos = -1; + t.size_to_id.lookup(p_size, pos); + + if (unlikely(pos == -1)) { + ((SafeNumeric *)(&total_num_allocators))->increment(); + pos = t.allocs.size(); + t.allocs.push_back(BlockAllocator()); + t.allocs[pos].init(p_size, 16); + t.size_to_id.insert(p_size, pos); + } else if (!ptr[pos].is_initialized()) { + ((SafeNumeric *)(&total_num_allocators))->increment(); + ptr[pos].init(2); + } + int64_t id = _create_id(pos, thread_id); + t.mtx.unlock(); + return id; +} + +int64_t StaticBlockAllocator::get_allocator_id_for_size(size_t p_size) { + uint64_t pos = _get_allocator_id_for_size(p_size); + return pos; +} + +void *StaticBlockAllocator::allocate_by_id(int64_t p_id) { + int32_t pos, thread_id; + _get_from_id(p_id, pos, thread_id); + ThreadData &t = threads_data[thread_id]; + BlockAllocator &b_allocator = t.allocs.ptr()[pos]; + t.mtx.lock(); + if (unlikely(!b_allocator.is_initialized())) { + ((SafeNumeric *)(&total_num_allocators))->increment(); + b_allocator.init(2); + } + void *ret = b_allocator.alloc(); + t.mtx.unlock(); + return ret; +} + +void StaticBlockAllocator::free_by_id(int64_t p_id, void *p_ptr) { + int32_t pos, thread_id; + _get_from_id(p_id, pos, thread_id); + ThreadData &t = threads_data[thread_id]; + t.mtx.lock(); + DEV_ASSERT(!(pos >= (int32_t)t.allocs.size() || !t.allocs.ptr()[pos].is_initialized())); + BlockAllocator &b_allocator = t.allocs.ptr()[pos]; + b_allocator.free(p_ptr); + + if (likely(b_allocator.get_total_elements() != 0)) { + t.mtx.unlock(); + return; + } + b_allocator.reset(); + if (unlikely(((SafeNumeric *)(&total_num_allocators))->decrement() == 0)) { + t.mtx.unlock(); + for (int i = 0; i < physics_threads_count; i++) { + threads_data[i].~ThreadData(); + } + memfree(threads_data); + threads_data = nullptr; + is_set_num_threads_by_OS = false; + return; + } + t.mtx.unlock(); +} diff --git a/core/os/static_block_allocator.h b/core/os/static_block_allocator.h new file mode 100644 index 00000000000..c0e29041724 --- /dev/null +++ b/core/os/static_block_allocator.h @@ -0,0 +1,51 @@ +/**************************************************************************/ +/* static_block_allocator.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef STATIC_BLOCK_ALLOCATOR_H +#define STATIC_BLOCK_ALLOCATOR_H + +#include "core/typedefs.h" + +#include + +class StaticBlockAllocator { + static int64_t _get_allocator_id_for_size(size_t p_size); + static int32_t _get_thread_id(); + static int64_t _create_id(int32_t p_size_pos, int32_t p_thread_id); + static void _get_from_id(int64_t p_id, int32_t &r_size_pos, int32_t &r_thread_id); + static void _init(); + +public: + static int64_t get_allocator_id_for_size(size_t p_size); + static void *allocate_by_id(int64_t p_id); + static void free_by_id(int64_t p_id, void *p_ptr); +}; + +#endif // STATIC_BLOCK_ALLOCATOR_H diff --git a/core/templates/hash_map.h b/core/templates/hash_map.h index a3e8c2c788c..c035af0f6d9 100644 --- a/core/templates/hash_map.h +++ b/core/templates/hash_map.h @@ -32,10 +32,10 @@ #define HASH_MAP_H #include "core/math/math_funcs.h" -#include "core/os/memory.h" #include "core/templates/hashfuncs.h" #include "core/templates/paged_allocator.h" #include "core/templates/pair.h" +#include "core/templates/typed_static_block_allocator.h" /** * A HashMap implementation that uses open addressing with Robin Hood hashing. @@ -64,7 +64,7 @@ struct HashMapElement { template , - typename Allocator = DefaultTypedAllocator>> + typename Allocator = TypedStaticBlockAllocator>> class HashMap { public: static constexpr uint32_t MIN_CAPACITY_INDEX = 2; // Use a prime. diff --git a/core/templates/list.h b/core/templates/list.h index 6663f06c309..b3c0087a39a 100644 --- a/core/templates/list.h +++ b/core/templates/list.h @@ -34,6 +34,7 @@ #include "core/error/error_macros.h" #include "core/os/memory.h" #include "core/templates/sort_array.h" +#include "core/templates/typed_static_block_allocator.h" /** * Generic Templatized Linked List Implementation. @@ -220,6 +221,7 @@ public: #endif private: struct _Data { + TypedStaticBlockAllocator a; Element *first = nullptr; Element *last = nullptr; int size_cache = 0; @@ -244,7 +246,7 @@ private: p_I->next_ptr->prev_ptr = p_I->prev_ptr; } - memdelete_allocator(const_cast(p_I)); + a.delete_allocation(const_cast(p_I)); size_cache--; return true; @@ -293,7 +295,7 @@ public: _data->size_cache = 0; } - Element *n = memnew_allocator(Element, A); + Element *n = _data->a.new_allocation(); n->value = (T &)value; n->prev_ptr = _data->last; @@ -332,7 +334,7 @@ public: _data->size_cache = 0; } - Element *n = memnew_allocator(Element, A); + Element *n = _data->a.new_allocation(); n->value = (T &)value; n->prev_ptr = nullptr; n->next_ptr = _data->first; @@ -366,7 +368,7 @@ public: return push_back(p_value); } - Element *n = memnew_allocator(Element, A); + Element *n = _data->a.new_allocation(); n->value = (T &)p_value; n->prev_ptr = p_element; n->next_ptr = p_element->next_ptr; @@ -392,7 +394,7 @@ public: return push_back(p_value); } - Element *n = memnew_allocator(Element, A); + Element *n = _data->a.new_allocation(); n->value = (T &)p_value; n->prev_ptr = p_element->prev_ptr; n->next_ptr = p_element; diff --git a/core/templates/rb_map.h b/core/templates/rb_map.h index ef555e4a161..43609c1b055 100644 --- a/core/templates/rb_map.h +++ b/core/templates/rb_map.h @@ -32,8 +32,8 @@ #define RB_MAP_H #include "core/error/error_macros.h" -#include "core/os/memory.h" #include "core/templates/pair.h" +#include "core/templates/typed_static_block_allocator.h" // based on the very nice implementation of rb-trees by: // https://web.archive.org/web/20120507164830/https://web.mit.edu/~emin/www/source_code/red_black_tree/index.html @@ -195,6 +195,7 @@ private: struct _Data { Element *_root = nullptr; Element *_nil = nullptr; + TypedStaticBlockAllocator element_alloc; int size_cache = 0; _FORCE_INLINE_ _Data() { @@ -208,14 +209,14 @@ private: } void _create_root() { - _root = memnew_allocator(Element(KeyValue(K(), V())), A); + _root = element_alloc.new_allocation(Element(KeyValue(K(), V()))); _root->parent = _root->left = _root->right = _nil; _root->color = BLACK; } void _free_root() { if (_root) { - memdelete_allocator(_root); + element_alloc.delete_allocation(_root); _root = nullptr; } } @@ -424,7 +425,7 @@ private: } typedef KeyValue KV; - Element *new_node = memnew_allocator(Element(KV(p_key, p_value)), A); + Element *new_node = _data.element_alloc.new_allocation(Element(KV(p_key, p_value))); new_node->parent = new_parent; new_node->right = _data._nil; new_node->left = _data._nil; @@ -560,7 +561,7 @@ private: p_node->_prev->_next = p_node->_next; } - memdelete_allocator(p_node); + _data.element_alloc.delete_allocation(p_node); _data.size_cache--; ERR_FAIL_COND(_data._nil->color == RED); } @@ -585,7 +586,7 @@ private: _cleanup_tree(p_element->left); _cleanup_tree(p_element->right); - memdelete_allocator(p_element); + _data.element_alloc.delete_allocation(p_element); } void _copy_from(const RBMap &p_map) { diff --git a/core/templates/rb_set.h b/core/templates/rb_set.h index ac7a8df36a3..e15ef976db2 100644 --- a/core/templates/rb_set.h +++ b/core/templates/rb_set.h @@ -31,7 +31,7 @@ #ifndef RB_SET_H #define RB_SET_H -#include "core/os/memory.h" +#include "core/templates/typed_static_block_allocator.h" #include "core/typedefs.h" // based on the very nice implementation of rb-trees by: @@ -166,6 +166,7 @@ private: struct _Data { Element *_root = nullptr; Element *_nil = nullptr; + TypedStaticBlockAllocator element_alloc; int size_cache = 0; _FORCE_INLINE_ _Data() { @@ -179,14 +180,14 @@ private: } void _create_root() { - _root = memnew_allocator(Element, A); + _root = element_alloc.new_allocation(); _root->parent = _root->left = _root->right = _nil; _root->color = BLACK; } void _free_root() { if (_root) { - memdelete_allocator(_root); + element_alloc.delete_allocation(_root); _root = nullptr; } } @@ -393,7 +394,7 @@ private: } } - Element *new_node = memnew_allocator(Element, A); + Element *new_node = _data.element_alloc.new_allocation(); new_node->parent = new_parent; new_node->right = _data._nil; new_node->left = _data._nil; @@ -528,8 +529,7 @@ private: if (p_node->_prev) { p_node->_prev->_next = p_node->_next; } - - memdelete_allocator(p_node); + _data.element_alloc.delete_allocation(p_node); _data.size_cache--; ERR_FAIL_COND(_data._nil->color == RED); } @@ -554,7 +554,7 @@ private: _cleanup_tree(p_element->left); _cleanup_tree(p_element->right); - memdelete_allocator(p_element); + _data.element_alloc.delete_allocation(p_element); } void _copy_from(const RBSet &p_set) { diff --git a/core/templates/typed_static_block_allocator.h b/core/templates/typed_static_block_allocator.h new file mode 100644 index 00000000000..df72e3416e5 --- /dev/null +++ b/core/templates/typed_static_block_allocator.h @@ -0,0 +1,62 @@ +/**************************************************************************/ +/* typed_static_block_allocator.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TYPED_STATIC_BLOCK_ALLOCATOR_H +#define TYPED_STATIC_BLOCK_ALLOCATOR_H + +#include "core/os/memory.h" +#include "core/os/static_block_allocator.h" + +template +class TypedStaticBlockAllocator { + int64_t allocator_id = -1; + +public: + template + T *new_allocation(const Args &&...p_args) { + if (unlikely(allocator_id == -1)) { + allocator_id = StaticBlockAllocator::get_allocator_id_for_size(sizeof(T)); + } + T *ret = static_cast(StaticBlockAllocator::allocate_by_id(allocator_id)); + memnew_placement(ret, T(p_args...)); + return ret; + } + void delete_allocation(T *p_allocation) { + if (!predelete_handler(p_allocation)) { + return; // doesn't want to be deleted + } + if constexpr (!std::is_trivially_destructible_v) { + p_allocation->~T(); + } + StaticBlockAllocator::free_by_id(allocator_id, p_allocation); + } +}; + +#endif // TYPED_STATIC_BLOCK_ALLOCATOR_H