Port a bunch of Godot container templates to GDExtension.
parent
6a464b53f1
commit
e36180f377
|
@ -222,6 +222,8 @@ def generate_builtin_class_header(builtin_api, size, used_classes, fully_used_cl
|
||||||
# Special cases.
|
# Special cases.
|
||||||
if class_name == "String":
|
if class_name == "String":
|
||||||
result.append("#include <godot_cpp/variant/char_string.hpp>")
|
result.append("#include <godot_cpp/variant/char_string.hpp>")
|
||||||
|
result.append("#include <godot_cpp/variant/char_utils.hpp>")
|
||||||
|
result.append("#include <godot_cpp/variant/ucaps.hpp>")
|
||||||
|
|
||||||
if class_name == "Array":
|
if class_name == "Array":
|
||||||
result.append("#include <godot_cpp/variant/array_helpers.hpp>")
|
result.append("#include <godot_cpp/variant/array_helpers.hpp>")
|
||||||
|
|
|
@ -209,6 +209,9 @@ MethodBind *ClassDB::bind_vararg_method(uint32_t p_flags, const char *p_name, M
|
||||||
|
|
||||||
return bind;
|
return bind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define GDREGISTER_CLASS(m_class) ClassDB::register_class<m_class>();
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
||||||
#endif // ! CLASS_DB_HPP
|
#endif // ! CLASS_DB_HPP
|
||||||
|
|
|
@ -62,6 +62,34 @@ namespace Math {
|
||||||
#define Math_INF INFINITY
|
#define Math_INF INFINITY
|
||||||
#define Math_NAN NAN
|
#define Math_NAN NAN
|
||||||
|
|
||||||
|
// Windows badly defines a lot of stuff we'll never use. Undefine it.
|
||||||
|
#ifdef _WIN32
|
||||||
|
#undef MIN // override standard definition
|
||||||
|
#undef MAX // override standard definition
|
||||||
|
#undef CLAMP // override standard definition
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Generic ABS function, for math uses please use Math::abs.
|
||||||
|
#ifndef ABS
|
||||||
|
#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef SIGN
|
||||||
|
#define SIGN(m_v) (((m_v) == 0) ? (0.0) : (((m_v) < 0) ? (-1.0) : (+1.0)))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MIN
|
||||||
|
#define MIN(m_a, m_b) (((m_a) < (m_b)) ? (m_a) : (m_b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef MAX
|
||||||
|
#define MAX(m_a, m_b) (((m_a) > (m_b)) ? (m_a) : (m_b))
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CLAMP
|
||||||
|
#define CLAMP(m_a, m_min, m_max) (((m_a) < (m_min)) ? (m_min) : (((m_a) > (m_max)) ? m_max : m_a))
|
||||||
|
#endif
|
||||||
|
|
||||||
// Functions reproduced as in Godot's source code `math_funcs.h`.
|
// Functions reproduced as in Godot's source code `math_funcs.h`.
|
||||||
// Some are overloads to automatically support changing real_t into either double or float in the way Godot does.
|
// Some are overloads to automatically support changing real_t into either double or float in the way Godot does.
|
||||||
|
|
||||||
|
@ -435,7 +463,7 @@ inline int fast_ftoi(float a) {
|
||||||
: "m" (a));*/
|
: "m" (a));*/
|
||||||
|
|
||||||
#else
|
#else
|
||||||
b = lrintf(a); //assuming everything but msvc 2012 or earlier has lrint
|
b = lrintf(a); // assuming everything but msvc 2012 or earlier has lrint
|
||||||
#endif
|
#endif
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
|
||||||
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
|
void *operator new(size_t p_size, const char *p_description); ///< operator new that takes a description and uses MemoryStaticPool
|
||||||
|
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)); ///< operator new that takes a description and uses MemoryStaticPool
|
||||||
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
|
void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description); ///< operator new that takes a description and uses a pointer to the preallocated memory
|
||||||
|
|
||||||
_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) {
|
_ALWAYS_INLINE_ void *operator new(size_t p_size, void *p_pointer, size_t check, const char *p_description) {
|
||||||
|
@ -76,10 +77,27 @@ _ALWAYS_INLINE_ T *_post_initialize(T *p_obj) {
|
||||||
return p_obj;
|
return p_obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define memalloc(m_size) Memory::alloc_static(m_size)
|
||||||
|
#define memrealloc(m_mem, m_size) Memory::realloc_static(m_mem, m_size)
|
||||||
|
#define memfree(m_mem) Memory::free_static(m_mem)
|
||||||
|
|
||||||
#define memnew(m_class) _post_initialize(new ("") m_class)
|
#define memnew(m_class) _post_initialize(new ("") m_class)
|
||||||
|
|
||||||
|
#define memnew_allocator(m_class, m_allocator) _post_initialize(new (m_allocator::alloc) m_class)
|
||||||
#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement, sizeof(m_class), "") m_class)
|
#define memnew_placement(m_placement, m_class) _post_initialize(new (m_placement, sizeof(m_class), "") m_class)
|
||||||
|
|
||||||
|
// Generic comparator used in Map, List, etc.
|
||||||
|
template <class T>
|
||||||
|
struct Comparator {
|
||||||
|
_ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class DefaultAllocator {
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ static void *alloc(size_t p_memory) { return Memory::alloc_static(p_memory); }
|
||||||
|
_ALWAYS_INLINE_ static void free(void *p_ptr) { Memory::free_static(p_ptr); }
|
||||||
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
|
void memdelete(T *p_class, typename std::enable_if<!std::is_base_of_v<godot::Wrapped, T>>::type * = 0) {
|
||||||
if (!__has_trivial_destructor(T)) {
|
if (!__has_trivial_destructor(T)) {
|
||||||
|
@ -94,6 +112,15 @@ void memdelete(T *p_class) {
|
||||||
godot::internal::gdn_interface->object_destroy(p_class->_owner);
|
godot::internal::gdn_interface->object_destroy(p_class->_owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <class T, class A>
|
||||||
|
void memdelete_allocator(T *p_class) {
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
|
p_class->~T();
|
||||||
|
}
|
||||||
|
|
||||||
|
A::free(p_class);
|
||||||
|
}
|
||||||
|
|
||||||
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
|
#define memnew_arr(m_class, m_count) memnew_arr_template<m_class>(m_count)
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -137,6 +164,19 @@ void memdelete_arr(T *p_class) {
|
||||||
Memory::free_static(ptr);
|
Memory::free_static(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct _GlobalNil {
|
||||||
|
int color = 1;
|
||||||
|
_GlobalNil *right;
|
||||||
|
_GlobalNil *left;
|
||||||
|
_GlobalNil *parent;
|
||||||
|
|
||||||
|
_GlobalNil();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _GlobalNilClass {
|
||||||
|
static _GlobalNil _nil;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
||||||
#endif // ! GODOT_CPP_MEMORY_HPP
|
#endif // ! GODOT_CPP_MEMORY_HPP
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* mutex_lock.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 MUTEX_LOCK_HPP
|
||||||
|
#define MUTEX_LOCK_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/classes/mutex.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
class MutexLock {
|
||||||
|
const Mutex &mutex;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ explicit MutexLock(const Mutex &p_mutex) :
|
||||||
|
mutex(p_mutex) {
|
||||||
|
const_cast<Mutex *>(&mutex)->lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ ~MutexLock() {
|
||||||
|
const_cast<Mutex *>(&mutex)->unlock();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#define _THREAD_SAFE_CLASS_ mutable Mutex _thread_safe_;
|
||||||
|
#define _THREAD_SAFE_METHOD_ MutexLock _thread_safe_method_(_thread_safe_);
|
||||||
|
#define _THREAD_SAFE_LOCK_ _thread_safe_.lock();
|
||||||
|
#define _THREAD_SAFE_UNLOCK_ _thread_safe_.unlock();
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! MUTEX_LOCK_HPP
|
|
@ -0,0 +1,392 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* cowdata.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 COWDATA_HPP
|
||||||
|
#define COWDATA_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/classes/global_constants.hpp>
|
||||||
|
#include <godot_cpp/core/class_db.hpp>
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/math.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/templates/safe_refcount.hpp>
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Vector;
|
||||||
|
|
||||||
|
template <class T, class V>
|
||||||
|
class VMap;
|
||||||
|
|
||||||
|
// Silence a false positive warning (see GH-52119).
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wplacement-new"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class CowData {
|
||||||
|
template <class TV>
|
||||||
|
friend class Vector;
|
||||||
|
|
||||||
|
template <class TV, class VV>
|
||||||
|
friend class VMap;
|
||||||
|
|
||||||
|
private:
|
||||||
|
mutable T *_ptr = nullptr;
|
||||||
|
|
||||||
|
// internal helpers
|
||||||
|
|
||||||
|
_FORCE_INLINE_ SafeNumeric<uint32_t> *_get_refcount() const {
|
||||||
|
if (!_ptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<SafeNumeric<uint32_t> *>(_ptr) - 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ uint32_t *_get_size() const {
|
||||||
|
if (!_ptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<uint32_t *>(_ptr) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *_get_data() const {
|
||||||
|
if (!_ptr) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return reinterpret_cast<T *>(_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ size_t _get_alloc_size(size_t p_elements) const {
|
||||||
|
return Math::next_power_of_2(p_elements * sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool _get_alloc_size_checked(size_t p_elements, size_t *out) const {
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
size_t o;
|
||||||
|
size_t p;
|
||||||
|
if (__builtin_mul_overflow(p_elements, sizeof(T), &o)) {
|
||||||
|
*out = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
*out = Math::next_power_of_2(o);
|
||||||
|
if (__builtin_add_overflow(o, static_cast<size_t>(32), &p)) {
|
||||||
|
return false; // No longer allocated here.
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
// Speed is more important than correctness here, do the operations unchecked
|
||||||
|
// and hope for the best.
|
||||||
|
*out = _get_alloc_size(p_elements);
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void _unref(void *p_data);
|
||||||
|
void _ref(const CowData *p_from);
|
||||||
|
void _ref(const CowData &p_from);
|
||||||
|
uint32_t _copy_on_write();
|
||||||
|
|
||||||
|
public:
|
||||||
|
void operator=(const CowData<T> &p_from) { _ref(p_from); }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *ptrw() {
|
||||||
|
_copy_on_write();
|
||||||
|
return (T *)_get_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ const T *ptr() const {
|
||||||
|
return _get_data();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int size() const {
|
||||||
|
uint32_t *size = (uint32_t *)_get_size();
|
||||||
|
if (size) {
|
||||||
|
return *size;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void clear() { resize(0); }
|
||||||
|
_FORCE_INLINE_ bool is_empty() const { return _ptr == nullptr; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void set(int p_index, const T &p_elem) {
|
||||||
|
ERR_FAIL_INDEX(p_index, size());
|
||||||
|
_copy_on_write();
|
||||||
|
_get_data()[p_index] = p_elem;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T &get_m(int p_index) {
|
||||||
|
CRASH_BAD_INDEX(p_index, size());
|
||||||
|
_copy_on_write();
|
||||||
|
return _get_data()[p_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ const T &get(int p_index) const {
|
||||||
|
CRASH_BAD_INDEX(p_index, size());
|
||||||
|
|
||||||
|
return _get_data()[p_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Error resize(int p_size);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void remove_at(int p_index) {
|
||||||
|
ERR_FAIL_INDEX(p_index, size());
|
||||||
|
T *p = ptrw();
|
||||||
|
int len = size();
|
||||||
|
for (int i = p_index; i < len - 1; i++) {
|
||||||
|
p[i] = p[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(len - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Error insert(int p_pos, const T &p_val) {
|
||||||
|
ERR_FAIL_INDEX_V(p_pos, size() + 1, ERR_INVALID_PARAMETER);
|
||||||
|
resize(size() + 1);
|
||||||
|
for (int i = (size() - 1); i > p_pos; i--) {
|
||||||
|
set(i, get(i - 1));
|
||||||
|
}
|
||||||
|
set(p_pos, p_val);
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
int find(const T &p_val, int p_from = 0) const;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ CowData() {}
|
||||||
|
_FORCE_INLINE_ ~CowData();
|
||||||
|
_FORCE_INLINE_ CowData(CowData<T> &p_from) { _ref(p_from); };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CowData<T>::_unref(void *p_data) {
|
||||||
|
if (!p_data) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeNumeric<uint32_t> *refc = _get_refcount();
|
||||||
|
|
||||||
|
if (refc->decrement() > 0) {
|
||||||
|
return; // still in use
|
||||||
|
}
|
||||||
|
// clean up
|
||||||
|
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
|
uint32_t *count = _get_size();
|
||||||
|
T *data = (T *)(count + 1);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < *count; ++i) {
|
||||||
|
// call destructors
|
||||||
|
data[i].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// free mem
|
||||||
|
Memory::free_static((uint8_t *)p_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
uint32_t CowData<T>::_copy_on_write() {
|
||||||
|
if (!_ptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SafeNumeric<uint32_t> *refc = _get_refcount();
|
||||||
|
|
||||||
|
uint32_t rc = refc->get();
|
||||||
|
if (unlikely(rc > 1)) {
|
||||||
|
/* in use by more than me */
|
||||||
|
uint32_t current_size = *_get_size();
|
||||||
|
|
||||||
|
uint32_t *mem_new = (uint32_t *)Memory::alloc_static(_get_alloc_size(current_size));
|
||||||
|
|
||||||
|
new (mem_new - 2) SafeNumeric<uint32_t>(1); // refcount
|
||||||
|
*(mem_new - 1) = current_size; // size
|
||||||
|
|
||||||
|
T *_data = (T *)(mem_new);
|
||||||
|
|
||||||
|
// initialize new elements
|
||||||
|
if (__has_trivial_copy(T)) {
|
||||||
|
memcpy(mem_new, _ptr, current_size * sizeof(T));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
for (uint32_t i = 0; i < current_size; i++) {
|
||||||
|
memnew_placement(&_data[i], T(_get_data()[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_unref(_ptr);
|
||||||
|
_ptr = _data;
|
||||||
|
|
||||||
|
rc = 1;
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
Error CowData<T>::resize(int p_size) {
|
||||||
|
ERR_FAIL_COND_V(p_size < 0, ERR_INVALID_PARAMETER);
|
||||||
|
|
||||||
|
int current_size = size();
|
||||||
|
|
||||||
|
if (p_size == current_size) {
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_size == 0) {
|
||||||
|
// wants to clean up
|
||||||
|
_unref(_ptr);
|
||||||
|
_ptr = nullptr;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// possibly changing size, copy on write
|
||||||
|
uint32_t rc = _copy_on_write();
|
||||||
|
|
||||||
|
size_t current_alloc_size = _get_alloc_size(current_size);
|
||||||
|
size_t alloc_size;
|
||||||
|
ERR_FAIL_COND_V(!_get_alloc_size_checked(p_size, &alloc_size), ERR_OUT_OF_MEMORY);
|
||||||
|
|
||||||
|
if (p_size > current_size) {
|
||||||
|
if (alloc_size != current_alloc_size) {
|
||||||
|
if (current_size == 0) {
|
||||||
|
// alloc from scratch
|
||||||
|
uint32_t *ptr = (uint32_t *)Memory::alloc_static(alloc_size);
|
||||||
|
ERR_FAIL_COND_V(!ptr, ERR_OUT_OF_MEMORY);
|
||||||
|
*(ptr - 1) = 0; // size, currently none
|
||||||
|
new (ptr - 2) SafeNumeric<uint32_t>(1); // refcount
|
||||||
|
|
||||||
|
_ptr = (T *)ptr;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size);
|
||||||
|
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
||||||
|
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); // refcount
|
||||||
|
|
||||||
|
_ptr = (T *)(_ptrnew);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// construct the newly created elements
|
||||||
|
|
||||||
|
if (!__has_trivial_constructor(T)) {
|
||||||
|
T *elems = _get_data();
|
||||||
|
|
||||||
|
for (int i = *_get_size(); i < p_size; i++) {
|
||||||
|
memnew_placement(&elems[i], T);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*_get_size() = p_size;
|
||||||
|
|
||||||
|
} else if (p_size < current_size) {
|
||||||
|
if (!__has_trivial_destructor(T)) {
|
||||||
|
// deinitialize no longer needed elements
|
||||||
|
for (uint32_t i = p_size; i < *_get_size(); i++) {
|
||||||
|
T *t = &_get_data()[i];
|
||||||
|
t->~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alloc_size != current_alloc_size) {
|
||||||
|
uint32_t *_ptrnew = (uint32_t *)Memory::realloc_static(_ptr, alloc_size);
|
||||||
|
ERR_FAIL_COND_V(!_ptrnew, ERR_OUT_OF_MEMORY);
|
||||||
|
new (_ptrnew - 2) SafeNumeric<uint32_t>(rc); // refcount
|
||||||
|
|
||||||
|
_ptr = (T *)(_ptrnew);
|
||||||
|
}
|
||||||
|
|
||||||
|
*_get_size() = p_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
int CowData<T>::find(const T &p_val, int p_from) const {
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (p_from < 0 || size() == 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = p_from; i < size(); i++) {
|
||||||
|
if (get(i) == p_val) {
|
||||||
|
ret = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CowData<T>::_ref(const CowData *p_from) {
|
||||||
|
_ref(*p_from);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void CowData<T>::_ref(const CowData &p_from) {
|
||||||
|
if (_ptr == p_from._ptr) {
|
||||||
|
return; // self assign, do nothing.
|
||||||
|
}
|
||||||
|
|
||||||
|
_unref(_ptr);
|
||||||
|
_ptr = nullptr;
|
||||||
|
|
||||||
|
if (!p_from._ptr) {
|
||||||
|
return; // nothing to do
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_from._get_refcount()->conditional_increment() > 0) { // could reference
|
||||||
|
_ptr = p_from._ptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
CowData<T>::~CowData() {
|
||||||
|
_unref(_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(__GNUC__) && !defined(__clang__)
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! COWDATA_HPP
|
|
@ -0,0 +1,557 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* hash_map.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 HASH_MAP_HPP
|
||||||
|
#define HASH_MAP_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/templates/hashfuncs.hpp>
|
||||||
|
#include <godot_cpp/templates/list.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class HashMap
|
||||||
|
*
|
||||||
|
* Implementation of a standard Hashing HashMap, for quick lookups of Data associated with a Key.
|
||||||
|
* The implementation provides hashers for the default types, if you need a special kind of hasher, provide
|
||||||
|
* your own.
|
||||||
|
* @param TKey Key, search is based on it, needs to be hasheable. It is unique in this container.
|
||||||
|
* @param TData Data, data associated with the key
|
||||||
|
* @param Hasher Hasher object, needs to provide a valid static hash function for TKey
|
||||||
|
* @param Comparator comparator object, needs to be able to safely compare two TKey values.
|
||||||
|
* It needs to ensure that x == x for any items inserted in the map. Bear in mind that nan != nan when implementing an equality check.
|
||||||
|
* @param MIN_HASH_TABLE_POWER Miminum size of the hash table, as a power of two. You rarely need to change this parameter.
|
||||||
|
* @param RELATIONSHIP Relationship at which the hash table is resized. if amount of elements is RELATIONSHIP
|
||||||
|
* times bigger than the hash table, table is resized to solve this condition. if RELATIONSHIP is zero, table is always MIN_HASH_TABLE_POWER.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class TKey, class TData, class Hasher = HashMapHasherDefault, class Comparator = HashMapComparatorDefault<TKey>, uint8_t MIN_HASH_TABLE_POWER = 3, uint8_t RELATIONSHIP = 8>
|
||||||
|
class HashMap {
|
||||||
|
public:
|
||||||
|
struct Pair {
|
||||||
|
TKey key;
|
||||||
|
TData data;
|
||||||
|
|
||||||
|
Pair(const TKey &p_key) :
|
||||||
|
key(p_key),
|
||||||
|
data() {}
|
||||||
|
Pair(const TKey &p_key, const TData &p_data) :
|
||||||
|
key(p_key),
|
||||||
|
data(p_data) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Element {
|
||||||
|
private:
|
||||||
|
friend class HashMap;
|
||||||
|
|
||||||
|
uint32_t hash = 0;
|
||||||
|
Element *next = nullptr;
|
||||||
|
Element() {}
|
||||||
|
Pair pair;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const TKey &key() const {
|
||||||
|
return pair.key;
|
||||||
|
}
|
||||||
|
|
||||||
|
TData &value() {
|
||||||
|
return pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
const TData &value() const {
|
||||||
|
return pair.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element(const TKey &p_key) :
|
||||||
|
pair(p_key) {}
|
||||||
|
Element(const Element &p_other) :
|
||||||
|
hash(p_other.hash),
|
||||||
|
pair(p_other.pair.key, p_other.pair.data) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element **hash_table = nullptr;
|
||||||
|
uint8_t hash_table_power = 0;
|
||||||
|
uint32_t elements = 0;
|
||||||
|
|
||||||
|
void make_hash_table() {
|
||||||
|
ERR_FAIL_COND(hash_table);
|
||||||
|
|
||||||
|
hash_table = memnew_arr(Element *, (1 << MIN_HASH_TABLE_POWER));
|
||||||
|
|
||||||
|
hash_table_power = MIN_HASH_TABLE_POWER;
|
||||||
|
elements = 0;
|
||||||
|
for (int i = 0; i < (1 << MIN_HASH_TABLE_POWER); i++) {
|
||||||
|
hash_table[i] = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase_hash_table() {
|
||||||
|
ERR_FAIL_COND_MSG(elements, "Cannot erase hash table if there are still elements inside.");
|
||||||
|
|
||||||
|
memdelete_arr(hash_table);
|
||||||
|
hash_table = nullptr;
|
||||||
|
hash_table_power = 0;
|
||||||
|
elements = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void check_hash_table() {
|
||||||
|
int new_hash_table_power = -1;
|
||||||
|
|
||||||
|
if ((int)elements > ((1 << hash_table_power) * RELATIONSHIP)) {
|
||||||
|
/* rehash up */
|
||||||
|
new_hash_table_power = hash_table_power + 1;
|
||||||
|
|
||||||
|
while ((int)elements > ((1 << new_hash_table_power) * RELATIONSHIP)) {
|
||||||
|
new_hash_table_power++;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if ((hash_table_power > (int)MIN_HASH_TABLE_POWER) && ((int)elements < ((1 << (hash_table_power - 1)) * RELATIONSHIP))) {
|
||||||
|
/* rehash down */
|
||||||
|
new_hash_table_power = hash_table_power - 1;
|
||||||
|
|
||||||
|
while ((int)elements < ((1 << (new_hash_table_power - 1)) * RELATIONSHIP)) {
|
||||||
|
new_hash_table_power--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_hash_table_power < (int)MIN_HASH_TABLE_POWER) {
|
||||||
|
new_hash_table_power = MIN_HASH_TABLE_POWER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_hash_table_power == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element **new_hash_table = memnew_arr(Element *, ((uint64_t)1 << new_hash_table_power));
|
||||||
|
ERR_FAIL_COND_MSG(!new_hash_table, "Out of memory.");
|
||||||
|
|
||||||
|
for (int i = 0; i < (1 << new_hash_table_power); i++) {
|
||||||
|
new_hash_table[i] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hash_table) {
|
||||||
|
for (int i = 0; i < (1 << hash_table_power); i++) {
|
||||||
|
while (hash_table[i]) {
|
||||||
|
Element *se = hash_table[i];
|
||||||
|
hash_table[i] = se->next;
|
||||||
|
int new_pos = se->hash & ((1 << new_hash_table_power) - 1);
|
||||||
|
se->next = new_hash_table[new_pos];
|
||||||
|
new_hash_table[new_pos] = se;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_arr(hash_table);
|
||||||
|
}
|
||||||
|
hash_table = new_hash_table;
|
||||||
|
hash_table_power = new_hash_table_power;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* I want to have only one function.. */
|
||||||
|
_FORCE_INLINE_ const Element *get_element(const TKey &p_key) const {
|
||||||
|
uint32_t hash = Hasher::hash(p_key);
|
||||||
|
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||||
|
|
||||||
|
Element *e = hash_table[index];
|
||||||
|
|
||||||
|
while (e) {
|
||||||
|
/* checking hash first avoids comparing key, which may take longer */
|
||||||
|
if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
|
||||||
|
/* the pair exists in this hashtable, so just update data */
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *create_element(const TKey &p_key) {
|
||||||
|
/* if element doesn't exist, create it */
|
||||||
|
Element *e = memnew(Element(p_key));
|
||||||
|
ERR_FAIL_COND_V_MSG(!e, nullptr, "Out of memory.");
|
||||||
|
uint32_t hash = Hasher::hash(p_key);
|
||||||
|
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||||
|
e->next = hash_table[index];
|
||||||
|
e->hash = hash;
|
||||||
|
|
||||||
|
hash_table[index] = e;
|
||||||
|
elements++;
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy_from(const HashMap &p_t) {
|
||||||
|
if (&p_t == this) {
|
||||||
|
return; /* much less bother with that */
|
||||||
|
}
|
||||||
|
|
||||||
|
clear();
|
||||||
|
|
||||||
|
if (!p_t.hash_table || p_t.hash_table_power == 0) {
|
||||||
|
return; /* not copying from empty table */
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_table = memnew_arr(Element *, (uint64_t)1 << p_t.hash_table_power);
|
||||||
|
hash_table_power = p_t.hash_table_power;
|
||||||
|
elements = p_t.elements;
|
||||||
|
|
||||||
|
for (int i = 0; i < (1 << p_t.hash_table_power); i++) {
|
||||||
|
hash_table[i] = nullptr;
|
||||||
|
|
||||||
|
const Element *e = p_t.hash_table[i];
|
||||||
|
|
||||||
|
while (e) {
|
||||||
|
Element *le = memnew(Element(*e)); /* local element */
|
||||||
|
|
||||||
|
/* add to list and reassign pointers */
|
||||||
|
le->next = hash_table[i];
|
||||||
|
hash_table[i] = le;
|
||||||
|
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
Element *set(const TKey &p_key, const TData &p_data) {
|
||||||
|
return set(Pair(p_key, p_data));
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *set(const Pair &p_pair) {
|
||||||
|
Element *e = nullptr;
|
||||||
|
if (!hash_table) {
|
||||||
|
make_hash_table(); // if no table, make one
|
||||||
|
} else {
|
||||||
|
e = const_cast<Element *>(get_element(p_pair.key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we made it up to here, the pair doesn't exist, create and assign */
|
||||||
|
|
||||||
|
if (!e) {
|
||||||
|
e = create_element(p_pair.key);
|
||||||
|
if (!e) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
check_hash_table(); // perform mantenience routine
|
||||||
|
}
|
||||||
|
|
||||||
|
e->pair.data = p_pair.data;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const TKey &p_key) const {
|
||||||
|
return getptr(p_key) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a key from data, return a const reference.
|
||||||
|
* WARNING: this doesn't check errors, use either getptr and check nullptr, or check
|
||||||
|
* first with has(key)
|
||||||
|
*/
|
||||||
|
|
||||||
|
const TData &get(const TKey &p_key) const {
|
||||||
|
const TData *res = getptr(p_key);
|
||||||
|
CRASH_COND_MSG(!res, "Map key not found.");
|
||||||
|
return *res;
|
||||||
|
}
|
||||||
|
|
||||||
|
TData &get(const TKey &p_key) {
|
||||||
|
TData *res = getptr(p_key);
|
||||||
|
CRASH_COND_MSG(!res, "Map key not found.");
|
||||||
|
return *res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as get, except it can return nullptr when item was not found.
|
||||||
|
* This is mainly used for speed purposes.
|
||||||
|
*/
|
||||||
|
|
||||||
|
_FORCE_INLINE_ TData *getptr(const TKey &p_key) {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = const_cast<Element *>(get_element(p_key));
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
return &e->pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ const TData *getptr(const TKey &p_key) const {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Element *e = const_cast<Element *>(get_element(p_key));
|
||||||
|
|
||||||
|
if (e) {
|
||||||
|
return &e->pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as get, except it can return nullptr when item was not found.
|
||||||
|
* This version is custom, will take a hash and a custom key (that should support operator==()
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
_FORCE_INLINE_ TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash = p_custom_hash;
|
||||||
|
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||||
|
|
||||||
|
Element *e = hash_table[index];
|
||||||
|
|
||||||
|
while (e) {
|
||||||
|
/* checking hash first avoids comparing key, which may take longer */
|
||||||
|
if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
|
||||||
|
/* the pair exists in this hashtable, so just update data */
|
||||||
|
return &e->pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
_FORCE_INLINE_ const TData *custom_getptr(C p_custom_key, uint32_t p_custom_hash) const {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash = p_custom_hash;
|
||||||
|
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||||
|
|
||||||
|
const Element *e = hash_table[index];
|
||||||
|
|
||||||
|
while (e) {
|
||||||
|
/* checking hash first avoids comparing key, which may take longer */
|
||||||
|
if (e->hash == hash && Comparator::compare(e->pair.key, p_custom_key)) {
|
||||||
|
/* the pair exists in this hashtable, so just update data */
|
||||||
|
return &e->pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Erase an item, return true if erasing was successful
|
||||||
|
*/
|
||||||
|
|
||||||
|
bool erase(const TKey &p_key) {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t hash = Hasher::hash(p_key);
|
||||||
|
uint32_t index = hash & ((1 << hash_table_power) - 1);
|
||||||
|
|
||||||
|
Element *e = hash_table[index];
|
||||||
|
Element *p = nullptr;
|
||||||
|
while (e) {
|
||||||
|
/* checking hash first avoids comparing key, which may take longer */
|
||||||
|
if (e->hash == hash && Comparator::compare(e->pair.key, p_key)) {
|
||||||
|
if (p) {
|
||||||
|
p->next = e->next;
|
||||||
|
} else {
|
||||||
|
// begin of list
|
||||||
|
hash_table[index] = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete(e);
|
||||||
|
elements--;
|
||||||
|
|
||||||
|
if (elements == 0) {
|
||||||
|
erase_hash_table();
|
||||||
|
} else {
|
||||||
|
check_hash_table();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = e;
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const TData &operator[](const TKey &p_key) const { // constref
|
||||||
|
|
||||||
|
return get(p_key);
|
||||||
|
}
|
||||||
|
inline TData &operator[](const TKey &p_key) { // assignment
|
||||||
|
|
||||||
|
Element *e = nullptr;
|
||||||
|
if (!hash_table) {
|
||||||
|
make_hash_table(); // if no table, make one
|
||||||
|
} else {
|
||||||
|
e = const_cast<Element *>(get_element(p_key));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if we made it up to here, the pair doesn't exist, create */
|
||||||
|
if (!e) {
|
||||||
|
e = create_element(p_key);
|
||||||
|
CRASH_COND(!e);
|
||||||
|
check_hash_table(); // perform mantenience routine
|
||||||
|
}
|
||||||
|
|
||||||
|
return e->pair.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next key to p_key, and the first key if p_key is null.
|
||||||
|
* Returns a pointer to the next key if found, nullptr otherwise.
|
||||||
|
* Adding/Removing elements while iterating will, of course, have unexpected results, don't do it.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* const TKey *k=nullptr;
|
||||||
|
*
|
||||||
|
* while( (k=table.next(k)) ) {
|
||||||
|
*
|
||||||
|
* print( *k );
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
const TKey *next(const TKey *p_key) const {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!p_key) { /* get the first key */
|
||||||
|
|
||||||
|
for (int i = 0; i < (1 << hash_table_power); i++) {
|
||||||
|
if (hash_table[i]) {
|
||||||
|
return &hash_table[i]->pair.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else { /* get the next key */
|
||||||
|
|
||||||
|
const Element *e = get_element(*p_key);
|
||||||
|
ERR_FAIL_COND_V_MSG(!e, nullptr, "Invalid key supplied.");
|
||||||
|
if (e->next) {
|
||||||
|
/* if there is a "next" in the list, return that */
|
||||||
|
return &e->next->pair.key;
|
||||||
|
} else {
|
||||||
|
/* go to next elements */
|
||||||
|
uint32_t index = e->hash & ((1 << hash_table_power) - 1);
|
||||||
|
index++;
|
||||||
|
for (int i = index; i < (1 << hash_table_power); i++) {
|
||||||
|
if (hash_table[i]) {
|
||||||
|
return &hash_table[i]->pair.key;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nothing found, was at end */
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr; /* nothing found */
|
||||||
|
}
|
||||||
|
|
||||||
|
inline unsigned int size() const {
|
||||||
|
return elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_empty() const {
|
||||||
|
return elements == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
/* clean up */
|
||||||
|
if (hash_table) {
|
||||||
|
for (int i = 0; i < (1 << hash_table_power); i++) {
|
||||||
|
while (hash_table[i]) {
|
||||||
|
Element *e = hash_table[i];
|
||||||
|
hash_table[i] = e->next;
|
||||||
|
memdelete(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_arr(hash_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
hash_table = nullptr;
|
||||||
|
hash_table_power = 0;
|
||||||
|
elements = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const HashMap &p_table) {
|
||||||
|
copy_from(p_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_key_list(List<TKey> *r_keys) const {
|
||||||
|
if (unlikely(!hash_table)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < (1 << hash_table_power); i++) {
|
||||||
|
Element *e = hash_table[i];
|
||||||
|
while (e) {
|
||||||
|
r_keys->push_back(e->pair.key);
|
||||||
|
e = e->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
HashMap() {}
|
||||||
|
|
||||||
|
HashMap(const HashMap &p_table) {
|
||||||
|
copy_from(p_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
~HashMap() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! HASH_MAP_HPP
|
|
@ -0,0 +1,191 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* hashfuncs.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 HASHFUNCS_HPP
|
||||||
|
#define HASHFUNCS_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/math.hpp>
|
||||||
|
#include <godot_cpp/variant/rid.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashing functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DJB2 Hash function
|
||||||
|
* @param C String
|
||||||
|
* @return 32-bits hashcode
|
||||||
|
*/
|
||||||
|
static inline uint32_t hash_djb2(const char *p_cstr) {
|
||||||
|
const unsigned char *chr = (const unsigned char *)p_cstr;
|
||||||
|
uint32_t hash = 5381;
|
||||||
|
uint32_t c;
|
||||||
|
|
||||||
|
while ((c = *chr++)) {
|
||||||
|
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t hash_djb2_buffer(const uint8_t *p_buff, int p_len, uint32_t p_prev = 5381) {
|
||||||
|
uint32_t hash = p_prev;
|
||||||
|
|
||||||
|
for (int i = 0; i < p_len; i++) {
|
||||||
|
hash = ((hash << 5) + hash) + p_buff[i]; /* hash * 33 + c */
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t hash_djb2_one_32(uint32_t p_in, uint32_t p_prev = 5381) {
|
||||||
|
return ((p_prev << 5) + p_prev) + p_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thomas Wang's 64-bit to 32-bit Hash function:
|
||||||
|
* https://web.archive.org/web/20071223173210/https:/www.concentric.net/~Ttwang/tech/inthash.htm
|
||||||
|
*
|
||||||
|
* @param p_int - 64-bit unsigned integer key to be hashed
|
||||||
|
* @return unsigned 32-bit value representing hashcode
|
||||||
|
*/
|
||||||
|
static inline uint32_t hash_one_uint64(const uint64_t p_int) {
|
||||||
|
uint64_t v = p_int;
|
||||||
|
v = (~v) + (v << 18); // v = (v << 18) - v - 1;
|
||||||
|
v = v ^ (v >> 31);
|
||||||
|
v = v * 21; // v = (v + (v << 2)) + (v << 4);
|
||||||
|
v = v ^ (v >> 11);
|
||||||
|
v = v + (v << 6);
|
||||||
|
v = v ^ (v >> 22);
|
||||||
|
return uint32_t(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint32_t hash_djb2_one_float(double p_in, uint32_t p_prev = 5381) {
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
uint64_t i;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
// Normalize +/- 0.0 and NaN values so they hash the same.
|
||||||
|
if (p_in == 0.0f) {
|
||||||
|
u.d = 0.0;
|
||||||
|
} else if (std::isnan(p_in)) {
|
||||||
|
u.d = NAN;
|
||||||
|
} else {
|
||||||
|
u.d = p_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((p_prev << 5) + p_prev) + hash_one_uint64(u.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static inline uint32_t make_uint32_t(T p_in) {
|
||||||
|
union {
|
||||||
|
T t;
|
||||||
|
uint32_t _u32;
|
||||||
|
} _u;
|
||||||
|
_u._u32 = 0;
|
||||||
|
_u.t = p_in;
|
||||||
|
return _u._u32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t hash_djb2_one_float_64(double p_in, uint64_t p_prev = 5381) {
|
||||||
|
union {
|
||||||
|
double d;
|
||||||
|
uint64_t i;
|
||||||
|
} u;
|
||||||
|
|
||||||
|
// Normalize +/- 0.0 and NaN values so they hash the same.
|
||||||
|
if (p_in == 0.0f) {
|
||||||
|
u.d = 0.0;
|
||||||
|
} else if (std::isnan(p_in)) {
|
||||||
|
u.d = NAN;
|
||||||
|
} else {
|
||||||
|
u.d = p_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((p_prev << 5) + p_prev) + u.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint64_t hash_djb2_one_64(uint64_t p_in, uint64_t p_prev = 5381) {
|
||||||
|
return ((p_prev << 5) + p_prev) + p_in;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
static inline uint64_t make_uint64_t(T p_in) {
|
||||||
|
union {
|
||||||
|
T t;
|
||||||
|
uint64_t _u64;
|
||||||
|
} _u;
|
||||||
|
_u._u64 = 0; // in case p_in is smaller
|
||||||
|
|
||||||
|
_u.t = p_in;
|
||||||
|
return _u._u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct HashMapHasherDefault {
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const char *p_cstr) { return hash_djb2(p_cstr); }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const uint64_t p_int) { return hash_one_uint64(p_int); }
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const int64_t p_int) { return hash(uint64_t(p_int)); }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const float p_float) { return hash_djb2_one_float(p_float); }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const double p_double) { return hash_djb2_one_float(p_double); }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const uint32_t p_int) { return p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const int32_t p_int) { return (uint32_t)p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const uint16_t p_int) { return p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const int16_t p_int) { return (uint32_t)p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const uint8_t p_int) { return p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const int8_t p_int) { return (uint32_t)p_int; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const wchar_t p_wchar) { return (uint32_t)p_wchar; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const char16_t p_uchar) { return (uint32_t)p_uchar; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const char32_t p_uchar) { return (uint32_t)p_uchar; }
|
||||||
|
static _FORCE_INLINE_ uint32_t hash(const RID &p_rid) { return hash_one_uint64(p_rid.get_id()); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct HashMapComparatorDefault {
|
||||||
|
static bool compare(const T &p_lhs, const T &p_rhs) {
|
||||||
|
return p_lhs == p_rhs;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compare(const float &p_lhs, const float &p_rhs) {
|
||||||
|
return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool compare(const double &p_lhs, const double &p_rhs) {
|
||||||
|
return (p_lhs == p_rhs) || (std::isnan(p_lhs) && std::isnan(p_rhs));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! HASHFUNCS_HPP
|
|
@ -0,0 +1,769 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* list.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 LIST_HPP
|
||||||
|
#define LIST_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/templates/sort_array.hpp>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic Templatized Linked List Implementation.
|
||||||
|
* The implementation differs from the STL one because
|
||||||
|
* a compatible preallocated linked list can be written
|
||||||
|
* using the same API, or features such as erasing an element
|
||||||
|
* from the iterator.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T, class A = DefaultAllocator>
|
||||||
|
class List {
|
||||||
|
struct _Data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Element {
|
||||||
|
private:
|
||||||
|
friend class List<T, A>;
|
||||||
|
|
||||||
|
T value;
|
||||||
|
Element *next_ptr = nullptr;
|
||||||
|
Element *prev_ptr = nullptr;
|
||||||
|
_Data *data = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Get NEXT Element iterator, for constant lists.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const Element *next() const {
|
||||||
|
return next_ptr;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get NEXT Element iterator,
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ Element *next() {
|
||||||
|
return next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get PREV Element iterator, for constant lists.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const Element *prev() const {
|
||||||
|
return prev_ptr;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Get PREV Element iterator,
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ Element *prev() {
|
||||||
|
return prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* * operator, for using as *iterator, when iterators are defined on stack.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const T &operator*() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const T *operator->() const {
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* * operator, for using as *iterator, when iterators are defined on stack,
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ T &operator*() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* operator->, for using as iterator->, when iterators are defined on stack, for constant lists.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ T *operator->() {
|
||||||
|
return &value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the value stored in this element.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ T &get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* get the value stored in this element, for constant lists
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const T &get() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* set the value stored in this element.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ void set(const T &p_value) {
|
||||||
|
value = (T &)p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase() {
|
||||||
|
data->erase(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Element() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef T ValueType;
|
||||||
|
|
||||||
|
struct Iterator {
|
||||||
|
_FORCE_INLINE_ T &operator*() const {
|
||||||
|
return E->get();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ T *operator->() const { return &E->get(); }
|
||||||
|
_FORCE_INLINE_ Iterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
Iterator(Element *p_E) { E = p_E; }
|
||||||
|
Iterator() {}
|
||||||
|
Iterator(const Iterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstIterator {
|
||||||
|
_FORCE_INLINE_ const T &operator*() const {
|
||||||
|
return E->get();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ const T *operator->() const { return &E->get(); }
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; }
|
||||||
|
_FORCE_INLINE_ ConstIterator() {}
|
||||||
|
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Iterator begin() {
|
||||||
|
return Iterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator end() {
|
||||||
|
return Iterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ Iterator find(const K &p_key) {
|
||||||
|
return Iterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_FORCE_INLINE_ ConstIterator begin() const {
|
||||||
|
return ConstIterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator end() const {
|
||||||
|
return ConstIterator(nullptr);
|
||||||
|
}
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ ConstIterator find(const K &p_key) const {
|
||||||
|
return ConstIterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
struct _Data {
|
||||||
|
Element *first = nullptr;
|
||||||
|
Element *last = nullptr;
|
||||||
|
int size_cache = 0;
|
||||||
|
|
||||||
|
bool erase(const Element *p_I) {
|
||||||
|
ERR_FAIL_COND_V(!p_I, false);
|
||||||
|
ERR_FAIL_COND_V(p_I->data != this, false);
|
||||||
|
|
||||||
|
if (first == p_I) {
|
||||||
|
first = p_I->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (last == p_I) {
|
||||||
|
last = p_I->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_I->prev_ptr) {
|
||||||
|
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_I->next_ptr) {
|
||||||
|
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_allocator<Element, A>(const_cast<Element *>(p_I));
|
||||||
|
size_cache--;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_Data *_data = nullptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* return a const iterator to the beginning of the list.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const Element *front() const {
|
||||||
|
return _data ? _data->first : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return an iterator to the beginning of the list.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ Element *front() {
|
||||||
|
return _data ? _data->first : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return a const iterator to the last member of the list.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ const Element *back() const {
|
||||||
|
return _data ? _data->last : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return an iterator to the last member of the list.
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ Element *back() {
|
||||||
|
return _data ? _data->last : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store a new element at the end of the list
|
||||||
|
*/
|
||||||
|
Element *push_back(const T &value) {
|
||||||
|
if (!_data) {
|
||||||
|
_data = memnew_allocator(_Data, A);
|
||||||
|
_data->first = nullptr;
|
||||||
|
_data->last = nullptr;
|
||||||
|
_data->size_cache = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *n = memnew_allocator(Element, A);
|
||||||
|
n->value = (T &)value;
|
||||||
|
|
||||||
|
n->prev_ptr = _data->last;
|
||||||
|
n->next_ptr = nullptr;
|
||||||
|
n->data = _data;
|
||||||
|
|
||||||
|
if (_data->last) {
|
||||||
|
_data->last->next_ptr = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->last = n;
|
||||||
|
|
||||||
|
if (!_data->first) {
|
||||||
|
_data->first = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->size_cache++;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back() {
|
||||||
|
if (_data && _data->last) {
|
||||||
|
erase(_data->last);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* store a new element at the beginning of the list
|
||||||
|
*/
|
||||||
|
Element *push_front(const T &value) {
|
||||||
|
if (!_data) {
|
||||||
|
_data = memnew_allocator(_Data, A);
|
||||||
|
_data->first = nullptr;
|
||||||
|
_data->last = nullptr;
|
||||||
|
_data->size_cache = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *n = memnew_allocator(Element, A);
|
||||||
|
n->value = (T &)value;
|
||||||
|
n->prev_ptr = nullptr;
|
||||||
|
n->next_ptr = _data->first;
|
||||||
|
n->data = _data;
|
||||||
|
|
||||||
|
if (_data->first) {
|
||||||
|
_data->first->prev_ptr = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->first = n;
|
||||||
|
|
||||||
|
if (!_data->last) {
|
||||||
|
_data->last = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->size_cache++;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_front() {
|
||||||
|
if (_data && _data->first) {
|
||||||
|
erase(_data->first);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *insert_after(Element *p_element, const T &p_value) {
|
||||||
|
CRASH_COND(p_element && (!_data || p_element->data != _data));
|
||||||
|
|
||||||
|
if (!p_element) {
|
||||||
|
return push_back(p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *n = memnew_allocator(Element, A);
|
||||||
|
n->value = (T &)p_value;
|
||||||
|
n->prev_ptr = p_element;
|
||||||
|
n->next_ptr = p_element->next_ptr;
|
||||||
|
n->data = _data;
|
||||||
|
|
||||||
|
if (!p_element->next_ptr) {
|
||||||
|
_data->last = n;
|
||||||
|
} else {
|
||||||
|
p_element->next_ptr->prev_ptr = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_element->next_ptr = n;
|
||||||
|
|
||||||
|
_data->size_cache++;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *insert_before(Element *p_element, const T &p_value) {
|
||||||
|
CRASH_COND(p_element && (!_data || p_element->data != _data));
|
||||||
|
|
||||||
|
if (!p_element) {
|
||||||
|
return push_back(p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *n = memnew_allocator(Element, A);
|
||||||
|
n->value = (T &)p_value;
|
||||||
|
n->prev_ptr = p_element->prev_ptr;
|
||||||
|
n->next_ptr = p_element;
|
||||||
|
n->data = _data;
|
||||||
|
|
||||||
|
if (!p_element->prev_ptr) {
|
||||||
|
_data->first = n;
|
||||||
|
} else {
|
||||||
|
p_element->prev_ptr->next_ptr = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_element->prev_ptr = n;
|
||||||
|
|
||||||
|
_data->size_cache++;
|
||||||
|
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* find an element in the list,
|
||||||
|
*/
|
||||||
|
template <class T_v>
|
||||||
|
Element *find(const T_v &p_val) {
|
||||||
|
Element *it = front();
|
||||||
|
while (it) {
|
||||||
|
if (it->value == p_val) {
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
it = it->next();
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase an element in the list, by iterator pointing to it. Return true if it was found/erased.
|
||||||
|
*/
|
||||||
|
bool erase(const Element *p_I) {
|
||||||
|
if (_data && p_I) {
|
||||||
|
bool ret = _data->erase(p_I);
|
||||||
|
|
||||||
|
if (_data->size_cache == 0) {
|
||||||
|
memdelete_allocator<_Data, A>(_data);
|
||||||
|
_data = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* erase the first element in the list, that contains value
|
||||||
|
*/
|
||||||
|
bool erase(const T &value) {
|
||||||
|
Element *I = find(value);
|
||||||
|
return erase(I);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return whether the list is empty
|
||||||
|
*/
|
||||||
|
_FORCE_INLINE_ bool is_empty() const {
|
||||||
|
return (!_data || !_data->size_cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* clear the list
|
||||||
|
*/
|
||||||
|
void clear() {
|
||||||
|
while (front()) {
|
||||||
|
erase(front());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int size() const {
|
||||||
|
return _data ? _data->size_cache : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void swap(Element *p_A, Element *p_B) {
|
||||||
|
ERR_FAIL_COND(!p_A || !p_B);
|
||||||
|
ERR_FAIL_COND(p_A->data != _data);
|
||||||
|
ERR_FAIL_COND(p_B->data != _data);
|
||||||
|
|
||||||
|
if (p_A == p_B) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Element *A_prev = p_A->prev_ptr;
|
||||||
|
Element *A_next = p_A->next_ptr;
|
||||||
|
Element *B_prev = p_B->prev_ptr;
|
||||||
|
Element *B_next = p_B->next_ptr;
|
||||||
|
|
||||||
|
if (A_prev) {
|
||||||
|
A_prev->next_ptr = p_B;
|
||||||
|
} else {
|
||||||
|
_data->first = p_B;
|
||||||
|
}
|
||||||
|
if (B_prev) {
|
||||||
|
B_prev->next_ptr = p_A;
|
||||||
|
} else {
|
||||||
|
_data->first = p_A;
|
||||||
|
}
|
||||||
|
if (A_next) {
|
||||||
|
A_next->prev_ptr = p_B;
|
||||||
|
} else {
|
||||||
|
_data->last = p_B;
|
||||||
|
}
|
||||||
|
if (B_next) {
|
||||||
|
B_next->prev_ptr = p_A;
|
||||||
|
} else {
|
||||||
|
_data->last = p_A;
|
||||||
|
}
|
||||||
|
p_A->prev_ptr = A_next == p_B ? p_B : B_prev;
|
||||||
|
p_A->next_ptr = B_next == p_A ? p_B : B_next;
|
||||||
|
p_B->prev_ptr = B_next == p_A ? p_A : A_prev;
|
||||||
|
p_B->next_ptr = A_next == p_B ? p_A : A_next;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* copy the list
|
||||||
|
*/
|
||||||
|
void operator=(const List &p_list) {
|
||||||
|
clear();
|
||||||
|
const Element *it = p_list.front();
|
||||||
|
while (it) {
|
||||||
|
push_back(it->get());
|
||||||
|
it = it->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
T &operator[](int p_index) {
|
||||||
|
CRASH_BAD_INDEX(p_index, size());
|
||||||
|
|
||||||
|
Element *I = front();
|
||||||
|
int c = 0;
|
||||||
|
while (c < p_index) {
|
||||||
|
I = I->next();
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &operator[](int p_index) const {
|
||||||
|
CRASH_BAD_INDEX(p_index, size());
|
||||||
|
|
||||||
|
const Element *I = front();
|
||||||
|
int c = 0;
|
||||||
|
while (c < p_index) {
|
||||||
|
I = I->next();
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return I->get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_to_back(Element *p_I) {
|
||||||
|
ERR_FAIL_COND(p_I->data != _data);
|
||||||
|
if (!p_I->next_ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data->first == p_I) {
|
||||||
|
_data->first = p_I->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data->last == p_I) {
|
||||||
|
_data->last = p_I->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_I->prev_ptr) {
|
||||||
|
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||||
|
|
||||||
|
_data->last->next_ptr = p_I;
|
||||||
|
p_I->prev_ptr = _data->last;
|
||||||
|
p_I->next_ptr = nullptr;
|
||||||
|
_data->last = p_I;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reverse() {
|
||||||
|
int s = size() / 2;
|
||||||
|
Element *F = front();
|
||||||
|
Element *B = back();
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
SWAP(F->value, B->value);
|
||||||
|
F = F->next();
|
||||||
|
B = B->prev();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_to_front(Element *p_I) {
|
||||||
|
ERR_FAIL_COND(p_I->data != _data);
|
||||||
|
if (!p_I->prev_ptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data->first == p_I) {
|
||||||
|
_data->first = p_I->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_data->last == p_I) {
|
||||||
|
_data->last = p_I->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_I->prev_ptr->next_ptr = p_I->next_ptr;
|
||||||
|
|
||||||
|
if (p_I->next_ptr) {
|
||||||
|
p_I->next_ptr->prev_ptr = p_I->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data->first->prev_ptr = p_I;
|
||||||
|
p_I->next_ptr = _data->first;
|
||||||
|
p_I->prev_ptr = nullptr;
|
||||||
|
_data->first = p_I;
|
||||||
|
}
|
||||||
|
|
||||||
|
void move_before(Element *value, Element *where) {
|
||||||
|
if (value->prev_ptr) {
|
||||||
|
value->prev_ptr->next_ptr = value->next_ptr;
|
||||||
|
} else {
|
||||||
|
_data->first = value->next_ptr;
|
||||||
|
}
|
||||||
|
if (value->next_ptr) {
|
||||||
|
value->next_ptr->prev_ptr = value->prev_ptr;
|
||||||
|
} else {
|
||||||
|
_data->last = value->prev_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
value->next_ptr = where;
|
||||||
|
if (!where) {
|
||||||
|
value->prev_ptr = _data->last;
|
||||||
|
_data->last = value;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
value->prev_ptr = where->prev_ptr;
|
||||||
|
|
||||||
|
if (where->prev_ptr) {
|
||||||
|
where->prev_ptr->next_ptr = value;
|
||||||
|
} else {
|
||||||
|
_data->first = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
where->prev_ptr = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* simple insertion sort
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sort() {
|
||||||
|
sort_custom<Comparator<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
void sort_custom_inplace() {
|
||||||
|
if (size() < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *from = front();
|
||||||
|
Element *current = from;
|
||||||
|
Element *to = from;
|
||||||
|
|
||||||
|
while (current) {
|
||||||
|
Element *next = current->next_ptr;
|
||||||
|
|
||||||
|
if (from != current) {
|
||||||
|
current->prev_ptr = nullptr;
|
||||||
|
current->next_ptr = from;
|
||||||
|
|
||||||
|
Element *find = from;
|
||||||
|
C less;
|
||||||
|
while (find && less(find->value, current->value)) {
|
||||||
|
current->prev_ptr = find;
|
||||||
|
current->next_ptr = find->next_ptr;
|
||||||
|
find = find->next_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current->prev_ptr) {
|
||||||
|
current->prev_ptr->next_ptr = current;
|
||||||
|
} else {
|
||||||
|
from = current;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (current->next_ptr) {
|
||||||
|
current->next_ptr->prev_ptr = current;
|
||||||
|
} else {
|
||||||
|
to = current;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
current->prev_ptr = nullptr;
|
||||||
|
current->next_ptr = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
current = next;
|
||||||
|
}
|
||||||
|
_data->first = from;
|
||||||
|
_data->last = to;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
struct AuxiliaryComparator {
|
||||||
|
C compare;
|
||||||
|
_FORCE_INLINE_ bool operator()(const Element *a, const Element *b) const {
|
||||||
|
return compare(a->value, b->value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
void sort_custom() {
|
||||||
|
// this version uses auxiliary memory for speed.
|
||||||
|
// if you don't want to use auxiliary memory, use the in_place version
|
||||||
|
|
||||||
|
int s = size();
|
||||||
|
if (s < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element **aux_buffer = memnew_arr(Element *, s);
|
||||||
|
|
||||||
|
int idx = 0;
|
||||||
|
for (Element *E = front(); E; E = E->next_ptr) {
|
||||||
|
aux_buffer[idx] = E;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SortArray<Element *, AuxiliaryComparator<C>> sort;
|
||||||
|
sort.sort(aux_buffer, s);
|
||||||
|
|
||||||
|
_data->first = aux_buffer[0];
|
||||||
|
aux_buffer[0]->prev_ptr = nullptr;
|
||||||
|
aux_buffer[0]->next_ptr = aux_buffer[1];
|
||||||
|
|
||||||
|
_data->last = aux_buffer[s - 1];
|
||||||
|
aux_buffer[s - 1]->prev_ptr = aux_buffer[s - 2];
|
||||||
|
aux_buffer[s - 1]->next_ptr = nullptr;
|
||||||
|
|
||||||
|
for (int i = 1; i < s - 1; i++) {
|
||||||
|
aux_buffer[i]->prev_ptr = aux_buffer[i - 1];
|
||||||
|
aux_buffer[i]->next_ptr = aux_buffer[i + 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_arr(aux_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
const void *id() const {
|
||||||
|
return (void *)_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* copy constructor for the list
|
||||||
|
*/
|
||||||
|
List(const List &p_list) {
|
||||||
|
const Element *it = p_list.front();
|
||||||
|
while (it) {
|
||||||
|
push_back(it->get());
|
||||||
|
it = it->next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List() {}
|
||||||
|
|
||||||
|
~List() {
|
||||||
|
clear();
|
||||||
|
if (_data) {
|
||||||
|
ERR_FAIL_COND(_data->size_cache);
|
||||||
|
memdelete_allocator<_Data, A>(_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! LIST_HPP
|
|
@ -0,0 +1,757 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* map.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 MAP_HPP
|
||||||
|
#define MAP_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/templates/pair.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
template <class K, class V, class C = Comparator<K>, class A = DefaultAllocator>
|
||||||
|
class Map {
|
||||||
|
enum Color {
|
||||||
|
RED,
|
||||||
|
BLACK
|
||||||
|
};
|
||||||
|
struct _Data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Element {
|
||||||
|
private:
|
||||||
|
friend class Map<K, V, C, A>;
|
||||||
|
int color = RED;
|
||||||
|
Element *right = nullptr;
|
||||||
|
Element *left = nullptr;
|
||||||
|
Element *parent = nullptr;
|
||||||
|
Element *_next = nullptr;
|
||||||
|
Element *_prev = nullptr;
|
||||||
|
KeyValue<K, V> _data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
KeyValue<K, V> &key_value() { return _data; }
|
||||||
|
const KeyValue<K, V> &key_value() const { return _data; }
|
||||||
|
|
||||||
|
const Element *next() const {
|
||||||
|
return _next;
|
||||||
|
}
|
||||||
|
Element *next() {
|
||||||
|
return _next;
|
||||||
|
}
|
||||||
|
const Element *prev() const {
|
||||||
|
return _prev;
|
||||||
|
}
|
||||||
|
Element *prev() {
|
||||||
|
return _prev;
|
||||||
|
}
|
||||||
|
const K &key() const {
|
||||||
|
return _data.key;
|
||||||
|
}
|
||||||
|
V &value() {
|
||||||
|
return _data.value;
|
||||||
|
}
|
||||||
|
const V &value() const {
|
||||||
|
return _data.value;
|
||||||
|
}
|
||||||
|
V &get() {
|
||||||
|
return _data.value;
|
||||||
|
}
|
||||||
|
const V &get() const {
|
||||||
|
return _data.value;
|
||||||
|
}
|
||||||
|
Element(const KeyValue<K, V> &p_data) :
|
||||||
|
_data(p_data) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef KeyValue<K, V> ValueType;
|
||||||
|
|
||||||
|
struct Iterator {
|
||||||
|
_FORCE_INLINE_ KeyValue<K, V> &operator*() const {
|
||||||
|
return E->key_value();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ KeyValue<K, V> *operator->() const { return &E->key_value(); }
|
||||||
|
_FORCE_INLINE_ Iterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
Iterator(Element *p_E) { E = p_E; }
|
||||||
|
Iterator() {}
|
||||||
|
Iterator(const Iterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstIterator {
|
||||||
|
_FORCE_INLINE_ const KeyValue<K, V> &operator*() const {
|
||||||
|
return E->key_value();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ const KeyValue<K, V> *operator->() const { return &E->key_value(); }
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
ConstIterator(const Element *p_E) { E = p_E; }
|
||||||
|
ConstIterator() {}
|
||||||
|
ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Iterator begin() {
|
||||||
|
return Iterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator end() {
|
||||||
|
return Iterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ Iterator find(const K &p_key) {
|
||||||
|
return Iterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
_FORCE_INLINE_ void remove(const Iterator &p_iter) {
|
||||||
|
return erase(p_iter.E);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ConstIterator begin() const {
|
||||||
|
return ConstIterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator end() const {
|
||||||
|
return ConstIterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ ConstIterator find(const K &p_key) const {
|
||||||
|
return ConstIterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
struct _Data {
|
||||||
|
Element *_root = nullptr;
|
||||||
|
Element *_nil;
|
||||||
|
int size_cache = 0;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ _Data() {
|
||||||
|
#ifdef GLOBALNIL_DISABLED
|
||||||
|
_nil = memnew_allocator(Element, A);
|
||||||
|
_nil->parent = _nil->left = _nil->right = _nil;
|
||||||
|
_nil->color = BLACK;
|
||||||
|
#else
|
||||||
|
_nil = (Element *)&_GlobalNilClass::_nil;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void _create_root() {
|
||||||
|
_root = memnew_allocator(Element(KeyValue<K, V>(K(), V())), A);
|
||||||
|
_root->parent = _root->left = _root->right = _nil;
|
||||||
|
_root->color = BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _free_root() {
|
||||||
|
if (_root) {
|
||||||
|
memdelete_allocator<Element, A>(_root);
|
||||||
|
_root = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~_Data() {
|
||||||
|
_free_root();
|
||||||
|
|
||||||
|
#ifdef GLOBALNIL_DISABLED
|
||||||
|
memdelete_allocator<Element, A>(_nil);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_Data _data;
|
||||||
|
|
||||||
|
inline void _set_color(Element *p_node, int p_color) {
|
||||||
|
ERR_FAIL_COND(p_node == _data._nil && p_color == RED);
|
||||||
|
p_node->color = p_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void _rotate_left(Element *p_node) {
|
||||||
|
Element *r = p_node->right;
|
||||||
|
p_node->right = r->left;
|
||||||
|
if (r->left != _data._nil) {
|
||||||
|
r->left->parent = p_node;
|
||||||
|
}
|
||||||
|
r->parent = p_node->parent;
|
||||||
|
if (p_node == p_node->parent->left) {
|
||||||
|
p_node->parent->left = r;
|
||||||
|
} else {
|
||||||
|
p_node->parent->right = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->left = p_node;
|
||||||
|
p_node->parent = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void _rotate_right(Element *p_node) {
|
||||||
|
Element *l = p_node->left;
|
||||||
|
p_node->left = l->right;
|
||||||
|
if (l->right != _data._nil) {
|
||||||
|
l->right->parent = p_node;
|
||||||
|
}
|
||||||
|
l->parent = p_node->parent;
|
||||||
|
if (p_node == p_node->parent->right) {
|
||||||
|
p_node->parent->right = l;
|
||||||
|
} else {
|
||||||
|
p_node->parent->left = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
l->right = p_node;
|
||||||
|
p_node->parent = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Element *_successor(Element *p_node) const {
|
||||||
|
Element *node = p_node;
|
||||||
|
|
||||||
|
if (node->right != _data._nil) {
|
||||||
|
node = node->right;
|
||||||
|
while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
while (node == node->parent->right) {
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->parent == _data._root) {
|
||||||
|
return nullptr; // No successor, as p_node = last node
|
||||||
|
}
|
||||||
|
return node->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Element *_predecessor(Element *p_node) const {
|
||||||
|
Element *node = p_node;
|
||||||
|
|
||||||
|
if (node->left != _data._nil) {
|
||||||
|
node = node->left;
|
||||||
|
while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */
|
||||||
|
node = node->right;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
while (node == node->parent->left) {
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == _data._root) {
|
||||||
|
return nullptr; // No predecessor, as p_node = first node
|
||||||
|
}
|
||||||
|
return node->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_find(const K &p_key) const {
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
if (less(p_key, node->_data.key)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->_data.key, p_key)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
return node; // found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_find_closest(const K &p_key) const {
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
Element *prev = nullptr;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
prev = node;
|
||||||
|
|
||||||
|
if (less(p_key, node->_data.key)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->_data.key, p_key)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
return node; // found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev == nullptr) {
|
||||||
|
return nullptr; // tree empty
|
||||||
|
}
|
||||||
|
|
||||||
|
if (less(p_key, prev->_data.key)) {
|
||||||
|
prev = prev->_prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _insert_rb_fix(Element *p_new_node) {
|
||||||
|
Element *node = p_new_node;
|
||||||
|
Element *nparent = node->parent;
|
||||||
|
Element *ngrand_parent;
|
||||||
|
|
||||||
|
while (nparent->color == RED) {
|
||||||
|
ngrand_parent = nparent->parent;
|
||||||
|
|
||||||
|
if (nparent == ngrand_parent->left) {
|
||||||
|
if (ngrand_parent->right->color == RED) {
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent->right, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
node = ngrand_parent;
|
||||||
|
nparent = node->parent;
|
||||||
|
} else {
|
||||||
|
if (node == nparent->right) {
|
||||||
|
_rotate_left(nparent);
|
||||||
|
node = nparent;
|
||||||
|
nparent = node->parent;
|
||||||
|
}
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
_rotate_right(ngrand_parent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ngrand_parent->left->color == RED) {
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent->left, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
node = ngrand_parent;
|
||||||
|
nparent = node->parent;
|
||||||
|
} else {
|
||||||
|
if (node == nparent->left) {
|
||||||
|
_rotate_right(nparent);
|
||||||
|
node = nparent;
|
||||||
|
nparent = node->parent;
|
||||||
|
}
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
_rotate_left(ngrand_parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_color(_data._root->left, BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_insert(const K &p_key, const V &p_value) {
|
||||||
|
Element *new_parent = _data._root;
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
new_parent = node;
|
||||||
|
|
||||||
|
if (less(p_key, node->_data.key)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->_data.key, p_key)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
node->_data.value = p_value;
|
||||||
|
return node; // Return existing node with new value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef KeyValue<K, V> KV;
|
||||||
|
Element *new_node = memnew_allocator(Element(KV(p_key, p_value)), A);
|
||||||
|
new_node->parent = new_parent;
|
||||||
|
new_node->right = _data._nil;
|
||||||
|
new_node->left = _data._nil;
|
||||||
|
|
||||||
|
// new_node->data=_data;
|
||||||
|
|
||||||
|
if (new_parent == _data._root || less(p_key, new_parent->_data.key)) {
|
||||||
|
new_parent->left = new_node;
|
||||||
|
} else {
|
||||||
|
new_parent->right = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_node->_next = _successor(new_node);
|
||||||
|
new_node->_prev = _predecessor(new_node);
|
||||||
|
if (new_node->_next) {
|
||||||
|
new_node->_next->_prev = new_node;
|
||||||
|
}
|
||||||
|
if (new_node->_prev) {
|
||||||
|
new_node->_prev->_next = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data.size_cache++;
|
||||||
|
_insert_rb_fix(new_node);
|
||||||
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _erase_fix_rb(Element *p_node) {
|
||||||
|
Element *root = _data._root->left;
|
||||||
|
Element *node = _data._nil;
|
||||||
|
Element *sibling = p_node;
|
||||||
|
Element *parent = sibling->parent;
|
||||||
|
|
||||||
|
while (node != root) { // If red node found, will exit at a break
|
||||||
|
if (sibling->color == RED) {
|
||||||
|
_set_color(sibling, BLACK);
|
||||||
|
_set_color(parent, RED);
|
||||||
|
if (sibling == parent->right) {
|
||||||
|
sibling = sibling->left;
|
||||||
|
_rotate_left(parent);
|
||||||
|
} else {
|
||||||
|
sibling = sibling->right;
|
||||||
|
_rotate_right(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) {
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
if (parent->color == RED) {
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
break;
|
||||||
|
} else { // loop: haven't found any red nodes yet
|
||||||
|
node = parent;
|
||||||
|
parent = node->parent;
|
||||||
|
sibling = (node == parent->left) ? parent->right : parent->left;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sibling == parent->right) {
|
||||||
|
if (sibling->right->color == BLACK) {
|
||||||
|
_set_color(sibling->left, BLACK);
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
_rotate_right(sibling);
|
||||||
|
sibling = sibling->parent;
|
||||||
|
}
|
||||||
|
_set_color(sibling, parent->color);
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
_set_color(sibling->right, BLACK);
|
||||||
|
_rotate_left(parent);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (sibling->left->color == BLACK) {
|
||||||
|
_set_color(sibling->right, BLACK);
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
_rotate_left(sibling);
|
||||||
|
sibling = sibling->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_color(sibling, parent->color);
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
_set_color(sibling->left, BLACK);
|
||||||
|
_rotate_right(parent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_FAIL_COND(_data._nil->color != BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _erase(Element *p_node) {
|
||||||
|
Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
|
||||||
|
Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
|
||||||
|
|
||||||
|
Element *sibling;
|
||||||
|
if (rp == rp->parent->left) {
|
||||||
|
rp->parent->left = node;
|
||||||
|
sibling = rp->parent->right;
|
||||||
|
} else {
|
||||||
|
rp->parent->right = node;
|
||||||
|
sibling = rp->parent->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->color == RED) {
|
||||||
|
node->parent = rp->parent;
|
||||||
|
_set_color(node, BLACK);
|
||||||
|
} else if (rp->color == BLACK && rp->parent != _data._root) {
|
||||||
|
_erase_fix_rb(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rp != p_node) {
|
||||||
|
ERR_FAIL_COND(rp == _data._nil);
|
||||||
|
|
||||||
|
rp->left = p_node->left;
|
||||||
|
rp->right = p_node->right;
|
||||||
|
rp->parent = p_node->parent;
|
||||||
|
rp->color = p_node->color;
|
||||||
|
if (p_node->left != _data._nil) {
|
||||||
|
p_node->left->parent = rp;
|
||||||
|
}
|
||||||
|
if (p_node->right != _data._nil) {
|
||||||
|
p_node->right->parent = rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_node == p_node->parent->left) {
|
||||||
|
p_node->parent->left = rp;
|
||||||
|
} else {
|
||||||
|
p_node->parent->right = rp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_node->_next) {
|
||||||
|
p_node->_next->_prev = p_node->_prev;
|
||||||
|
}
|
||||||
|
if (p_node->_prev) {
|
||||||
|
p_node->_prev->_next = p_node->_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_allocator<Element, A>(p_node);
|
||||||
|
_data.size_cache--;
|
||||||
|
ERR_FAIL_COND(_data._nil->color == RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _calculate_depth(Element *p_element, int &max_d, int d) const {
|
||||||
|
if (p_element == _data._nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculate_depth(p_element->left, max_d, d + 1);
|
||||||
|
_calculate_depth(p_element->right, max_d, d + 1);
|
||||||
|
|
||||||
|
if (d > max_d) {
|
||||||
|
max_d = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cleanup_tree(Element *p_element) {
|
||||||
|
if (p_element == _data._nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_tree(p_element->left);
|
||||||
|
_cleanup_tree(p_element->right);
|
||||||
|
memdelete_allocator<Element, A>(p_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copy_from(const Map &p_map) {
|
||||||
|
clear();
|
||||||
|
// not the fastest way, but safeset to write.
|
||||||
|
for (Element *I = p_map.front(); I; I = I->next()) {
|
||||||
|
insert(I->key(), I->value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Element *find(const K &p_key) const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Element *res = _find(p_key);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *find(const K &p_key) {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *res = _find(p_key);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Element *find_closest(const K &p_key) const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Element *res = _find_closest(p_key);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *find_closest(const K &p_key) {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *res = _find_closest(p_key);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const K &p_key) const {
|
||||||
|
return find(p_key) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *insert(const K &p_key, const V &p_value) {
|
||||||
|
if (!_data._root) {
|
||||||
|
_data._create_root();
|
||||||
|
}
|
||||||
|
return _insert(p_key, p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(Element *p_element) {
|
||||||
|
if (!_data._root || !p_element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_erase(p_element);
|
||||||
|
if (_data.size_cache == 0 && _data._root) {
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool erase(const K &p_key) {
|
||||||
|
if (!_data._root) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = find(p_key);
|
||||||
|
if (!e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_erase(e);
|
||||||
|
if (_data.size_cache == 0 && _data._root) {
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const V &operator[](const K &p_key) const {
|
||||||
|
CRASH_COND(!_data._root);
|
||||||
|
const Element *e = find(p_key);
|
||||||
|
CRASH_COND(!e);
|
||||||
|
return e->_data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V &operator[](const K &p_key) {
|
||||||
|
if (!_data._root) {
|
||||||
|
_data._create_root();
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = find(p_key);
|
||||||
|
if (!e) {
|
||||||
|
e = insert(p_key, V());
|
||||||
|
}
|
||||||
|
|
||||||
|
return e->_data.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *front() const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = _data._root->left;
|
||||||
|
if (e == _data._nil) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (e->left != _data._nil) {
|
||||||
|
e = e->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *back() const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = _data._root->left;
|
||||||
|
if (e == _data._nil) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (e->right != _data._nil) {
|
||||||
|
e = e->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_empty() const { return _data.size_cache == 0; }
|
||||||
|
inline int size() const { return _data.size_cache; }
|
||||||
|
|
||||||
|
int calculate_depth() const {
|
||||||
|
// used for debug mostly
|
||||||
|
if (!_data._root) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_d = 0;
|
||||||
|
_calculate_depth(_data._root->left, max_d, 0);
|
||||||
|
return max_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
if (!_data._root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_tree(_data._root->left);
|
||||||
|
_data._root->left = _data._nil;
|
||||||
|
_data.size_cache = 0;
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const Map &p_map) {
|
||||||
|
_copy_from(p_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
Map(const Map &p_map) {
|
||||||
|
_copy_from(p_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Map() {}
|
||||||
|
|
||||||
|
~Map() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! MAP_HPP
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* pair.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 PAIR_HPP
|
||||||
|
#define PAIR_HPP
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class F, class S>
|
||||||
|
struct Pair {
|
||||||
|
F first;
|
||||||
|
S second;
|
||||||
|
|
||||||
|
Pair() :
|
||||||
|
first(),
|
||||||
|
second() {
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(F p_first, const S &p_second) :
|
||||||
|
first(p_first),
|
||||||
|
second(p_second) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class F, class S>
|
||||||
|
bool operator==(const Pair<F, S> &pair, const Pair<F, S> &other) {
|
||||||
|
return (pair.first == other.first) && (pair.second == other.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F, class S>
|
||||||
|
bool operator!=(const Pair<F, S> &pair, const Pair<F, S> &other) {
|
||||||
|
return (pair.first != other.first) || (pair.second != other.second);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class F, class S>
|
||||||
|
struct PairSort {
|
||||||
|
bool operator()(const Pair<F, S> &A, const Pair<F, S> &B) const {
|
||||||
|
if (A.first != B.first) {
|
||||||
|
return A.first < B.first;
|
||||||
|
}
|
||||||
|
return A.second < B.second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class K, class V>
|
||||||
|
struct KeyValue {
|
||||||
|
const K key;
|
||||||
|
V value;
|
||||||
|
|
||||||
|
void operator=(const KeyValue &p_kv) = delete;
|
||||||
|
_FORCE_INLINE_ KeyValue(const KeyValue &p_kv) :
|
||||||
|
key(p_kv.key),
|
||||||
|
value(p_kv.value) {
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ KeyValue(const K &p_key, const V &p_value) :
|
||||||
|
key(p_key),
|
||||||
|
value(p_value) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class K, class V>
|
||||||
|
bool operator==(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
|
||||||
|
return (pair.key == other.key) && (pair.value == other.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class K, class V>
|
||||||
|
bool operator!=(const KeyValue<K, V> &pair, const KeyValue<K, V> &other) {
|
||||||
|
return (pair.key != other.key) || (pair.value != other.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class K, class V>
|
||||||
|
struct KeyValueSort {
|
||||||
|
bool operator()(const KeyValue<K, V> &A, const KeyValue<K, V> &B) const {
|
||||||
|
return A.key < B.key;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! PAIR_HPP
|
|
@ -0,0 +1,465 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* rid_owner.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 RID_OWNER_HPP
|
||||||
|
#define RID_OWNER_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/godot.hpp>
|
||||||
|
#include <godot_cpp/templates/list.hpp>
|
||||||
|
#include <godot_cpp/templates/spin_lock.hpp>
|
||||||
|
#include <godot_cpp/variant/utility_functions.hpp>
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <typeinfo>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T, bool THREAD_SAFE = false>
|
||||||
|
class RID_Alloc {
|
||||||
|
T **chunks = nullptr;
|
||||||
|
uint32_t **free_list_chunks = nullptr;
|
||||||
|
uint32_t **validator_chunks = nullptr;
|
||||||
|
|
||||||
|
uint32_t elements_in_chunk;
|
||||||
|
uint32_t max_alloc = 0;
|
||||||
|
uint32_t alloc_count = 0;
|
||||||
|
|
||||||
|
const char *description = nullptr;
|
||||||
|
|
||||||
|
SpinLock spin_lock;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ RID _allocate_rid() {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (alloc_count == max_alloc) {
|
||||||
|
// allocate a new chunk
|
||||||
|
uint32_t chunk_count = alloc_count == 0 ? 0 : (max_alloc / elements_in_chunk);
|
||||||
|
|
||||||
|
// grow chunks
|
||||||
|
chunks = (T **)memrealloc(chunks, sizeof(T *) * (chunk_count + 1));
|
||||||
|
chunks[chunk_count] = (T *)memalloc(sizeof(T) * elements_in_chunk); // but don't initialize
|
||||||
|
|
||||||
|
// grow validators
|
||||||
|
validator_chunks = (uint32_t **)memrealloc(validator_chunks, sizeof(uint32_t *) * (chunk_count + 1));
|
||||||
|
validator_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
|
||||||
|
// grow free lists
|
||||||
|
free_list_chunks = (uint32_t **)memrealloc(free_list_chunks, sizeof(uint32_t *) * (chunk_count + 1));
|
||||||
|
free_list_chunks[chunk_count] = (uint32_t *)memalloc(sizeof(uint32_t) * elements_in_chunk);
|
||||||
|
|
||||||
|
// initialize
|
||||||
|
for (uint32_t i = 0; i < elements_in_chunk; i++) {
|
||||||
|
// Don't initialize chunk.
|
||||||
|
validator_chunks[chunk_count][i] = 0xFFFFFFFF;
|
||||||
|
free_list_chunks[chunk_count][i] = alloc_count + i;
|
||||||
|
}
|
||||||
|
|
||||||
|
max_alloc += elements_in_chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t free_index = free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk];
|
||||||
|
|
||||||
|
uint32_t free_chunk = free_index / elements_in_chunk;
|
||||||
|
uint32_t free_element = free_index % elements_in_chunk;
|
||||||
|
|
||||||
|
uint32_t validator = (uint32_t)(UtilityFunctions::rid_allocate_id() & 0x7FFFFFFF);
|
||||||
|
uint64_t id = validator;
|
||||||
|
id <<= 32;
|
||||||
|
id |= free_index;
|
||||||
|
|
||||||
|
validator_chunks[free_chunk][free_element] = validator;
|
||||||
|
|
||||||
|
validator_chunks[free_chunk][free_element] |= 0x80000000; // mark uninitialized bit
|
||||||
|
|
||||||
|
alloc_count++;
|
||||||
|
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return UtilityFunctions::rid_from_int64(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
RID make_rid() {
|
||||||
|
RID rid = _allocate_rid();
|
||||||
|
initialize_rid(rid);
|
||||||
|
return rid;
|
||||||
|
}
|
||||||
|
RID make_rid(const T &p_value) {
|
||||||
|
RID rid = _allocate_rid();
|
||||||
|
initialize_rid(rid, p_value);
|
||||||
|
return rid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// allocate but don't initialize, use initialize_rid afterwards
|
||||||
|
RID allocate_rid() {
|
||||||
|
return _allocate_rid();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *get_or_null(const RID &p_rid, bool p_initialize = false) {
|
||||||
|
if (p_rid == RID()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t id = p_rid.get_id();
|
||||||
|
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||||
|
if (unlikely(idx >= max_alloc)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t idx_chunk = idx / elements_in_chunk;
|
||||||
|
uint32_t idx_element = idx % elements_in_chunk;
|
||||||
|
|
||||||
|
uint32_t validator = uint32_t(id >> 32);
|
||||||
|
|
||||||
|
if (unlikely(p_initialize)) {
|
||||||
|
if (unlikely(!(validator_chunks[idx_chunk][idx_element] & 0x80000000))) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Initializing already initialized RID");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely((validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) != validator)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Attempting to initialize the wrong RID");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
validator_chunks[idx_chunk][idx_element] &= 0x7FFFFFFF; // initialized
|
||||||
|
|
||||||
|
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
if ((validator_chunks[idx_chunk][idx_element] & 0x80000000) && validator_chunks[idx_chunk][idx_element] != 0xFFFFFFFF) {
|
||||||
|
ERR_FAIL_V_MSG(nullptr, "Attempting to use an uninitialized RID");
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *ptr = &chunks[idx_chunk][idx_element];
|
||||||
|
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
void initialize_rid(RID p_rid) {
|
||||||
|
T *mem = get_or_null(p_rid, true);
|
||||||
|
ERR_FAIL_COND(!mem);
|
||||||
|
memnew_placement(mem, T);
|
||||||
|
}
|
||||||
|
void initialize_rid(RID p_rid, const T &p_value) {
|
||||||
|
T *mem = get_or_null(p_rid, true);
|
||||||
|
ERR_FAIL_COND(!mem);
|
||||||
|
memnew_placement(mem, T(p_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool owns(const RID &p_rid) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t id = p_rid.get_id();
|
||||||
|
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||||
|
if (unlikely(idx >= max_alloc)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t idx_chunk = idx / elements_in_chunk;
|
||||||
|
uint32_t idx_element = idx % elements_in_chunk;
|
||||||
|
|
||||||
|
uint32_t validator = uint32_t(id >> 32);
|
||||||
|
|
||||||
|
bool owned = (validator_chunks[idx_chunk][idx_element] & 0x7FFFFFFF) == validator;
|
||||||
|
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
return owned;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void free(const RID &p_rid) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t id = p_rid.get_id();
|
||||||
|
uint32_t idx = uint32_t(id & 0xFFFFFFFF);
|
||||||
|
if (unlikely(idx >= max_alloc)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
ERR_FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t idx_chunk = idx / elements_in_chunk;
|
||||||
|
uint32_t idx_element = idx % elements_in_chunk;
|
||||||
|
|
||||||
|
uint32_t validator = uint32_t(id >> 32);
|
||||||
|
if (unlikely(validator_chunks[idx_chunk][idx_element] & 0x80000000)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
ERR_FAIL_MSG("Attempted to free an uninitialized or invalid RID");
|
||||||
|
} else if (unlikely(validator_chunks[idx_chunk][idx_element] != validator)) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
ERR_FAIL();
|
||||||
|
}
|
||||||
|
|
||||||
|
chunks[idx_chunk][idx_element].~T();
|
||||||
|
validator_chunks[idx_chunk][idx_element] = 0xFFFFFFFF; // go invalid
|
||||||
|
|
||||||
|
alloc_count--;
|
||||||
|
free_list_chunks[alloc_count / elements_in_chunk][alloc_count % elements_in_chunk] = idx;
|
||||||
|
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ uint32_t get_rid_count() const {
|
||||||
|
return alloc_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void get_owned_list(List<RID> *p_owned) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < max_alloc; i++) {
|
||||||
|
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||||
|
if (validator != 0xFFFFFFFF) {
|
||||||
|
p_owned->push_back(UtilityFunctions::rid_from_int64((validator << 32) | i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// used for fast iteration in the elements or RIDs
|
||||||
|
void fill_owned_buffer(RID *p_rid_buffer) {
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.lock();
|
||||||
|
}
|
||||||
|
uint32_t idx = 0;
|
||||||
|
for (size_t i = 0; i < max_alloc; i++) {
|
||||||
|
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||||
|
if (validator != 0xFFFFFFFF) {
|
||||||
|
p_rid_buffer[idx] = UtilityFunctions::rid_from_int64((validator << 32) | i);
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (THREAD_SAFE) {
|
||||||
|
spin_lock.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_description(const char *p_descrption) {
|
||||||
|
description = p_descrption;
|
||||||
|
}
|
||||||
|
|
||||||
|
RID_Alloc(uint32_t p_target_chunk_byte_size = 65536) {
|
||||||
|
elements_in_chunk = sizeof(T) > p_target_chunk_byte_size ? 1 : (p_target_chunk_byte_size / sizeof(T));
|
||||||
|
}
|
||||||
|
|
||||||
|
~RID_Alloc() {
|
||||||
|
if (alloc_count) {
|
||||||
|
if (description) {
|
||||||
|
printf("ERROR: %d RID allocations of type '%s' were leaked at exit.", alloc_count, description);
|
||||||
|
} else {
|
||||||
|
#ifdef NO_SAFE_CAST
|
||||||
|
printf("ERROR: %d RID allocations of type 'unknown' were leaked at exit.", alloc_count);
|
||||||
|
#else
|
||||||
|
printf("ERROR: %d RID allocations of type '%s' were leaked at exit.", alloc_count, typeid(T).name());
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < max_alloc; i++) {
|
||||||
|
uint64_t validator = validator_chunks[i / elements_in_chunk][i % elements_in_chunk];
|
||||||
|
if (validator & 0x80000000) {
|
||||||
|
continue; // uninitialized
|
||||||
|
}
|
||||||
|
if (validator != 0xFFFFFFFF) {
|
||||||
|
chunks[i / elements_in_chunk][i % elements_in_chunk].~T();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t chunk_count = max_alloc / elements_in_chunk;
|
||||||
|
for (uint32_t i = 0; i < chunk_count; i++) {
|
||||||
|
memfree(chunks[i]);
|
||||||
|
memfree(validator_chunks[i]);
|
||||||
|
memfree(free_list_chunks[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chunks) {
|
||||||
|
memfree(chunks);
|
||||||
|
memfree(free_list_chunks);
|
||||||
|
memfree(validator_chunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, bool THREAD_SAFE = false>
|
||||||
|
class RID_PtrOwner {
|
||||||
|
RID_Alloc<T *, THREAD_SAFE> alloc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_FORCE_INLINE_ RID make_rid(T *p_ptr) {
|
||||||
|
return alloc.make_rid(p_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ RID allocate_rid() {
|
||||||
|
return alloc.allocate_rid();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void initialize_rid(RID p_rid, T *p_ptr) {
|
||||||
|
alloc.initialize_rid(p_rid, p_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *get_or_null(const RID &p_rid) {
|
||||||
|
T **ptr = alloc.get_or_null(p_rid);
|
||||||
|
if (unlikely(!ptr)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return *ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void replace(const RID &p_rid, T *p_new_ptr) {
|
||||||
|
T **ptr = alloc.get_or_null(p_rid);
|
||||||
|
ERR_FAIL_COND(!ptr);
|
||||||
|
*ptr = p_new_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool owns(const RID &p_rid) {
|
||||||
|
return alloc.owns(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void free(const RID &p_rid) {
|
||||||
|
alloc.free(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ uint32_t get_rid_count() const {
|
||||||
|
return alloc.get_rid_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
|
||||||
|
return alloc.get_owned_list(p_owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill_owned_buffer(RID *p_rid_buffer) {
|
||||||
|
alloc.fill_owned_buffer(p_rid_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_description(const char *p_descrption) {
|
||||||
|
alloc.set_description(p_descrption);
|
||||||
|
}
|
||||||
|
|
||||||
|
RID_PtrOwner(uint32_t p_target_chunk_byte_size = 65536) :
|
||||||
|
alloc(p_target_chunk_byte_size) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, bool THREAD_SAFE = false>
|
||||||
|
class RID_Owner {
|
||||||
|
RID_Alloc<T, THREAD_SAFE> alloc;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_FORCE_INLINE_ RID make_rid() {
|
||||||
|
return alloc.make_rid();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ RID make_rid(const T &p_ptr) {
|
||||||
|
return alloc.make_rid(p_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ RID allocate_rid() {
|
||||||
|
return alloc.allocate_rid();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void initialize_rid(RID p_rid) {
|
||||||
|
alloc.initialize_rid(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void initialize_rid(RID p_rid, const T &p_ptr) {
|
||||||
|
alloc.initialize_rid(p_rid, p_ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *get_or_null(const RID &p_rid) {
|
||||||
|
return alloc.get_or_null(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool owns(const RID &p_rid) {
|
||||||
|
return alloc.owns(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void free(const RID &p_rid) {
|
||||||
|
alloc.free(p_rid);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ uint32_t get_rid_count() const {
|
||||||
|
return alloc.get_rid_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ void get_owned_list(List<RID> *p_owned) {
|
||||||
|
return alloc.get_owned_list(p_owned);
|
||||||
|
}
|
||||||
|
void fill_owned_buffer(RID *p_rid_buffer) {
|
||||||
|
alloc.fill_owned_buffer(p_rid_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_description(const char *p_descrption) {
|
||||||
|
alloc.set_description(p_descrption);
|
||||||
|
}
|
||||||
|
RID_Owner(uint32_t p_target_chunk_byte_size = 65536) :
|
||||||
|
alloc(p_target_chunk_byte_size) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! RID_OWNER_HPP
|
|
@ -0,0 +1,326 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* safe_refcount.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 SAFE_REFCOUNT_HPP
|
||||||
|
#define SAFE_REFCOUNT_HPP
|
||||||
|
|
||||||
|
#if !defined(NO_THREADS)
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <type_traits>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
// Design goals for these classes:
|
||||||
|
// - No automatic conversions or arithmetic operators,
|
||||||
|
// to keep explicit the use of atomics everywhere.
|
||||||
|
// - Using acquire-release semantics, even to set the first value.
|
||||||
|
// The first value may be set relaxedly in many cases, but adding the distinction
|
||||||
|
// between relaxed and unrelaxed operation to the interface would make it needlessly
|
||||||
|
// flexible. There's negligible waste in having release semantics for the initial
|
||||||
|
// value and, as an important benefit, you can be sure the value is properly synchronized
|
||||||
|
// even with threads that are already running.
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SafeNumeric {
|
||||||
|
std::atomic<T> value;
|
||||||
|
|
||||||
|
static_assert(std::atomic<T>::is_always_lock_free);
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ void set(T p_value) {
|
||||||
|
value.store(p_value, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T get() const {
|
||||||
|
return value.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T increment() {
|
||||||
|
return value.fetch_add(1, std::memory_order_acq_rel) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the original value instead of the new one
|
||||||
|
_ALWAYS_INLINE_ T postincrement() {
|
||||||
|
return value.fetch_add(1, std::memory_order_acq_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T decrement() {
|
||||||
|
return value.fetch_sub(1, std::memory_order_acq_rel) - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the original value instead of the new one
|
||||||
|
_ALWAYS_INLINE_ T postdecrement() {
|
||||||
|
return value.fetch_sub(1, std::memory_order_acq_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T add(T p_value) {
|
||||||
|
return value.fetch_add(p_value, std::memory_order_acq_rel) + p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the original value instead of the new one
|
||||||
|
_ALWAYS_INLINE_ T postadd(T p_value) {
|
||||||
|
return value.fetch_add(p_value, std::memory_order_acq_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T sub(T p_value) {
|
||||||
|
return value.fetch_sub(p_value, std::memory_order_acq_rel) - p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns the original value instead of the new one
|
||||||
|
_ALWAYS_INLINE_ T postsub(T p_value) {
|
||||||
|
return value.fetch_sub(p_value, std::memory_order_acq_rel);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T exchange_if_greater(T p_value) {
|
||||||
|
while (true) {
|
||||||
|
T tmp = value.load(std::memory_order_acquire);
|
||||||
|
if (tmp >= p_value) {
|
||||||
|
return tmp; // already greater, or equal
|
||||||
|
}
|
||||||
|
if (value.compare_exchange_weak(tmp, p_value, std::memory_order_release)) {
|
||||||
|
return p_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T conditional_increment() {
|
||||||
|
while (true) {
|
||||||
|
T c = value.load(std::memory_order_acquire);
|
||||||
|
if (c == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (value.compare_exchange_weak(c, c + 1, std::memory_order_release)) {
|
||||||
|
return c + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) {
|
||||||
|
set(p_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SafeFlag {
|
||||||
|
std::atomic_bool flag;
|
||||||
|
|
||||||
|
static_assert(std::atomic_bool::is_always_lock_free);
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ bool is_set() const {
|
||||||
|
return flag.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void set() {
|
||||||
|
flag.store(true, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void clear() {
|
||||||
|
flag.store(false, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void set_to(bool p_value) {
|
||||||
|
flag.store(p_value, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) {
|
||||||
|
set_to(p_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SafeRefCount {
|
||||||
|
SafeNumeric<uint32_t> count;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ bool ref() { // true on success
|
||||||
|
return count.conditional_increment() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t refval() { // none-zero on success
|
||||||
|
return count.conditional_increment();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
|
||||||
|
return count.decrement() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
|
||||||
|
return count.decrement();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t get() const {
|
||||||
|
return count.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void init(uint32_t p_value = 1) {
|
||||||
|
count.set(p_value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class SafeNumeric {
|
||||||
|
protected:
|
||||||
|
T value;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ void set(T p_value) {
|
||||||
|
value = p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T get() const {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T increment() {
|
||||||
|
return ++value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T postincrement() {
|
||||||
|
return value++;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T decrement() {
|
||||||
|
return --value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T postdecrement() {
|
||||||
|
return value--;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T add(T p_value) {
|
||||||
|
return value += p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T postadd(T p_value) {
|
||||||
|
T old = value;
|
||||||
|
value += p_value;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T sub(T p_value) {
|
||||||
|
return value -= p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T postsub(T p_value) {
|
||||||
|
T old = value;
|
||||||
|
value -= p_value;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T exchange_if_greater(T p_value) {
|
||||||
|
if (value < p_value) {
|
||||||
|
value = p_value;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ T conditional_increment() {
|
||||||
|
if (value == 0) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return ++value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ explicit SafeNumeric<T>(T p_value = static_cast<T>(0)) :
|
||||||
|
value(p_value) {
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SafeFlag {
|
||||||
|
protected:
|
||||||
|
bool flag;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ bool is_set() const {
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void set() {
|
||||||
|
flag = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void clear() {
|
||||||
|
flag = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void set_to(bool p_value) {
|
||||||
|
flag = p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ explicit SafeFlag(bool p_value = false) :
|
||||||
|
flag(p_value) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SafeRefCount {
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ bool ref() { // true on success
|
||||||
|
if (count != 0) {
|
||||||
|
++count;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t refval() { // none-zero on success
|
||||||
|
if (count != 0) {
|
||||||
|
return ++count;
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ bool unref() { // true if must be disposed of
|
||||||
|
return --count == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t unrefval() { // 0 if must be disposed of
|
||||||
|
return --count;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ uint32_t get() const {
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
_ALWAYS_INLINE_ void init(uint32_t p_value = 1) {
|
||||||
|
count = p_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! SAFE_REFCOUNT_HPP
|
|
@ -0,0 +1,71 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* search_array.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 SEARCH_ARRAY_HPP
|
||||||
|
#define SEARCH_ARRAY_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/templates/sort_array.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T, class Comparator = _DefaultComparator<T>>
|
||||||
|
class SearchArray {
|
||||||
|
public:
|
||||||
|
Comparator compare;
|
||||||
|
|
||||||
|
inline int bisect(const T *p_array, int p_len, const T &p_value, bool p_before) const {
|
||||||
|
int lo = 0;
|
||||||
|
int hi = p_len;
|
||||||
|
if (p_before) {
|
||||||
|
while (lo < hi) {
|
||||||
|
const int mid = (lo + hi) / 2;
|
||||||
|
if (compare(p_array[mid], p_value)) {
|
||||||
|
lo = mid + 1;
|
||||||
|
} else {
|
||||||
|
hi = mid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (lo < hi) {
|
||||||
|
const int mid = (lo + hi) / 2;
|
||||||
|
if (compare(p_value, p_array[mid])) {
|
||||||
|
hi = mid;
|
||||||
|
} else {
|
||||||
|
lo = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lo;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! SEARCH_ARRAY_HPP
|
|
@ -0,0 +1,707 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* set.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 SET_HPP
|
||||||
|
#define SET_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T, class C = Comparator<T>, class A = DefaultAllocator>
|
||||||
|
class Set {
|
||||||
|
enum Color {
|
||||||
|
RED,
|
||||||
|
BLACK
|
||||||
|
};
|
||||||
|
struct _Data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
class Element {
|
||||||
|
private:
|
||||||
|
friend class Set<T, C, A>;
|
||||||
|
int color = RED;
|
||||||
|
Element *right = nullptr;
|
||||||
|
Element *left = nullptr;
|
||||||
|
Element *parent = nullptr;
|
||||||
|
Element *_next = nullptr;
|
||||||
|
Element *_prev = nullptr;
|
||||||
|
T value;
|
||||||
|
//_Data *data;
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Element *next() const {
|
||||||
|
return _next;
|
||||||
|
}
|
||||||
|
Element *next() {
|
||||||
|
return _next;
|
||||||
|
}
|
||||||
|
const Element *prev() const {
|
||||||
|
return _prev;
|
||||||
|
}
|
||||||
|
Element *prev() {
|
||||||
|
return _prev;
|
||||||
|
}
|
||||||
|
T &get() {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
const T &get() const {
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
Element() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef T ValueType;
|
||||||
|
|
||||||
|
struct Iterator {
|
||||||
|
_FORCE_INLINE_ T &operator*() const {
|
||||||
|
return E->get();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ T *operator->() const { return &E->get(); }
|
||||||
|
_FORCE_INLINE_ Iterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
Iterator(Element *p_E) { E = p_E; }
|
||||||
|
Iterator() {}
|
||||||
|
Iterator(const Iterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstIterator {
|
||||||
|
_FORCE_INLINE_ const T &operator*() const {
|
||||||
|
return E->get();
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ const T *operator->() const { return &E->get(); }
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||||
|
E = E->next();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||||
|
E = E->prev();
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return E == b.E; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return E != b.E; }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ConstIterator(const Element *p_E) { E = p_E; }
|
||||||
|
_FORCE_INLINE_ ConstIterator() {}
|
||||||
|
_FORCE_INLINE_ ConstIterator(const ConstIterator &p_it) { E = p_it.E; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const Element *E = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Iterator begin() {
|
||||||
|
return Iterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator end() {
|
||||||
|
return Iterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ Iterator find(const K &p_key) {
|
||||||
|
return Iterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ConstIterator begin() const {
|
||||||
|
return ConstIterator(front());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator end() const {
|
||||||
|
return ConstIterator(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
//to use when replacing find()
|
||||||
|
_FORCE_INLINE_ ConstIterator find(const K &p_key) const {
|
||||||
|
return ConstIterator(find(p_key));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
private:
|
||||||
|
struct _Data {
|
||||||
|
Element *_root = nullptr;
|
||||||
|
Element *_nil = nullptr;
|
||||||
|
int size_cache = 0;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ _Data() {
|
||||||
|
#ifdef GLOBALNIL_DISABLED
|
||||||
|
_nil = memnew_allocator(Element, A);
|
||||||
|
_nil->parent = _nil->left = _nil->right = _nil;
|
||||||
|
_nil->color = BLACK;
|
||||||
|
#else
|
||||||
|
_nil = (Element *)&_GlobalNilClass::_nil;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void _create_root() {
|
||||||
|
_root = memnew_allocator(Element, A);
|
||||||
|
_root->parent = _root->left = _root->right = _nil;
|
||||||
|
_root->color = BLACK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _free_root() {
|
||||||
|
if (_root) {
|
||||||
|
memdelete_allocator<Element, A>(_root);
|
||||||
|
_root = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~_Data() {
|
||||||
|
_free_root();
|
||||||
|
|
||||||
|
#ifdef GLOBALNIL_DISABLED
|
||||||
|
memdelete_allocator<Element, A>(_nil);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_Data _data;
|
||||||
|
|
||||||
|
inline void _set_color(Element *p_node, int p_color) {
|
||||||
|
ERR_FAIL_COND(p_node == _data._nil && p_color == RED);
|
||||||
|
p_node->color = p_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void _rotate_left(Element *p_node) {
|
||||||
|
Element *r = p_node->right;
|
||||||
|
p_node->right = r->left;
|
||||||
|
if (r->left != _data._nil) {
|
||||||
|
r->left->parent = p_node;
|
||||||
|
}
|
||||||
|
r->parent = p_node->parent;
|
||||||
|
if (p_node == p_node->parent->left) {
|
||||||
|
p_node->parent->left = r;
|
||||||
|
} else {
|
||||||
|
p_node->parent->right = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
r->left = p_node;
|
||||||
|
p_node->parent = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void _rotate_right(Element *p_node) {
|
||||||
|
Element *l = p_node->left;
|
||||||
|
p_node->left = l->right;
|
||||||
|
if (l->right != _data._nil) {
|
||||||
|
l->right->parent = p_node;
|
||||||
|
}
|
||||||
|
l->parent = p_node->parent;
|
||||||
|
if (p_node == p_node->parent->right) {
|
||||||
|
p_node->parent->right = l;
|
||||||
|
} else {
|
||||||
|
p_node->parent->left = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
l->right = p_node;
|
||||||
|
p_node->parent = l;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Element *_successor(Element *p_node) const {
|
||||||
|
Element *node = p_node;
|
||||||
|
|
||||||
|
if (node->right != _data._nil) {
|
||||||
|
node = node->right;
|
||||||
|
while (node->left != _data._nil) { /* returns the minimum of the right subtree of node */
|
||||||
|
node = node->left;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
while (node == node->parent->right) {
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->parent == _data._root) {
|
||||||
|
return nullptr; // No successor, as p_node = last node
|
||||||
|
}
|
||||||
|
return node->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Element *_predecessor(Element *p_node) const {
|
||||||
|
Element *node = p_node;
|
||||||
|
|
||||||
|
if (node->left != _data._nil) {
|
||||||
|
node = node->left;
|
||||||
|
while (node->right != _data._nil) { /* returns the minimum of the left subtree of node */
|
||||||
|
node = node->right;
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
} else {
|
||||||
|
while (node == node->parent->left) {
|
||||||
|
node = node->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node == _data._root) {
|
||||||
|
return nullptr; // No predecessor, as p_node = first node.
|
||||||
|
}
|
||||||
|
return node->parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_find(const T &p_value) const {
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
if (less(p_value, node->value)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->value, p_value)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
return node; // found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_lower_bound(const T &p_value) const {
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
Element *prev = nullptr;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
prev = node;
|
||||||
|
|
||||||
|
if (less(p_value, node->value)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->value, p_value)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
return node; // found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prev == nullptr) {
|
||||||
|
return nullptr; // tree empty
|
||||||
|
}
|
||||||
|
|
||||||
|
if (less(prev->value, p_value)) {
|
||||||
|
prev = prev->_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return prev;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _insert_rb_fix(Element *p_new_node) {
|
||||||
|
Element *node = p_new_node;
|
||||||
|
Element *nparent = node->parent;
|
||||||
|
Element *ngrand_parent;
|
||||||
|
|
||||||
|
while (nparent->color == RED) {
|
||||||
|
ngrand_parent = nparent->parent;
|
||||||
|
|
||||||
|
if (nparent == ngrand_parent->left) {
|
||||||
|
if (ngrand_parent->right->color == RED) {
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent->right, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
node = ngrand_parent;
|
||||||
|
nparent = node->parent;
|
||||||
|
} else {
|
||||||
|
if (node == nparent->right) {
|
||||||
|
_rotate_left(nparent);
|
||||||
|
node = nparent;
|
||||||
|
nparent = node->parent;
|
||||||
|
}
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
_rotate_right(ngrand_parent);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (ngrand_parent->left->color == RED) {
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent->left, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
node = ngrand_parent;
|
||||||
|
nparent = node->parent;
|
||||||
|
} else {
|
||||||
|
if (node == nparent->left) {
|
||||||
|
_rotate_right(nparent);
|
||||||
|
node = nparent;
|
||||||
|
nparent = node->parent;
|
||||||
|
}
|
||||||
|
_set_color(nparent, BLACK);
|
||||||
|
_set_color(ngrand_parent, RED);
|
||||||
|
_rotate_left(ngrand_parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_color(_data._root->left, BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *_insert(const T &p_value) {
|
||||||
|
Element *new_parent = _data._root;
|
||||||
|
Element *node = _data._root->left;
|
||||||
|
C less;
|
||||||
|
|
||||||
|
while (node != _data._nil) {
|
||||||
|
new_parent = node;
|
||||||
|
|
||||||
|
if (less(p_value, node->value)) {
|
||||||
|
node = node->left;
|
||||||
|
} else if (less(node->value, p_value)) {
|
||||||
|
node = node->right;
|
||||||
|
} else {
|
||||||
|
return node; // Return existing node
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *new_node = memnew_allocator(Element, A);
|
||||||
|
new_node->parent = new_parent;
|
||||||
|
new_node->right = _data._nil;
|
||||||
|
new_node->left = _data._nil;
|
||||||
|
new_node->value = p_value;
|
||||||
|
// new_node->data=_data;
|
||||||
|
|
||||||
|
if (new_parent == _data._root || less(p_value, new_parent->value)) {
|
||||||
|
new_parent->left = new_node;
|
||||||
|
} else {
|
||||||
|
new_parent->right = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
new_node->_next = _successor(new_node);
|
||||||
|
new_node->_prev = _predecessor(new_node);
|
||||||
|
if (new_node->_next) {
|
||||||
|
new_node->_next->_prev = new_node;
|
||||||
|
}
|
||||||
|
if (new_node->_prev) {
|
||||||
|
new_node->_prev->_next = new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
_data.size_cache++;
|
||||||
|
_insert_rb_fix(new_node);
|
||||||
|
return new_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _erase_fix_rb(Element *p_node) {
|
||||||
|
Element *root = _data._root->left;
|
||||||
|
Element *node = _data._nil;
|
||||||
|
Element *sibling = p_node;
|
||||||
|
Element *parent = sibling->parent;
|
||||||
|
|
||||||
|
while (node != root) { // If red node found, will exit at a break
|
||||||
|
if (sibling->color == RED) {
|
||||||
|
_set_color(sibling, BLACK);
|
||||||
|
_set_color(parent, RED);
|
||||||
|
if (sibling == parent->right) {
|
||||||
|
sibling = sibling->left;
|
||||||
|
_rotate_left(parent);
|
||||||
|
} else {
|
||||||
|
sibling = sibling->right;
|
||||||
|
_rotate_right(parent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ((sibling->left->color == BLACK) && (sibling->right->color == BLACK)) {
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
if (parent->color == RED) {
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
break;
|
||||||
|
} else { // loop: haven't found any red nodes yet
|
||||||
|
node = parent;
|
||||||
|
parent = node->parent;
|
||||||
|
sibling = (node == parent->left) ? parent->right : parent->left;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sibling == parent->right) {
|
||||||
|
if (sibling->right->color == BLACK) {
|
||||||
|
_set_color(sibling->left, BLACK);
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
_rotate_right(sibling);
|
||||||
|
sibling = sibling->parent;
|
||||||
|
}
|
||||||
|
_set_color(sibling, parent->color);
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
_set_color(sibling->right, BLACK);
|
||||||
|
_rotate_left(parent);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (sibling->left->color == BLACK) {
|
||||||
|
_set_color(sibling->right, BLACK);
|
||||||
|
_set_color(sibling, RED);
|
||||||
|
_rotate_left(sibling);
|
||||||
|
sibling = sibling->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
_set_color(sibling, parent->color);
|
||||||
|
_set_color(parent, BLACK);
|
||||||
|
_set_color(sibling->left, BLACK);
|
||||||
|
_rotate_right(parent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_FAIL_COND(_data._nil->color != BLACK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _erase(Element *p_node) {
|
||||||
|
Element *rp = ((p_node->left == _data._nil) || (p_node->right == _data._nil)) ? p_node : p_node->_next;
|
||||||
|
Element *node = (rp->left == _data._nil) ? rp->right : rp->left;
|
||||||
|
|
||||||
|
Element *sibling;
|
||||||
|
if (rp == rp->parent->left) {
|
||||||
|
rp->parent->left = node;
|
||||||
|
sibling = rp->parent->right;
|
||||||
|
} else {
|
||||||
|
rp->parent->right = node;
|
||||||
|
sibling = rp->parent->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (node->color == RED) {
|
||||||
|
node->parent = rp->parent;
|
||||||
|
_set_color(node, BLACK);
|
||||||
|
} else if (rp->color == BLACK && rp->parent != _data._root) {
|
||||||
|
_erase_fix_rb(sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rp != p_node) {
|
||||||
|
ERR_FAIL_COND(rp == _data._nil);
|
||||||
|
|
||||||
|
rp->left = p_node->left;
|
||||||
|
rp->right = p_node->right;
|
||||||
|
rp->parent = p_node->parent;
|
||||||
|
rp->color = p_node->color;
|
||||||
|
if (p_node->left != _data._nil) {
|
||||||
|
p_node->left->parent = rp;
|
||||||
|
}
|
||||||
|
if (p_node->right != _data._nil) {
|
||||||
|
p_node->right->parent = rp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_node == p_node->parent->left) {
|
||||||
|
p_node->parent->left = rp;
|
||||||
|
} else {
|
||||||
|
p_node->parent->right = rp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (p_node->_next) {
|
||||||
|
p_node->_next->_prev = p_node->_prev;
|
||||||
|
}
|
||||||
|
if (p_node->_prev) {
|
||||||
|
p_node->_prev->_next = p_node->_next;
|
||||||
|
}
|
||||||
|
|
||||||
|
memdelete_allocator<Element, A>(p_node);
|
||||||
|
_data.size_cache--;
|
||||||
|
ERR_FAIL_COND(_data._nil->color == RED);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _calculate_depth(Element *p_element, int &max_d, int d) const {
|
||||||
|
if (p_element == _data._nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_calculate_depth(p_element->left, max_d, d + 1);
|
||||||
|
_calculate_depth(p_element->right, max_d, d + 1);
|
||||||
|
|
||||||
|
if (d > max_d) {
|
||||||
|
max_d = d;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _cleanup_tree(Element *p_element) {
|
||||||
|
if (p_element == _data._nil) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_tree(p_element->left);
|
||||||
|
_cleanup_tree(p_element->right);
|
||||||
|
memdelete_allocator<Element, A>(p_element);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _copy_from(const Set &p_set) {
|
||||||
|
clear();
|
||||||
|
// not the fastest way, but safeset to write.
|
||||||
|
for (Element *I = p_set.front(); I; I = I->next()) {
|
||||||
|
insert(I->get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
const Element *find(const T &p_value) const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Element *res = _find(p_value);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *find(const T &p_value) {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *res = _find(p_value);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *lower_bound(const T &p_value) const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return _lower_bound(p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const T &p_value) const {
|
||||||
|
return find(p_value) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *insert(const T &p_value) {
|
||||||
|
if (!_data._root) {
|
||||||
|
_data._create_root();
|
||||||
|
}
|
||||||
|
return _insert(p_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(Element *p_element) {
|
||||||
|
if (!_data._root || !p_element) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_erase(p_element);
|
||||||
|
if (_data.size_cache == 0 && _data._root) {
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool erase(const T &p_value) {
|
||||||
|
if (!_data._root) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = find(p_value);
|
||||||
|
if (!e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
_erase(e);
|
||||||
|
if (_data.size_cache == 0 && _data._root) {
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *front() const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = _data._root->left;
|
||||||
|
if (e == _data._nil) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (e->left != _data._nil) {
|
||||||
|
e = e->left;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *back() const {
|
||||||
|
if (!_data._root) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
Element *e = _data._root->left;
|
||||||
|
if (e == _data._nil) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (e->right != _data._nil) {
|
||||||
|
e = e->right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool is_empty() const { return _data.size_cache == 0; }
|
||||||
|
inline int size() const { return _data.size_cache; }
|
||||||
|
|
||||||
|
int calculate_depth() const {
|
||||||
|
// used for debug mostly
|
||||||
|
if (!_data._root) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_d = 0;
|
||||||
|
_calculate_depth(_data._root->left, max_d, 0);
|
||||||
|
return max_d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear() {
|
||||||
|
if (!_data._root) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cleanup_tree(_data._root->left);
|
||||||
|
_data._root->left = _data._nil;
|
||||||
|
_data.size_cache = 0;
|
||||||
|
_data._free_root();
|
||||||
|
}
|
||||||
|
|
||||||
|
void operator=(const Set &p_set) {
|
||||||
|
_copy_from(p_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set(const Set &p_set) {
|
||||||
|
_copy_from(p_set);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Set() {}
|
||||||
|
|
||||||
|
~Set() {
|
||||||
|
clear();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! SET_HPP
|
|
@ -0,0 +1,323 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* sort_array.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 SORT_ARRAY_HPP
|
||||||
|
#define SORT_ARRAY_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
#define ERR_BAD_COMPARE(cond) \
|
||||||
|
if (unlikely(cond)) { \
|
||||||
|
ERR_PRINT("bad comparison function; sorting will be broken"); \
|
||||||
|
break; \
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
struct _DefaultComparator {
|
||||||
|
_FORCE_INLINE_ bool operator()(const T &a, const T &b) const { return (a < b); }
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
#define SORT_ARRAY_VALIDATE_ENABLED true
|
||||||
|
#else
|
||||||
|
#define SORT_ARRAY_VALIDATE_ENABLED false
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class T, class Comparator = _DefaultComparator<T>, bool Validate = SORT_ARRAY_VALIDATE_ENABLED>
|
||||||
|
class SortArray {
|
||||||
|
enum {
|
||||||
|
INTROSORT_THRESHOLD = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Comparator compare;
|
||||||
|
|
||||||
|
inline const T &median_of_3(const T &a, const T &b, const T &c) const {
|
||||||
|
if (compare(a, b)) {
|
||||||
|
if (compare(b, c)) {
|
||||||
|
return b;
|
||||||
|
} else if (compare(a, c)) {
|
||||||
|
return c;
|
||||||
|
} else {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
} else if (compare(a, c)) {
|
||||||
|
return a;
|
||||||
|
} else if (compare(b, c)) {
|
||||||
|
return c;
|
||||||
|
} else {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int bitlog(int n) const {
|
||||||
|
int k;
|
||||||
|
for (k = 0; n != 1; n >>= 1) {
|
||||||
|
++k;
|
||||||
|
}
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heap / Heapsort functions */
|
||||||
|
|
||||||
|
inline void push_heap(int p_first, int p_hole_idx, int p_top_index, T p_value, T *p_array) const {
|
||||||
|
int parent = (p_hole_idx - 1) / 2;
|
||||||
|
while (p_hole_idx > p_top_index && compare(p_array[p_first + parent], p_value)) {
|
||||||
|
p_array[p_first + p_hole_idx] = p_array[p_first + parent];
|
||||||
|
p_hole_idx = parent;
|
||||||
|
parent = (p_hole_idx - 1) / 2;
|
||||||
|
}
|
||||||
|
p_array[p_first + p_hole_idx] = p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void pop_heap(int p_first, int p_last, int p_result, T p_value, T *p_array) const {
|
||||||
|
p_array[p_result] = p_array[p_first];
|
||||||
|
adjust_heap(p_first, 0, p_last - p_first, p_value, p_array);
|
||||||
|
}
|
||||||
|
inline void pop_heap(int p_first, int p_last, T *p_array) const {
|
||||||
|
pop_heap(p_first, p_last - 1, p_last - 1, p_array[p_last - 1], p_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void adjust_heap(int p_first, int p_hole_idx, int p_len, T p_value, T *p_array) const {
|
||||||
|
int top_index = p_hole_idx;
|
||||||
|
int second_child = 2 * p_hole_idx + 2;
|
||||||
|
|
||||||
|
while (second_child < p_len) {
|
||||||
|
if (compare(p_array[p_first + second_child], p_array[p_first + (second_child - 1)])) {
|
||||||
|
second_child--;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_array[p_first + p_hole_idx] = p_array[p_first + second_child];
|
||||||
|
p_hole_idx = second_child;
|
||||||
|
second_child = 2 * (second_child + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (second_child == p_len) {
|
||||||
|
p_array[p_first + p_hole_idx] = p_array[p_first + (second_child - 1)];
|
||||||
|
p_hole_idx = second_child - 1;
|
||||||
|
}
|
||||||
|
push_heap(p_first, p_hole_idx, top_index, p_value, p_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sort_heap(int p_first, int p_last, T *p_array) const {
|
||||||
|
while (p_last - p_first > 1) {
|
||||||
|
pop_heap(p_first, p_last--, p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void make_heap(int p_first, int p_last, T *p_array) const {
|
||||||
|
if (p_last - p_first < 2) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
int len = p_last - p_first;
|
||||||
|
int parent = (len - 2) / 2;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
adjust_heap(p_first, parent, len, p_array[p_first + parent], p_array);
|
||||||
|
if (parent == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
parent--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void partial_sort(int p_first, int p_last, int p_middle, T *p_array) const {
|
||||||
|
make_heap(p_first, p_middle, p_array);
|
||||||
|
for (int i = p_middle; i < p_last; i++) {
|
||||||
|
if (compare(p_array[i], p_array[p_first])) {
|
||||||
|
pop_heap(p_first, p_middle, i, p_array[i], p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort_heap(p_first, p_middle, p_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void partial_select(int p_first, int p_last, int p_middle, T *p_array) const {
|
||||||
|
make_heap(p_first, p_middle, p_array);
|
||||||
|
for (int i = p_middle; i < p_last; i++) {
|
||||||
|
if (compare(p_array[i], p_array[p_first])) {
|
||||||
|
pop_heap(p_first, p_middle, i, p_array[i], p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int partitioner(int p_first, int p_last, T p_pivot, T *p_array) const {
|
||||||
|
const int unmodified_first = p_first;
|
||||||
|
const int unmodified_last = p_last;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
while (compare(p_array[p_first], p_pivot)) {
|
||||||
|
if (Validate) {
|
||||||
|
ERR_BAD_COMPARE(p_first == unmodified_last - 1);
|
||||||
|
}
|
||||||
|
p_first++;
|
||||||
|
}
|
||||||
|
p_last--;
|
||||||
|
while (compare(p_pivot, p_array[p_last])) {
|
||||||
|
if (Validate) {
|
||||||
|
ERR_BAD_COMPARE(p_last == unmodified_first);
|
||||||
|
}
|
||||||
|
p_last--;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(p_first < p_last)) {
|
||||||
|
return p_first;
|
||||||
|
}
|
||||||
|
|
||||||
|
SWAP(p_array[p_first], p_array[p_last]);
|
||||||
|
p_first++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void introsort(int p_first, int p_last, T *p_array, int p_max_depth) const {
|
||||||
|
while (p_last - p_first > INTROSORT_THRESHOLD) {
|
||||||
|
if (p_max_depth == 0) {
|
||||||
|
partial_sort(p_first, p_last, p_last, p_array);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_max_depth--;
|
||||||
|
|
||||||
|
int cut = partitioner(
|
||||||
|
p_first,
|
||||||
|
p_last,
|
||||||
|
median_of_3(
|
||||||
|
p_array[p_first],
|
||||||
|
p_array[p_first + (p_last - p_first) / 2],
|
||||||
|
p_array[p_last - 1]),
|
||||||
|
p_array);
|
||||||
|
|
||||||
|
introsort(cut, p_last, p_array, p_max_depth);
|
||||||
|
p_last = cut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void introselect(int p_first, int p_nth, int p_last, T *p_array, int p_max_depth) const {
|
||||||
|
while (p_last - p_first > 3) {
|
||||||
|
if (p_max_depth == 0) {
|
||||||
|
partial_select(p_first, p_nth + 1, p_last, p_array);
|
||||||
|
SWAP(p_first, p_nth);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
p_max_depth--;
|
||||||
|
|
||||||
|
int cut = partitioner(
|
||||||
|
p_first,
|
||||||
|
p_last,
|
||||||
|
median_of_3(
|
||||||
|
p_array[p_first],
|
||||||
|
p_array[p_first + (p_last - p_first) / 2],
|
||||||
|
p_array[p_last - 1]),
|
||||||
|
p_array);
|
||||||
|
|
||||||
|
if (cut <= p_nth) {
|
||||||
|
p_first = cut;
|
||||||
|
} else {
|
||||||
|
p_last = cut;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
insertion_sort(p_first, p_last, p_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unguarded_linear_insert(int p_last, T p_value, T *p_array) const {
|
||||||
|
int next = p_last - 1;
|
||||||
|
while (compare(p_value, p_array[next])) {
|
||||||
|
if (Validate) {
|
||||||
|
ERR_BAD_COMPARE(next == 0);
|
||||||
|
}
|
||||||
|
p_array[p_last] = p_array[next];
|
||||||
|
p_last = next;
|
||||||
|
next--;
|
||||||
|
}
|
||||||
|
p_array[p_last] = p_value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void linear_insert(int p_first, int p_last, T *p_array) const {
|
||||||
|
T val = p_array[p_last];
|
||||||
|
if (compare(val, p_array[p_first])) {
|
||||||
|
for (int i = p_last; i > p_first; i--) {
|
||||||
|
p_array[i] = p_array[i - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
p_array[p_first] = val;
|
||||||
|
} else {
|
||||||
|
unguarded_linear_insert(p_last, val, p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void insertion_sort(int p_first, int p_last, T *p_array) const {
|
||||||
|
if (p_first == p_last) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (int i = p_first + 1; i != p_last; i++) {
|
||||||
|
linear_insert(p_first, i, p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void unguarded_insertion_sort(int p_first, int p_last, T *p_array) const {
|
||||||
|
for (int i = p_first; i != p_last; i++) {
|
||||||
|
unguarded_linear_insert(i, p_array[i], p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void final_insertion_sort(int p_first, int p_last, T *p_array) const {
|
||||||
|
if (p_last - p_first > INTROSORT_THRESHOLD) {
|
||||||
|
insertion_sort(p_first, p_first + INTROSORT_THRESHOLD, p_array);
|
||||||
|
unguarded_insertion_sort(p_first + INTROSORT_THRESHOLD, p_last, p_array);
|
||||||
|
} else {
|
||||||
|
insertion_sort(p_first, p_last, p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sort_range(int p_first, int p_last, T *p_array) const {
|
||||||
|
if (p_first != p_last) {
|
||||||
|
introsort(p_first, p_last, p_array, bitlog(p_last - p_first) * 2);
|
||||||
|
final_insertion_sort(p_first, p_last, p_array);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void sort(T *p_array, int p_len) const {
|
||||||
|
sort_range(0, p_len, p_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void nth_element(int p_first, int p_last, int p_nth, T *p_array) const {
|
||||||
|
if (p_first == p_last || p_nth == p_last) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
introselect(p_first, p_nth, p_last, p_array, bitlog(p_last - p_first) * 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! SORT_ARRAY_HPP
|
|
@ -0,0 +1,54 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* spin_lock.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 SPIN_LOCK_HPP
|
||||||
|
#define SPIN_LOCK_HPP
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
class SpinLock {
|
||||||
|
std::atomic_flag locked = ATOMIC_FLAG_INIT;
|
||||||
|
|
||||||
|
public:
|
||||||
|
_ALWAYS_INLINE_ void lock() {
|
||||||
|
while (locked.test_and_set(std::memory_order_acquire)) {
|
||||||
|
;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ALWAYS_INLINE_ void unlock() {
|
||||||
|
locked.clear(std::memory_order_release);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! SPIN_LOCK_HPP
|
|
@ -0,0 +1,205 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* thread_work_pool.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 THREAD_WORK_POOL_HPP
|
||||||
|
#define THREAD_WORK_POOL_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/classes/os.hpp>
|
||||||
|
#include <godot_cpp/classes/semaphore.hpp>
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
class ThreadWorkPool {
|
||||||
|
std::atomic<uint32_t> index;
|
||||||
|
|
||||||
|
struct BaseWork {
|
||||||
|
std::atomic<uint32_t> *index = nullptr;
|
||||||
|
uint32_t max_elements = 0;
|
||||||
|
virtual void work() = 0;
|
||||||
|
virtual ~BaseWork() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class C, class M, class U>
|
||||||
|
struct Work : public BaseWork {
|
||||||
|
C *instance;
|
||||||
|
M method;
|
||||||
|
U userdata;
|
||||||
|
virtual void work() {
|
||||||
|
while (true) {
|
||||||
|
uint32_t work_index = index->fetch_add(1, std::memory_order_relaxed);
|
||||||
|
if (work_index >= max_elements) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
(instance->*method)(work_index, userdata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadData {
|
||||||
|
std::thread thread;
|
||||||
|
Semaphore start;
|
||||||
|
Semaphore completed;
|
||||||
|
std::atomic<bool> exit;
|
||||||
|
BaseWork *work;
|
||||||
|
};
|
||||||
|
|
||||||
|
ThreadData *threads = nullptr;
|
||||||
|
uint32_t thread_count = 0;
|
||||||
|
uint32_t threads_working = 0;
|
||||||
|
BaseWork *current_work = nullptr;
|
||||||
|
|
||||||
|
static void _thread_function(void *p_user) {
|
||||||
|
ThreadData *thread = static_cast<ThreadData *>(p_user);
|
||||||
|
while (true) {
|
||||||
|
thread->start.wait();
|
||||||
|
if (thread->exit.load()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
thread->work->work();
|
||||||
|
thread->completed.post();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <class C, class M, class U>
|
||||||
|
void begin_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
|
||||||
|
ERR_FAIL_COND(!threads); // never initialized
|
||||||
|
ERR_FAIL_COND(current_work != nullptr);
|
||||||
|
|
||||||
|
index.store(0, std::memory_order_release);
|
||||||
|
|
||||||
|
Work<C, M, U> *w = new (Work<C, M, U>);
|
||||||
|
w->instance = p_instance;
|
||||||
|
w->userdata = p_userdata;
|
||||||
|
w->method = p_method;
|
||||||
|
w->index = &index;
|
||||||
|
w->max_elements = p_elements;
|
||||||
|
|
||||||
|
current_work = w;
|
||||||
|
|
||||||
|
threads_working = Math::min(p_elements, thread_count);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < threads_working; i++) {
|
||||||
|
threads[i].work = w;
|
||||||
|
threads[i].start.post();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_working() const {
|
||||||
|
return current_work != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_done_dispatching() const {
|
||||||
|
ERR_FAIL_COND_V(current_work == nullptr, true);
|
||||||
|
return index.load(std::memory_order_acquire) >= current_work->max_elements;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t get_work_index() const {
|
||||||
|
ERR_FAIL_COND_V(current_work == nullptr, 0);
|
||||||
|
uint32_t idx = index.load(std::memory_order_acquire);
|
||||||
|
return Math::min(idx, current_work->max_elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
void end_work() {
|
||||||
|
ERR_FAIL_COND(current_work == nullptr);
|
||||||
|
for (uint32_t i = 0; i < threads_working; i++) {
|
||||||
|
threads[i].completed.wait();
|
||||||
|
threads[i].work = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
threads_working = 0;
|
||||||
|
delete current_work;
|
||||||
|
current_work = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class C, class M, class U>
|
||||||
|
void do_work(uint32_t p_elements, C *p_instance, M p_method, U p_userdata) {
|
||||||
|
switch (p_elements) {
|
||||||
|
case 0:
|
||||||
|
// Nothing to do, so do nothing.
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
// No value in pushing the work to another thread if it's a single job
|
||||||
|
// and we're going to wait for it to finish. Just run it right here.
|
||||||
|
(p_instance->*p_method)(0, p_userdata);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Multiple jobs to do; commence threaded business.
|
||||||
|
begin_work(p_elements, p_instance, p_method, p_userdata);
|
||||||
|
end_work();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int get_thread_count() const { return thread_count; }
|
||||||
|
void init(int p_thread_count = -1) {
|
||||||
|
ERR_FAIL_COND(threads != nullptr);
|
||||||
|
if (p_thread_count < 0) {
|
||||||
|
p_thread_count = OS::get_singleton()->get_processor_count();
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_count = p_thread_count;
|
||||||
|
threads = new ThreadData[thread_count];
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < thread_count; i++) {
|
||||||
|
threads[i].exit.store(false);
|
||||||
|
threads[i].thread = std::thread(&ThreadWorkPool::_thread_function, &threads[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void finish() {
|
||||||
|
if (threads == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < thread_count; i++) {
|
||||||
|
threads[i].exit.store(true);
|
||||||
|
threads[i].start.post();
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < thread_count; i++) {
|
||||||
|
threads[i].thread.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[](threads);
|
||||||
|
threads = nullptr;
|
||||||
|
}
|
||||||
|
~ThreadWorkPool() {
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! THREAD_WORK_POOL_HPP
|
|
@ -0,0 +1,321 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* vector.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 VECTOR_HPP
|
||||||
|
#define VECTOR_HPP
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Vector
|
||||||
|
* Vector container. Regular Vector Container. Use with care and for smaller arrays when possible. Use Vector for large arrays.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <godot_cpp/core/error_macros.hpp>
|
||||||
|
#include <godot_cpp/core/memory.hpp>
|
||||||
|
#include <godot_cpp/templates/cowdata.hpp>
|
||||||
|
#include <godot_cpp/templates/search_array.hpp>
|
||||||
|
#include <godot_cpp/templates/sort_array.hpp>
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <initializer_list>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class VectorWriteProxy {
|
||||||
|
public:
|
||||||
|
_FORCE_INLINE_ T &operator[](int p_index) {
|
||||||
|
CRASH_BAD_INDEX(p_index, ((Vector<T> *)(this))->_cowdata.size());
|
||||||
|
|
||||||
|
return ((Vector<T> *)(this))->_cowdata.ptrw()[p_index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class Vector {
|
||||||
|
friend class VectorWriteProxy<T>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
VectorWriteProxy<T> write;
|
||||||
|
|
||||||
|
private:
|
||||||
|
CowData<T> _cowdata;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool push_back(T p_elem);
|
||||||
|
_FORCE_INLINE_ bool append(const T &p_elem) { return push_back(p_elem); } // alias
|
||||||
|
void fill(T p_elem);
|
||||||
|
|
||||||
|
void remove_at(int p_index) { _cowdata.remove_at(p_index); }
|
||||||
|
void erase(const T &p_val) {
|
||||||
|
int idx = find(p_val);
|
||||||
|
if (idx >= 0) {
|
||||||
|
remove_at(idx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void reverse();
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T *ptrw() { return _cowdata.ptrw(); }
|
||||||
|
_FORCE_INLINE_ const T *ptr() const { return _cowdata.ptr(); }
|
||||||
|
_FORCE_INLINE_ void clear() { resize(0); }
|
||||||
|
_FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ T get(int p_index) { return _cowdata.get(p_index); }
|
||||||
|
_FORCE_INLINE_ const T &get(int p_index) const { return _cowdata.get(p_index); }
|
||||||
|
_FORCE_INLINE_ void set(int p_index, const T &p_elem) { _cowdata.set(p_index, p_elem); }
|
||||||
|
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
|
||||||
|
Error resize(int p_size) { return _cowdata.resize(p_size); }
|
||||||
|
_FORCE_INLINE_ const T &operator[](int p_index) const { return _cowdata.get(p_index); }
|
||||||
|
Error insert(int p_pos, T p_val) { return _cowdata.insert(p_pos, p_val); }
|
||||||
|
int find(const T &p_val, int p_from = 0) const { return _cowdata.find(p_val, p_from); }
|
||||||
|
|
||||||
|
void append_array(Vector<T> p_other);
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool has(const T &p_val) const { return find(p_val) != -1; }
|
||||||
|
|
||||||
|
template <class C>
|
||||||
|
void sort_custom() {
|
||||||
|
int len = _cowdata.size();
|
||||||
|
if (len == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *data = ptrw();
|
||||||
|
SortArray<T, C> sorter;
|
||||||
|
sorter.sort(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void sort() {
|
||||||
|
sort_custom<_DefaultComparator<T>>();
|
||||||
|
}
|
||||||
|
|
||||||
|
int bsearch(const T &p_value, bool p_before) {
|
||||||
|
SearchArray<T> search;
|
||||||
|
return search.bisect(ptrw(), size(), p_value, p_before);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<T> duplicate() {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ordered_insert(const T &p_val) {
|
||||||
|
int i;
|
||||||
|
for (i = 0; i < _cowdata.size(); i++) {
|
||||||
|
if (p_val < operator[](i)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
insert(i, p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void operator=(const Vector &p_from) {
|
||||||
|
_cowdata._ref(p_from._cowdata);
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<uint8_t> to_byte_array() const {
|
||||||
|
Vector<uint8_t> ret;
|
||||||
|
ret.resize(size() * sizeof(T));
|
||||||
|
memcpy(ret.ptrw(), ptr(), sizeof(T) * size());
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vector<T> slice(int p_begin, int p_end = INT_MAX) const {
|
||||||
|
Vector<T> result;
|
||||||
|
|
||||||
|
const int s = size();
|
||||||
|
|
||||||
|
int begin = Math::clamp(p_begin, -s, s);
|
||||||
|
if (begin < 0) {
|
||||||
|
begin += s;
|
||||||
|
}
|
||||||
|
int end = Math::clamp(p_end, -s, s);
|
||||||
|
if (end < 0) {
|
||||||
|
end += s;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERR_FAIL_COND_V(begin > end, result);
|
||||||
|
|
||||||
|
int result_size = end - begin;
|
||||||
|
result.resize(result_size);
|
||||||
|
|
||||||
|
const T *const r = ptr();
|
||||||
|
T *const w = result.ptrw();
|
||||||
|
for (int i = 0; i < result_size; ++i) {
|
||||||
|
w[i] = r[begin + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const Vector<T> &p_arr) const {
|
||||||
|
int s = size();
|
||||||
|
if (s != p_arr.size()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
if (operator[](i) != p_arr[i]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Vector<T> &p_arr) const {
|
||||||
|
int s = size();
|
||||||
|
if (s != p_arr.size()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < s; i++) {
|
||||||
|
if (operator[](i) != p_arr[i]) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Iterator {
|
||||||
|
_FORCE_INLINE_ T &operator*() const {
|
||||||
|
return *elem_ptr;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ T *operator->() const { return elem_ptr; }
|
||||||
|
_FORCE_INLINE_ Iterator &operator++() {
|
||||||
|
elem_ptr++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator &operator--() {
|
||||||
|
elem_ptr--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const Iterator &b) const { return elem_ptr == b.elem_ptr; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const Iterator &b) const { return elem_ptr != b.elem_ptr; }
|
||||||
|
|
||||||
|
Iterator(T *p_ptr) { elem_ptr = p_ptr; }
|
||||||
|
Iterator() {}
|
||||||
|
Iterator(const Iterator &p_it) { elem_ptr = p_it.elem_ptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
T *elem_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ConstIterator {
|
||||||
|
_FORCE_INLINE_ const T &operator*() const {
|
||||||
|
return *elem_ptr;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ const T *operator->() const { return elem_ptr; }
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator++() {
|
||||||
|
elem_ptr++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator &operator--() {
|
||||||
|
elem_ptr--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; }
|
||||||
|
_FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; }
|
||||||
|
|
||||||
|
ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; }
|
||||||
|
ConstIterator() {}
|
||||||
|
ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
const T *elem_ptr = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Iterator begin() {
|
||||||
|
return Iterator(ptrw());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Iterator end() {
|
||||||
|
return Iterator(ptrw() + size());
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ConstIterator begin() const {
|
||||||
|
return ConstIterator(ptr());
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ ConstIterator end() const {
|
||||||
|
return ConstIterator(ptr() + size());
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Vector() {}
|
||||||
|
_FORCE_INLINE_ Vector(std::initializer_list<T> p_init) {
|
||||||
|
Error err = _cowdata.resize(p_init.size());
|
||||||
|
ERR_FAIL_COND(err);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (const T &element : p_init) {
|
||||||
|
_cowdata.set(i++, element);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_FORCE_INLINE_ Vector(const Vector &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ ~Vector() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Vector<T>::reverse() {
|
||||||
|
for (int i = 0; i < size() / 2; i++) {
|
||||||
|
T *p = ptrw();
|
||||||
|
SWAP(p[i], p[size() - i - 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Vector<T>::append_array(Vector<T> p_other) {
|
||||||
|
const int ds = p_other.size();
|
||||||
|
if (ds == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const int bs = size();
|
||||||
|
resize(bs + ds);
|
||||||
|
for (int i = 0; i < ds; ++i) {
|
||||||
|
ptrw()[bs + i] = p_other[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
bool Vector<T>::push_back(T p_elem) {
|
||||||
|
Error err = resize(size() + 1);
|
||||||
|
ERR_FAIL_COND_V(err, true);
|
||||||
|
set(size() - 1, p_elem);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
void Vector<T>::fill(T p_elem) {
|
||||||
|
T *p = ptrw();
|
||||||
|
for (int i = 0; i < size(); i++) {
|
||||||
|
p[i] = p_elem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! VECTOR_HPP
|
|
@ -0,0 +1,204 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* vmap.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 VMAP_HPP
|
||||||
|
#define VMAP_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/templates/cowdata.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T, class V>
|
||||||
|
class VMap {
|
||||||
|
public:
|
||||||
|
struct Pair {
|
||||||
|
T key;
|
||||||
|
V value;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Pair() {}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ Pair(const T &p_key, const V &p_value) {
|
||||||
|
key = p_key;
|
||||||
|
value = p_value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
CowData<Pair> _cowdata;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
|
||||||
|
r_exact = false;
|
||||||
|
if (_cowdata.is_empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0;
|
||||||
|
int high = _cowdata.size() - 1;
|
||||||
|
const Pair *a = _cowdata.ptr();
|
||||||
|
int middle = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (low > high) {
|
||||||
|
ERR_PRINT("low > high, this may be a bug");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
while (low <= high) {
|
||||||
|
middle = (low + high) / 2;
|
||||||
|
|
||||||
|
if (p_val < a[middle].key) {
|
||||||
|
high = middle - 1; // search low end of array
|
||||||
|
} else if (a[middle].key < p_val) {
|
||||||
|
low = middle + 1; // search high end of array
|
||||||
|
} else {
|
||||||
|
r_exact = true;
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the position where this would be inserted
|
||||||
|
if (a[middle].key < p_val) {
|
||||||
|
middle++;
|
||||||
|
}
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int _find_exact(const T &p_val) const {
|
||||||
|
if (_cowdata.is_empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0;
|
||||||
|
int high = _cowdata.size() - 1;
|
||||||
|
int middle;
|
||||||
|
const Pair *a = _cowdata.ptr();
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
middle = (low + high) / 2;
|
||||||
|
|
||||||
|
if (p_val < a[middle].key) {
|
||||||
|
high = middle - 1; // search low end of array
|
||||||
|
} else if (a[middle].key < p_val) {
|
||||||
|
low = middle + 1; // search high end of array
|
||||||
|
} else {
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
int insert(const T &p_key, const V &p_val) {
|
||||||
|
bool exact;
|
||||||
|
int pos = _find(p_key, exact);
|
||||||
|
if (exact) {
|
||||||
|
_cowdata.get_m(pos).value = p_val;
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
_cowdata.insert(pos, Pair(p_key, p_val));
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const T &p_val) const {
|
||||||
|
return _find_exact(p_val) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(const T &p_val) {
|
||||||
|
int pos = _find_exact(p_val);
|
||||||
|
if (pos < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_cowdata.remove_at(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find(const T &p_val) const {
|
||||||
|
return _find_exact(p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find_nearest(const T &p_val) const {
|
||||||
|
bool exact;
|
||||||
|
return _find(p_val, exact);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int size() const { return _cowdata.size(); }
|
||||||
|
_FORCE_INLINE_ bool is_empty() const { return _cowdata.is_empty(); }
|
||||||
|
|
||||||
|
const Pair *get_array() const {
|
||||||
|
return _cowdata.ptr();
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair *get_array() {
|
||||||
|
return _cowdata.ptrw();
|
||||||
|
}
|
||||||
|
|
||||||
|
const V &getv(int p_index) const {
|
||||||
|
return _cowdata.get(p_index).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
V &getv(int p_index) {
|
||||||
|
return _cowdata.get_m(p_index).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const T &getk(int p_index) const {
|
||||||
|
return _cowdata.get(p_index).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
T &getk(int p_index) {
|
||||||
|
return _cowdata.get_m(p_index).key;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const V &operator[](const T &p_key) const {
|
||||||
|
int pos = _find_exact(p_key);
|
||||||
|
|
||||||
|
CRASH_COND(pos < 0);
|
||||||
|
|
||||||
|
return _cowdata.get(pos).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline V &operator[](const T &p_key) {
|
||||||
|
int pos = _find_exact(p_key);
|
||||||
|
if (pos < 0) {
|
||||||
|
pos = insert(p_key, V());
|
||||||
|
}
|
||||||
|
|
||||||
|
return _cowdata.get_m(pos).value;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ VMap() {}
|
||||||
|
_FORCE_INLINE_ VMap(const VMap &p_from) { _cowdata._ref(p_from._cowdata); }
|
||||||
|
|
||||||
|
inline void operator=(const VMap &p_from) {
|
||||||
|
_cowdata._ref(p_from._cowdata);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // ! VMAP_H
|
|
@ -0,0 +1,145 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* vset.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 VSET_HPP
|
||||||
|
#define VSET_HPP
|
||||||
|
|
||||||
|
#include <godot_cpp/templates/vector.hpp>
|
||||||
|
|
||||||
|
namespace godot {
|
||||||
|
|
||||||
|
template <class T>
|
||||||
|
class VSet {
|
||||||
|
Vector<T> _data;
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int _find(const T &p_val, bool &r_exact) const {
|
||||||
|
r_exact = false;
|
||||||
|
if (_data.is_empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0;
|
||||||
|
int high = _data.size() - 1;
|
||||||
|
const T *a = &_data[0];
|
||||||
|
int middle = 0;
|
||||||
|
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
if (low > high) {
|
||||||
|
ERR_PRINT("low > high, this may be a bug");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
middle = (low + high) / 2;
|
||||||
|
|
||||||
|
if (p_val < a[middle]) {
|
||||||
|
high = middle - 1; // search low end of array
|
||||||
|
} else if (a[middle] < p_val) {
|
||||||
|
low = middle + 1; // search high end of array
|
||||||
|
} else {
|
||||||
|
r_exact = true;
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the position where this would be inserted
|
||||||
|
if (a[middle] < p_val) {
|
||||||
|
middle++;
|
||||||
|
}
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int _find_exact(const T &p_val) const {
|
||||||
|
if (_data.is_empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int low = 0;
|
||||||
|
int high = _data.size() - 1;
|
||||||
|
int middle;
|
||||||
|
const T *a = &_data[0];
|
||||||
|
|
||||||
|
while (low <= high) {
|
||||||
|
middle = (low + high) / 2;
|
||||||
|
|
||||||
|
if (p_val < a[middle]) {
|
||||||
|
high = middle - 1; // search low end of array
|
||||||
|
} else if (a[middle] < p_val) {
|
||||||
|
low = middle + 1; // search high end of array
|
||||||
|
} else {
|
||||||
|
return middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
void insert(const T &p_val) {
|
||||||
|
bool exact;
|
||||||
|
int pos = _find(p_val, exact);
|
||||||
|
if (exact) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_data.insert(pos, p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has(const T &p_val) const {
|
||||||
|
return _find_exact(p_val) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void erase(const T &p_val) {
|
||||||
|
int pos = _find_exact(p_val);
|
||||||
|
if (pos < 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_data.remove_at(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
int find(const T &p_val) const {
|
||||||
|
return _find_exact(p_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
_FORCE_INLINE_ bool is_empty() const { return _data.is_empty(); }
|
||||||
|
|
||||||
|
_FORCE_INLINE_ int size() const { return _data.size(); }
|
||||||
|
|
||||||
|
inline T &operator[](int p_index) {
|
||||||
|
return _data.write[p_index];
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const T &operator[](int p_index) const {
|
||||||
|
return _data[p_index];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace godot
|
||||||
|
|
||||||
|
#endif // VSET_H
|
|
@ -0,0 +1,90 @@
|
||||||
|
/*************************************************************************/
|
||||||
|
/* char_utils.hpp */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* This file is part of: */
|
||||||
|
/* GODOT ENGINE */
|
||||||
|
/* https://godotengine.org */
|
||||||
|
/*************************************************************************/
|
||||||
|
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
||||||
|
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
|
||||||
|
/* */
|
||||||
|
/* 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 CHAR_UTILS_HPP
|
||||||
|
#define CHAR_UTILS_HPP
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_ascii_upper_case(char32_t c) {
|
||||||
|
return (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_ascii_lower_case(char32_t c) {
|
||||||
|
return (c >= 'a' && c <= 'z');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_digit(char32_t c) {
|
||||||
|
return (c >= '0' && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_hex_digit(char32_t c) {
|
||||||
|
return (is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_binary_digit(char32_t c) {
|
||||||
|
return (c == '0' || c == '1');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_ascii_char(char32_t c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_ascii_alphanumeric_char(char32_t c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_ascii_identifier_char(char32_t c) {
|
||||||
|
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_symbol(char32_t c) {
|
||||||
|
return c != '_' && ((c >= '!' && c <= '/') || (c >= ':' && c <= '@') || (c >= '[' && c <= '`') || (c >= '{' && c <= '~') || c == '\t' || c == ' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_control(char32_t p_char) {
|
||||||
|
return (p_char <= 0x001f) || (p_char >= 0x007f && p_char <= 0x009f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_whitespace(char32_t p_char) {
|
||||||
|
return (p_char == ' ') || (p_char == 0x00a0) || (p_char == 0x1680) || (p_char >= 0x2000 && p_char <= 0x200a) || (p_char == 0x202f) || (p_char == 0x205f) || (p_char == 0x3000) || (p_char == 0x2028) || (p_char == 0x2029) || (p_char >= 0x0009 && p_char <= 0x000d) || (p_char == 0x0085);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_linebreak(char32_t p_char) {
|
||||||
|
return (p_char >= 0x000a && p_char <= 0x000d) || (p_char == 0x0085) || (p_char == 0x2028) || (p_char == 0x2029);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_punct(char32_t p_char) {
|
||||||
|
return (p_char >= ' ' && p_char <= '/') || (p_char >= ':' && p_char <= '@') || (p_char >= '[' && p_char <= '^') || (p_char == '`') || (p_char >= '{' && p_char <= '~') || (p_char >= 0x2000 && p_char <= 0x206f) || (p_char >= 0x3000 && p_char <= 0x303f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _FORCE_INLINE_ bool is_underscore(char32_t p_char) {
|
||||||
|
return (p_char == '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CHAR_UTILS_HPP
|
File diff suppressed because it is too large
Load Diff
|
@ -46,12 +46,24 @@ void Memory::free_static(void *p_ptr) {
|
||||||
internal::gdn_interface->mem_free(p_ptr);
|
internal::gdn_interface->mem_free(p_ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_GlobalNil::_GlobalNil() {
|
||||||
|
left = this;
|
||||||
|
right = this;
|
||||||
|
parent = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
_GlobalNil _GlobalNilClass::_nil;
|
||||||
|
|
||||||
} // namespace godot
|
} // namespace godot
|
||||||
|
|
||||||
void *operator new(size_t p_size, const char *p_description) {
|
void *operator new(size_t p_size, const char *p_description) {
|
||||||
return godot::Memory::alloc_static(p_size);
|
return godot::Memory::alloc_static(p_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *operator new(size_t p_size, void *(*p_allocfunc)(size_t p_size)) {
|
||||||
|
return p_allocfunc(p_size);
|
||||||
|
}
|
||||||
|
|
||||||
using namespace godot;
|
using namespace godot;
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
@ -69,4 +81,5 @@ void operator delete(void *p_mem, void *p_pointer, size_t check, const char *p_d
|
||||||
ERR_PRINT("Call to placement delete should not happen.");
|
ERR_PRINT("Call to placement delete should not happen.");
|
||||||
CRASH_NOW();
|
CRASH_NOW();
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue