From f897b42b379cfe8a8bcbf5a3938e5c5f8860eeff Mon Sep 17 00:00:00 2001 From: Relintai Date: Sat, 21 Aug 2021 13:47:58 +0200 Subject: [PATCH] Added a godot's Reference class and a few smaller headers. --- core/int_types.h | 57 +++++++ core/memory.h | 12 ++ core/object.h | 20 +++ core/object_id.h | 38 +++++ core/reference.cpp | 53 ++++++ core/reference.h | 201 +++++++++++++++++++++++ core/safe_refcount.cpp | 45 ++++++ core/safe_refcount.h | 333 ++++++++++++++++++++++++++++++++++++++ core/typedefs.h | 354 +++++++++++++++++++++++++++++++++++++++++ 9 files changed, 1113 insertions(+) create mode 100644 core/int_types.h create mode 100644 core/memory.h create mode 100644 core/object_id.h create mode 100644 core/reference.cpp create mode 100644 core/reference.h create mode 100644 core/safe_refcount.cpp create mode 100644 core/safe_refcount.h create mode 100644 core/typedefs.h diff --git a/core/int_types.h b/core/int_types.h new file mode 100644 index 0000000..c419cf9 --- /dev/null +++ b/core/int_types.h @@ -0,0 +1,57 @@ +/*************************************************************************/ +/* int_types.h */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ + +#ifdef _MSC_VER + +typedef signed __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef signed __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef signed __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef signed __int64 int64_t; +typedef unsigned __int64 uint64_t; + +#else + +#ifdef NO_STDINT_H +typedef unsigned char uint8_t; +typedef signed char int8_t; +typedef unsigned short uint16_t; +typedef signed short int16_t; +typedef unsigned int uint32_t; +typedef signed int int32_t; +typedef long long int64_t; +typedef unsigned long long uint64_t; +#else +#include +#endif + +#endif diff --git a/core/memory.h b/core/memory.h new file mode 100644 index 0000000..2402fb8 --- /dev/null +++ b/core/memory.h @@ -0,0 +1,12 @@ +#ifndef MEMORY_H +#define MEMORY_H + +//Simple memnew and memdelete macros so stuff that I took from the godotengine can use it. +//Not yet sure whether to use their allocator or not. +//This will be here until I decide. + +#define memnew(m_class) new m_class +#define memdelete(instance) delete instance + +#endif + diff --git a/core/object.h b/core/object.h index 9ff6bb2..657dc7e 100644 --- a/core/object.h +++ b/core/object.h @@ -75,6 +75,26 @@ public: Object(); virtual ~Object(); + + template + static T *cast_to(Object *p_object) { + if (!p_object) + return NULL; + if (p_object->is_class_ptr(T::get_class_ptr_static())) + return static_cast(p_object); + else + return NULL; + } + + template + static const T *cast_to(const Object *p_object) { + if (!p_object) + return NULL; + if (p_object->is_class_ptr(T::get_class_ptr_static())) + return static_cast(p_object); + else + return NULL; + } }; #endif \ No newline at end of file diff --git a/core/object_id.h b/core/object_id.h new file mode 100644 index 0000000..ea5c135 --- /dev/null +++ b/core/object_id.h @@ -0,0 +1,38 @@ +/*************************************************************************/ +/* object_id.h */ +/*************************************************************************/ +/* 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 OBJECTID_H +#define OBJECTID_H + +#include "core/int_types.h" + +typedef uint64_t ObjectID; + +#endif diff --git a/core/reference.cpp b/core/reference.cpp new file mode 100644 index 0000000..6210764 --- /dev/null +++ b/core/reference.cpp @@ -0,0 +1,53 @@ +#include "reference.h" + +bool Reference::init_ref() { + if (reference()) { + if (!is_referenced() && refcount_init.unref()) { + unreference(); // first referencing is already 1, so compensate for the ref above + } + + return true; + } else { + return false; + } +} + +int Reference::reference_get_count() const { + return refcount.get(); +} + +bool Reference::reference() { + uint32_t rc_val = refcount.refval(); + bool success = rc_val != 0; + + return success; +} + +bool Reference::unreference() { + uint32_t rc_val = refcount.unrefval(); + bool die = rc_val == 0; + + return die; +} + +Reference::Reference() { + refcount.init(); + refcount_init.init(); +} + +Reference::~Reference() { +} + +/* +void WeakRef::set_obj(Object *p_object) { + //ref = p_object ? p_object->get_instance_id() : 0; +} + +void WeakRef::set_ref(const REF &p_ref) { + //ref = p_ref.is_valid() ? p_ref->get_instance_id() : 0; +} + +WeakRef::WeakRef() : + ref(0) { +} +*/ \ No newline at end of file diff --git a/core/reference.h b/core/reference.h new file mode 100644 index 0000000..b5f4f2f --- /dev/null +++ b/core/reference.h @@ -0,0 +1,201 @@ +#ifndef REFERENCE_H +#define REFERENCE_H + +// Most of the code is from the godot engine's reference.h +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ + + +#include "safe_refcount.h" +#include "object.h" +#include "object_id.h" +#include "memory.h" + +class Reference : public Object { + RCPP_OBJECT(Reference, Object); + +public: + /*_FORCE_INLINE_*/ bool is_referenced() const { return refcount_init.get() != 1; } + bool init_ref(); + bool reference(); // returns false if refcount is at zero and didn't get increased + bool unreference(); + int reference_get_count() const; + + Reference(); + virtual ~Reference(); + +private: + SafeRefCount refcount; + SafeRefCount refcount_init; +}; + +template +class Ref { + T *reference; + + void ref(const Ref &p_from) { + if (p_from.reference == reference) { + return; + } + + unref(); + + reference = p_from.reference; + if (reference) { + reference->reference(); + } + } + + void ref_pointer(T *p_ref) { + ERR_FAIL_COND(!p_ref); + + if (p_ref->init_ref()) { + reference = p_ref; + } + } + + //virtual Reference * get_reference() const { return reference; } +public: + _FORCE_INLINE_ bool operator==(const T *p_ptr) const { + return reference == p_ptr; + } + _FORCE_INLINE_ bool operator!=(const T *p_ptr) const { + return reference != p_ptr; + } + + _FORCE_INLINE_ bool operator<(const Ref &p_r) const { + return reference < p_r.reference; + } + _FORCE_INLINE_ bool operator==(const Ref &p_r) const { + return reference == p_r.reference; + } + _FORCE_INLINE_ bool operator!=(const Ref &p_r) const { + return reference != p_r.reference; + } + + _FORCE_INLINE_ T *operator->() { + return reference; + } + + _FORCE_INLINE_ T *operator*() { + return reference; + } + + _FORCE_INLINE_ const T *operator->() const { + return reference; + } + + _FORCE_INLINE_ const T *ptr() const { + return reference; + } + _FORCE_INLINE_ T *ptr() { + return reference; + } + + _FORCE_INLINE_ const T *operator*() const { + return reference; + } + + void operator=(const Ref &p_from) { + ref(p_from); + } + + template + void operator=(const Ref &p_from) { + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + template + void reference_ptr(T_Other *p_ptr) { + if (reference == p_ptr) { + return; + } + unref(); + + T *r = Object::cast_to(p_ptr); + if (r) { + ref_pointer(r); + } + } + + Ref(const Ref &p_from) { + reference = nullptr; + ref(p_from); + } + + template + Ref(const Ref &p_from) { + reference = nullptr; + Reference *refb = const_cast(static_cast(p_from.ptr())); + if (!refb) { + unref(); + return; + } + Ref r; + r.reference = Object::cast_to(refb); + ref(r); + r.reference = nullptr; + } + + Ref(T *p_reference) { + reference = nullptr; + if (p_reference) { + ref_pointer(p_reference); + } + } + + inline bool is_valid() const { return reference != nullptr; } + inline bool is_null() const { return reference == nullptr; } + + void unref() { + //TODO this should be moved to mutexes, since this engine does not really + // do a lot of referencing on references and stuff + // mutexes will avoid more crashes? + + if (reference && reference->unreference()) { + memdelete(reference); + } + reference = nullptr; + } + + void instance() { + ref(memnew(T)); + } + + Ref() { + reference = nullptr; + } + + ~Ref() { + unref(); + } +}; + +typedef Ref REF; + +/* +class WeakRef : public Reference { + RCPP_OBJECT(WeakRef, Reference); + + ObjectID ref; + +protected: + static void _bind_methods(); + +public: + void set_obj(Object *p_object); + void set_ref(const REF &p_ref); + + WeakRef(); +}; +*/ + +#endif \ No newline at end of file diff --git a/core/safe_refcount.cpp b/core/safe_refcount.cpp new file mode 100644 index 0000000..033086c --- /dev/null +++ b/core/safe_refcount.cpp @@ -0,0 +1,45 @@ +/*************************************************************************/ +/* safe_refcount.cpp */ +/*************************************************************************/ +/* 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. */ +/*************************************************************************/ + +#if defined(DEBUG_ENABLED) && !defined(NO_THREADS) + +#include "safe_refcount.h" + +#include "core/error_macros.h" + +// On C++14 we don't have std::atomic::is_always_lockfree, so this is the best we can do +void check_lockless_atomics() { + // Doing the check for the types we actually care about + if (!std::atomic{}.is_lock_free() || !std::atomic{}.is_lock_free() || !std::atomic_bool{}.is_lock_free()) { + WARN_PRINT("Your compiler doesn't seem to support lockless atomics. Performance will be degraded. Please consider upgrading to a different or newer compiler."); + } +} + +#endif diff --git a/core/safe_refcount.h b/core/safe_refcount.h new file mode 100644 index 0000000..4fae30e --- /dev/null +++ b/core/safe_refcount.h @@ -0,0 +1,333 @@ +/*************************************************************************/ +/* safe_refcount.h */ +/*************************************************************************/ +/* 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_H +#define SAFE_REFCOUNT_H + +#include "core/typedefs.h" + +#if !defined(NO_THREADS) + +#include +#include + +// 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. + +// This is used in very specific areas of the engine where it's critical that these guarantees are held +#define SAFE_NUMERIC_TYPE_PUN_GUARANTEES(m_type) \ + static_assert(sizeof(SafeNumeric) == sizeof(m_type), ""); \ + static_assert(alignof(SafeNumeric) == alignof(m_type), ""); \ + static_assert(std::is_trivially_destructible>::value, ""); + +#if defined(DEBUG_ENABLED) +void check_lockless_atomics(); +#endif + +template +class SafeNumeric { + std::atomic value; + +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 p_value = static_cast(0)) { + set(p_value); + } +}; + +class SafeFlag { + std::atomic_bool flag; + +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 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 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 p_value = static_cast(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; + +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; + } + + SafeRefCount() : + count(0) {} +}; + +#endif + +#endif // SAFE_REFCOUNT_H diff --git a/core/typedefs.h b/core/typedefs.h new file mode 100644 index 0000000..f712d43 --- /dev/null +++ b/core/typedefs.h @@ -0,0 +1,354 @@ +/*************************************************************************/ +/* typedefs.h */ +/*************************************************************************/ +/* 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 TYPEDEFS_H +#define TYPEDEFS_H + +#include + +/** + * Basic definitions and simple functions to be used everywhere. + */ + +//#include "platform_config.h" + +#ifndef _STR +#define _STR(m_x) #m_x +#define _MKSTR(m_x) _STR(m_x) +#endif + +//should always inline no matter what +#ifndef _ALWAYS_INLINE_ + +#if defined(__GNUC__) && (__GNUC__ >= 4) +#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline +#elif defined(__llvm__) +#define _ALWAYS_INLINE_ __attribute__((always_inline)) inline +#elif defined(_MSC_VER) +#define _ALWAYS_INLINE_ __forceinline +#else +#define _ALWAYS_INLINE_ inline +#endif + +#endif + +//should always inline, except in some cases because it makes debugging harder +#ifndef _FORCE_INLINE_ + +#ifdef DISABLE_FORCED_INLINE +#define _FORCE_INLINE_ inline +#else +#define _FORCE_INLINE_ _ALWAYS_INLINE_ +#endif + +#endif + +//custom, gcc-safe offsetof, because gcc complains a lot. +template +T *_nullptr() { + T *t = NULL; + return t; +} + +#define OFFSET_OF(st, m) \ + ((size_t)((char *)&(_nullptr()->m) - (char *)0)) +/** + * Some platforms (devices) don't define NULL + */ + +#ifndef NULL +#define NULL 0 +#endif + +/** + * 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 ERROR // override (really stupid) wingdi.h standard definition +#undef DELETE // override (another really stupid) winnt.h standard definition +#undef MessageBox // override winuser.h standard definition +#undef MIN // override standard definition +#undef MAX // override standard definition +#undef CLAMP // override standard definition +#undef Error +#undef OK +#undef CONNECT_DEFERRED // override from Windows SDK, clashes with Object enum +#endif + +#include "int_types.h" + +//#include "core/error_list.h" + +/** Generic ABS function, for math uses please use Math::abs */ + +#ifndef ABS +#define ABS(m_v) (((m_v) < 0) ? (-(m_v)) : (m_v)) +#endif + +#define ABSDIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y))) + +#ifndef SGN +#define SGN(m_v) (((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 + +/** Generic swap template */ +#ifndef SWAP + +#define SWAP(m_x, m_y) __swap_tmpl((m_x), (m_y)) +template +inline void __swap_tmpl(T &x, T &y) { + T aux = x; + x = y; + y = aux; +} + +#endif //swap + +/* clang-format off */ +#define HEX2CHR(m_hex) \ + ((m_hex >= '0' && m_hex <= '9') ? (m_hex - '0') : \ + ((m_hex >= 'A' && m_hex <= 'F') ? (10 + m_hex - 'A') : \ + ((m_hex >= 'a' && m_hex <= 'f') ? (10 + m_hex - 'a') : 0))) +/* clang-format on */ + +// Macro to check whether we are compiled by clang +// and we have a specific builtin +#if defined(__llvm__) && defined(__has_builtin) +#define _llvm_has_builtin(x) __has_builtin(x) +#else +#define _llvm_has_builtin(x) 0 +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || _llvm_has_builtin(__builtin_mul_overflow) +#define _mul_overflow __builtin_mul_overflow +#endif + +#if (defined(__GNUC__) && (__GNUC__ >= 5)) || _llvm_has_builtin(__builtin_add_overflow) +#define _add_overflow __builtin_add_overflow +#endif + +/** Function to find the next power of 2 to an integer */ + +static _FORCE_INLINE_ unsigned int next_power_of_2(unsigned int x) { + if (x == 0) { + return 0; + } + + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + + return ++x; +} + +static _FORCE_INLINE_ unsigned int previous_power_of_2(unsigned int x) { + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x - (x >> 1); +} + +static _FORCE_INLINE_ unsigned int closest_power_of_2(unsigned int x) { + unsigned int nx = next_power_of_2(x); + unsigned int px = previous_power_of_2(x); + return (nx - x) > (x - px) ? px : nx; +} + +// We need this definition inside the function below. +static inline int get_shift_from_power_of_2(unsigned int p_pixel); + +template +static _FORCE_INLINE_ T nearest_power_of_2_templated(T x) { + --x; + + // The number of operations on x is the base two logarithm + // of the p_number of bits in the type. Add three to account + // for sizeof(T) being in bytes. + size_t num = get_shift_from_power_of_2(sizeof(T)) + 3; + + // If the compiler is smart, it unrolls this loop + // If its dumb, this is a bit slow. + for (size_t i = 0; i < num; i++) { + x |= x >> (1 << i); + } + + return ++x; +} + +/** Function to find the nearest (bigger) power of 2 to an integer */ + +static inline unsigned int nearest_shift(unsigned int p_number) { + for (int i = 30; i >= 0; i--) { + if (p_number & (1 << i)) { + return i + 1; + } + } + + return 0; +} + +/** get a shift value from a power of 2 */ +static inline int get_shift_from_power_of_2(unsigned int p_pixel) { + // return a GL_TEXTURE_SIZE_ENUM + + for (unsigned int i = 0; i < 32; i++) { + if (p_pixel == (unsigned int)(1 << i)) { + return i; + } + } + + return -1; +} + +/** Swap 16 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap16) +#define BSWAP16(x) __builtin_bswap16(x) +#else +static inline uint16_t BSWAP16(uint16_t x) { + return (x >> 8) | (x << 8); +} +#endif + +/** Swap 32 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap32) +#define BSWAP32(x) __builtin_bswap32(x) +#else +static inline uint32_t BSWAP32(uint32_t x) { + return ((x << 24) | ((x << 8) & 0x00FF0000) | ((x >> 8) & 0x0000FF00) | (x >> 24)); +} +#endif + +/** Swap 64 bits value for endianness */ +#if defined(__GNUC__) || _llvm_has_builtin(__builtin_bswap64) +#define BSWAP64(x) __builtin_bswap64(x) +#else +static inline uint64_t BSWAP64(uint64_t x) { + x = (x & 0x00000000FFFFFFFF) << 32 | (x & 0xFFFFFFFF00000000) >> 32; + x = (x & 0x0000FFFF0000FFFF) << 16 | (x & 0xFFFF0000FFFF0000) >> 16; + x = (x & 0x00FF00FF00FF00FF) << 8 | (x & 0xFF00FF00FF00FF00) >> 8; + return x; +} +#endif + +/** When compiling with RTTI, we can add an "extra" + * layer of safeness in many operations, so dynamic_cast + * is used besides casting by enum. + */ + +template +struct Comparator { + _ALWAYS_INLINE_ bool operator()(const T &p_a, const T &p_b) const { return (p_a < p_b); } +}; + +void _global_lock(); +void _global_unlock(); + +struct _GlobalLock { + _GlobalLock() { _global_lock(); } + ~_GlobalLock() { _global_unlock(); } +}; + +#define GLOBAL_LOCK_FUNCTION _GlobalLock _global_lock_; + +#ifdef NO_SAFE_CAST +#define SAFE_CAST static_cast +#else +#define SAFE_CAST dynamic_cast +#endif + +#define MT_SAFE + +#define __STRX(m_index) #m_index +#define __STR(m_index) __STRX(m_index) + +#ifdef __GNUC__ +#define likely(x) __builtin_expect(!!(x), 1) +#define unlikely(x) __builtin_expect(!!(x), 0) +#else +#define likely(x) x +#define unlikely(x) x +#endif + +#if defined(__GNUC__) +#define _PRINTF_FORMAT_ATTRIBUTE_2_0 __attribute__((format(printf, 2, 0))) +#define _PRINTF_FORMAT_ATTRIBUTE_2_3 __attribute__((format(printf, 2, 3))) +#else +#define _PRINTF_FORMAT_ATTRIBUTE_2_0 +#define _PRINTF_FORMAT_ATTRIBUTE_2_3 +#endif + +/** This is needed due to a strange OpenGL API that expects a pointer + * type for an argument that is actually an offset. + */ +#define CAST_INT_TO_UCHAR_PTR(ptr) ((uint8_t *)(uintptr_t)(ptr)) + +/** Hint for compilers that this fallthrough in a switch is intentional. + * Can be replaced by [[fallthrough]] annotation if we move to C++17. + * Including conditional support for it for people who set -std=c++17 + * themselves. + * Requires a trailing semicolon when used. + */ +#if __cplusplus >= 201703L +#define FALLTHROUGH [[fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define FALLTHROUGH __attribute__((fallthrough)) +#elif defined(__llvm__) && __cplusplus >= 201103L && defined(__has_feature) +#if __has_feature(cxx_attributes) && defined(__has_warning) +#if __has_warning("-Wimplicit-fallthrough") +#define FALLTHROUGH [[clang::fallthrough]] +#endif +#endif +#endif + +#ifndef FALLTHROUGH +#define FALLTHROUGH +#endif + +#endif // TYPEDEFS_H