diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index c895399e..9b399b2b 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -150,6 +150,7 @@ if(ENABLE_DISCORD) Library/DPP/Other.cpp Library/DPP/Other.hpp Library/DPP/Role.cpp Library/DPP/Role.hpp Library/DPP/User.cpp Library/DPP/User.hpp + Library/DPP/Utilities.cpp Library/DPP/Utilities.hpp ) endif() # Link to POCO libraries diff --git a/module/Library/DPP/Utilities.cpp b/module/Library/DPP/Utilities.cpp new file mode 100644 index 00000000..a0589f62 --- /dev/null +++ b/module/Library/DPP/Utilities.cpp @@ -0,0 +1,12 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/DPP/Utilities.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ + +// ------------------------------------------------------------------------------------------------ + + +} // Namespace:: SqMod diff --git a/module/Library/DPP/Utilities.hpp b/module/Library/DPP/Utilities.hpp new file mode 100644 index 00000000..13647d70 --- /dev/null +++ b/module/Library/DPP/Utilities.hpp @@ -0,0 +1,415 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +#include + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Wrapper around a std::vector of DPP values. +*/ +template < class T, class W > struct DpVectorProxy +{ + using Ptr = std::unique_ptr< std::vector< T > >; + using Vec = std::vector< std::pair< LightObj, W * > >; + /* -------------------------------------------------------------------------------------------- + * Referenced vector instance. + */ + Ptr mPtr{nullptr}; + /* -------------------------------------------------------------------------------------------- + * Cached script objects vector. + */ + Vec mVec{}; + /* -------------------------------------------------------------------------------------------- + * Whether the referenced pointer is owned. + */ + bool mOwned{false}; + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + DpVectorProxy() noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVectorProxy(typename Ptr::pointer ptr, bool owned = false) + : mPtr(ptr), mVec(), mOwned(owned) + { if (mPtr) mVec.resize(mPtr->size()); } + /* -------------------------------------------------------------------------------------------- + * Explicit constructor. + */ + explicit DpVectorProxy(const typename Ptr::element_type & o) noexcept + : DpVectorProxy(new typename Ptr::element_type(o), true) + { } + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + explicit DpVectorProxy(typename Ptr::element_type && o) noexcept + : DpVectorProxy(new typename Ptr::element_type(std::forward< Ptr::element_type >(o)), true) + { } + /* -------------------------------------------------------------------------------------------- + * Copy constructor (disabled). + */ + DpVectorProxy(const DpVectorProxy & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + DpVectorProxy(DpVectorProxy && o) noexcept = default; + /* -------------------------------------------------------------------------------------------- + * Destructor. + */ + ~DpVectorProxy() noexcept { Cleanup(); } + /* -------------------------------------------------------------------------------------------- + * Copy assignment operator (disabled). + */ + DpVectorProxy & operator = (const DpVectorProxy & o) = delete; + /* -------------------------------------------------------------------------------------------- + * Move assignment operator. + */ + DpVectorProxy & operator = (DpVectorProxy && o) noexcept + { + if (this != &o) { + Cleanup(); + // Transfer members values + mPtr = std::move(o.mPtr); + mVec = std::move(o.mVec); + mOwned = o.mOwned; + } + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Release any referenced resources and default to an empty/invalid state. + */ + void Cleanup() + { + // Invalidate cached instances + ClearCache(); + // Do we own this to try delete it? + if (!mOwned && mPtr) { + // Not our job, simply forget about it + [[maybe_unused]] auto p = mPtr.release(); + } else mPtr.reset(); // We own this so delete the instance + } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle. + */ + void Validate() const { if (!mPtr) STHROWF("Invalid discord vector handle"); } + /* -------------------------------------------------------------------------------------------- + * Validate the managed handle and retrieve a const reference to it. + */ + SQMOD_NODISCARD typename Ptr::element_type & Valid() const { Validate(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check whether a valid instance is managed. + */ + SQMOD_NODISCARD bool IsValid() const { return static_cast< bool >(mPtr); } + /* -------------------------------------------------------------------------------------------- + * Make sure an index is within range and return the container. Container must exist. + */ + void ValidIdx_(SQInteger i) + { + if (static_cast< size_t >(i) >= Valid().size()) + { + STHROWF("Invalid vector container index({})", i); + } + } + typename Ptr::element_type & ValidIdx(SQInteger i) { ValidIdx_(i); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure an index is within range and return the container. Container must exist. + */ + void ValidIdx_(SQInteger i) const + { + if (static_cast< size_t >(i) >= Valid().size()) + { + STHROWF("Invalid vector container index({})", i); + } + } + const typename Ptr::element_type & ValidIdx(SQInteger i) const { ValidIdx_(i); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure a container instance is referenced and is populated, then return it. + */ + void ValidPop_() + { + if (Valid().empty()) + { + STHROWF("Vector container is empty"); + } + } + typename Ptr::element_type & ValidPop() { ValidPop_(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Make sure a container instance is referenced and is populated, then return it. + */ + void ValidPop_() const + { + if (Valid().empty()) + { + STHROWF("Vector container is empty"); + } + } + const typename Ptr::element_type & ValidPop() const { ValidPop_(); return *mPtr; } + /* -------------------------------------------------------------------------------------------- + * Check if a container instance is referenced. + */ + SQMOD_NODISCARD bool IsNull() const { return !mPtr; } + /* -------------------------------------------------------------------------------------------- + * Retrieve a value from the container. + */ + SQMOD_NODISCARD LightObj & Get_(SQInteger i) + { + // Is the element cached? + if (mVec[static_cast< size_t >(i)].first.IsNull()) + { + mVec[static_cast< size_t >(i)] = Obj(&mPtr->at(static_cast< size_t >(i))); + } + // Return the object from the cache + return mVec[static_cast< size_t >(i)].first; + } + SQMOD_NODISCARD LightObj & Get(SQInteger i) { ValidIdx_(i); return Get_(i); } + /* -------------------------------------------------------------------------------------------- + * Modify a value from the container. + */ + void Set(SQInteger i, const W & v) { ValidIdx(i)[static_cast< size_t >(i)] = v.Valid(); } + /* -------------------------------------------------------------------------------------------- + * Check if the container has no elements. + */ + SQMOD_NODISCARD bool Empty() const { return Valid().empty(); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements in the container. + */ + SQMOD_NODISCARD SQInteger Size() const { return static_cast< SQInteger >(Valid().size()); } + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements that the container has currently allocated space for. + */ + SQMOD_NODISCARD SQInteger Capacity() const { return static_cast< SQInteger >(Valid().capacity()); } + /* -------------------------------------------------------------------------------------------- + * Synchronize cache container instances. + */ + void CacheSync() + { + // Invalidate cached instances, if any + for (size_t i = 0; i < mVec.size(); ++i) + { + // Is this element cached? + if (mVec[i].second != nullptr) + { + // Discard previous instance, if any + [[maybe_unused]] auto _ = mVec[i].second->mPtr.release(); + // Sync to new instance + mVec[i].second->mPtr.reset(&mPtr->at(i)); + } + } + } + /* -------------------------------------------------------------------------------------------- + * Increase the capacity of the container to a value that's greater or equal to the one specified. + */ + DpVectorProxy & Reserve(SQInteger n) + { + Valid().reserve(ClampL< SQInteger, size_t >(n)); + mVec.reserve(mPtr->size()); + CacheSync(); + return *this; + } + /* -------------------------------------------------------------------------------------------- + * Request the removal of unused capacity. + */ + void Compact() { Valid().shrink_to_fit(); CacheSync(); mVec.shrink_to_fit(); } + /* -------------------------------------------------------------------------------------------- + * Erase all elements from the cache container. + */ + void ClearCache() + { + // Invalidate cached instances, if any + for (auto & e : mVec) + { + // Is this element cached? + if (e.second != nullptr) + { + // Invalidate the instance + e.second->Cleanup(); + // Forget about it + e.second = nullptr; + // Release script object + e.first.Release(); + } + } + // Clear the cache vector + mVec.clear(); + } + /* -------------------------------------------------------------------------------------------- + * Erase all elements from the container. + */ + void Clear() { Validate(); ClearCache(); mPtr->clear(); } + /* -------------------------------------------------------------------------------------------- + * Push a value at the back of the container. + */ + void Push(const W & v) + { + Valid().push_back(v.Valid()); + mVec.emplace_back(); + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Extends the Container by appending all the items in the given container. + */ + void Extend(DpVectorProxy & v) + { + Valid().insert(Valid().end(), v.Valid().begin(), v.Valid().end()); + mVec.resize(mPtr->size()); + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Pop the last element in the container. + */ + void Pop() + { + Validate(); + // Is this element cached? + if (mVec.back().second != nullptr) + { + // Invalidate the instance + mVec.back().second->Cleanup(); + mVec.back().first.Release(); + } + // Safe to remove + mPtr->pop_back(); + mVec.pop_back(); + } + /* -------------------------------------------------------------------------------------------- + * Erase the element at a certain position. + */ + void EraseAt(SQInteger i) + { + ValidIdx_(i); + // Is this element cached? + if (mVec[static_cast< size_t >(i)].second != nullptr) + { + // Invalidate the instance + mVec[static_cast< size_t >(i)].second->Cleanup(); + mVec[static_cast< size_t >(i)].first.Release(); + } + // Safe to remove + mPtr->erase(mPtr->begin() + static_cast< size_t >(i)); + mVec.erase(mVec.begin() + static_cast< size_t >(i)); + // Synchronize cache + CacheSync(); + } + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor. + */ + void Each(Function & fn) + { + Validate(); + // Iterate referenced vector elements + for (size_t i = 0; i < mVec.size(); ++i) + { + fn.Execute(Get(static_cast< SQInteger >(i))); + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate values in range through a functor. + */ + void EachRange(SQInteger p, SQInteger n, Function & fn) + { + ValidIdx_(p); + ValidIdx_(p + n); + // Iterate specified range + for (n += p; p < n; ++p) + { + fn.Execute(Get(static_cast< SQInteger >(p))); + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor until stopped (i.e false is returned). + */ + void While(Function & fn) + { + Validate(); + // Iterate referenced vector elements + for (size_t i = 0; i < mVec.size(); ++i) + { + auto ret = fn.Eval(Get(static_cast< SQInteger >(i))); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + /* -------------------------------------------------------------------------------------------- + * Iterate values in range through a functor until stopped (i.e false is returned). + */ + void WhileRange(SQInteger p, SQInteger n, Function & fn) + { + ValidIdx_(p); + ValidIdx_(p + n); + // Iterate specified range + for (n += p; p < n; ++p) + { + auto ret = fn.Eval(Get(static_cast< SQInteger >(p))); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + /* -------------------------------------------------------------------------------------------- + * Retrieve a wrapped instance as a script object. + */ + SQMOD_NODISCARD static std::pair< LightObj, W * > Obj(T * ptr, bool owned = false) + { + // Create the wrapper instance for given pointer + auto wp = std::make_unique< W >(ptr, false); + // Create script object for wrapper instance + std::pair< LightObj, W * > p{LightObj{wp.get()}, wp.get()}; + // Release ownership of the wrapper instance + [[maybe_unused]] auto _ = wp.release(); + // Return the script object and wrapper instance + return p; + } + /* -------------------------------------------------------------------------------------------- + * Retrieve a wrapped instance as a script object. + */ + SQMOD_NODISCARD static std::pair< LightObj, W * > Obj(const T * ptr, bool owned = false) + { + return Obj(const_cast< T * >(ptr), owned); + } +}; + +template < class T, class W, class U > inline void Register_DPP_VectorProxy(HSQUIRRELVM vm, Table & ns, const SQChar * name) +{ + using Container = DpVectorProxy< T, W >; + // -------------------------------------------------------------------------------------------- + ns.Bind(name, + Class< Container, NoConstructor< Container > >(vm, U::Str) + // Meta-methods + .SquirrelFunc(_SC("_typename"), &U::Fn) + // Properties + .Prop(_SC("Null"), &Container::IsNull) + .Prop(_SC("Empty"), &Container::Empty) + .Prop(_SC("Size"), &Container::Size) + .Prop(_SC("Capacity"), &Container::Capacity, &Container::Reserve) + // Member Methods + .Func(_SC("Get"), &Container::Get) + .Func(_SC("Set"), &Container::Set) + .Func(_SC("Reserve"), &Container::Reserve) + .Func(_SC("Compact"), &Container::Compact) + .Func(_SC("Clear"), &Container::Clear) + .Func(_SC("Push"), &Container::Push) + .Func(_SC("Append"), &Container::Push) + .Func(_SC("Extend"), &Container::Extend) + .Func(_SC("Pop"), &Container::Pop) + .Func(_SC("EraseAt"), &Container::EraseAt) + .Func(_SC("Each"), &Container::Each) + .Func(_SC("EachRange"), &Container::EachRange) + .Func(_SC("While"), &Container::While) + .Func(_SC("WhileRange"), &Container::WhileRange) + ); +} + +} // Namespace:: SqMod