diff --git a/module/Library/Utils.cpp b/module/Library/Utils.cpp index 27eb8b9b..269db36c 100644 --- a/module/Library/Utils.cpp +++ b/module/Library/Utils.cpp @@ -8,6 +8,9 @@ // ------------------------------------------------------------------------------------------------ namespace SqMod { +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqIdPoolTypename, _SC("SqIdPool")) + /* ------------------------------------------------------------------------------------------------ * Probably not the best implementation but should cover all sorts of weird cases. */ @@ -83,15 +86,18 @@ static SQInteger SqExtractIPv4(HSQUIRRELVM vm) } // ------------------------------------------------------------------------------------------------ +extern void Register_IdPool(HSQUIRRELVM vm, Table & ns); extern void Register_Vector(HSQUIRRELVM vm, Table & ns); extern void Register_Native_String(HSQUIRRELVM vm, Table & ns); extern void Register_ServerAnnouncer(HSQUIRRELVM vm, Table & ns); + // ================================================================================================ void Register_Utils(HSQUIRRELVM vm) { Table ns(vm); + Register_IdPool(vm, ns); Register_Vector(vm, ns); Register_Native_String(vm, ns); Register_ServerAnnouncer(vm, ns); @@ -101,4 +107,35 @@ void Register_Utils(HSQUIRRELVM vm) RootTable(vm).Bind(_SC("SqUtils"), ns); } +// ------------------------------------------------------------------------------------------------ +void Register_IdPool(HSQUIRRELVM vm, Table & ns) +{ + // -------------------------------------------------------------------------------------------- + ns.Bind(_SC("IdPool"), + Class< SqIdPool, NoCopy< SqIdPool > >(vm, SqIdPoolTypename::Str) + // Constructors + .Ctor() + .template Ctor< SqIdPool::Type >() + .template Ctor< SqIdPool::Type, SqIdPool::Type >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqIdPoolTypename::Fn) + // Member Variables + .ConstVar(_SC("Next"), &SqIdPool::mNext) + .ConstVar(_SC("Step"), &SqIdPool::mStep) + .ConstVar(_SC("Start"), &SqIdPool::mStart) + // Properties + .Prop(_SC("FreeCount"), &SqIdPool::FreeCount) + .Prop(_SC("UsedCount"), &SqIdPool::UsedCount) + // Member Methods + .Func(_SC("Reset"), &SqIdPool::Reset) + .Func(_SC("Acquire"), &SqIdPool::Acquire) + .Func(_SC("Release"), &SqIdPool::Release) + .Func(_SC("Using"), &SqIdPool::Using) + .Func(_SC("EachUsed"), &SqIdPool::EachUsed) + .Func(_SC("WhileUsed"), &SqIdPool::WhileUsed) + .Func(_SC("EachFree"), &SqIdPool::EachFree) + .Func(_SC("WhileFree"), &SqIdPool::WhileFree) + ); +} + } // Namespace:: SqMod diff --git a/module/Library/Utils.hpp b/module/Library/Utils.hpp index 324c3337..0601ee09 100644 --- a/module/Library/Utils.hpp +++ b/module/Library/Utils.hpp @@ -6,4 +6,187 @@ // ------------------------------------------------------------------------------------------------ namespace SqMod { +/* ------------------------------------------------------------------------------------------------ + * Helper utility used to provide reusable unique IDs of signed integer type. + * It is not thread-safe since the script runs in single-threaded mode. +*/ +struct SqIdPool +{ + using Type = SQInteger; // Type that is used to represent an ID. + using Pool = std::vector< Type >; // Container for both used and unused IDs. + + // -------------------------------------------------------------------------------------------- + // Pool of available IDs. + Pool mPool{}; + // Pool of currently used IDs. + Pool mUsed{}; + // The ID that will be generated next time the pool is empty. + Type mNext{0}; + // How much to increment with each ID. + Type mStep{1}; + // Where to start generating IDs. + Type mStart{0}; + + /* -------------------------------------------------------------------------------------------- + * Base constructors. + */ + SqIdPool() noexcept = default; + SqIdPool(Type start) noexcept + : SqIdPool(start, 1) + { + } + SqIdPool(Type start, Type step) noexcept + : mPool(), mUsed(), mNext(start), mStep(step), mStart(start) + { + } + + /* -------------------------------------------------------------------------------------------- + * Copy/Move constructors (disabled). + */ + SqIdPool(const SqIdPool &) noexcept = delete; + SqIdPool(SqIdPool &&) noexcept = delete; + + /* -------------------------------------------------------------------------------------------- + * Copy/Move assignment operators (disabled). + */ + SqIdPool & operator = (const SqIdPool &) noexcept = delete; + SqIdPool & operator = (SqIdPool &&) noexcept = delete; + + /* -------------------------------------------------------------------------------------------- + * Discard all current IDs (free and used) and reset the start to the specified start. + * This invalidates all IDs that are currently left in use. + */ + void Reset() + { + mNext = mStart; + mPool.clear(); + mUsed.clear(); + } + + /* -------------------------------------------------------------------------------------------- + * Acquire a unique ID from the pool. + */ + SQMOD_NODISCARD Type Acquire() + { + Type id = mNext; + // Do we have some reusable IDs? + if (mPool.empty()) + { + mNext += mStep; // Create a new one and update the next one + } + else + { + id = mPool.back(); // Get one from the back of the pool + mPool.pop_back(); // Remove it from the free pool + } + // Store it in the list of active IDs + mUsed.push_back(id); + // Return this ID + return id; + } + + /* -------------------------------------------------------------------------------------------- + * Release a unique ID back to the pool. + */ + bool Release(Type id) + { + // Find the specified ID into + for (Pool::size_type i = 0; i < mUsed.size(); ++i) + { + // Is this the ID we're looking for? + if (mUsed[i] == id) + { + // Swap the element with the last one + std::swap(mUsed[i], mUsed.back()); + // Remove the last element + mUsed.pop_back(); + // Make this ID available, again + mPool.push_back(id); + // We actually found this ID + return true; + } + } + // This ID does not belong to this pool + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Check if the pool has the specified ID currently in use. + */ + SQMOD_NODISCARD bool Using(Type id) + { + return std::find(mUsed.begin(), mUsed.end(), id) != mUsed.end(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of IDs that are currently available in the free pool. + */ + SQMOD_NODISCARD SQInteger FreeCount() const + { + return mPool.size(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of IDs that are currently in use. + */ + SQMOD_NODISCARD SQInteger UsedCount() const + { + return mUsed.size(); + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all used IDs through a functor. + */ + void EachUsed(Function & fn) const + { + for (const auto & id : mUsed) + { + fn.Execute(id); + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all used IDs through a functor until stopped (i.e false is returned). + */ + void WhileUsed(Function & fn) const + { + for (const auto & id : mUsed) + { + auto ret = fn.Eval(id); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all free IDs through a functor. + */ + void EachFree(Function & fn) const + { + for (const auto & id : mPool) + { + fn.Execute(id); + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all free IDs through a functor until stopped (i.e false is returned). + */ + void WhileFree(Function & fn) const + { + for (const auto & id : mPool) + { + auto ret = fn.Eval(id); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } +}; + } // Namespace:: SqMod