From 323dc3ad1bf64b27ab42ac09336006fd7eacfa28 Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Sun, 4 Jul 2021 03:34:33 +0300 Subject: [PATCH] Implement dictionary type. Basically a fast (hybrid) contiguous hash map. --- module/CMakeLists.txt | 1 + module/Library/Utils.cpp | 2 + module/Library/Utils/Dictionary.cpp | 35 ++++ module/Library/Utils/Dictionary.hpp | 289 ++++++++++++++++++++++++++++ module/Library/Utils/Map.cpp | 2 +- 5 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 module/Library/Utils/Dictionary.cpp create mode 100644 module/Library/Utils/Dictionary.hpp diff --git a/module/CMakeLists.txt b/module/CMakeLists.txt index 87c7b118..a7b15fde 100644 --- a/module/CMakeLists.txt +++ b/module/CMakeLists.txt @@ -86,6 +86,7 @@ add_library(SqModule MODULE SqBase.hpp Main.cpp Library/System/Path.cpp Library/System/Path.hpp Library/Utils.cpp Library/Utils.hpp Library/Utils/Announce.cpp Library/Utils/Announce.hpp + Library/Utils/Dictionary.cpp Library/Utils/Dictionary.hpp Library/Utils/Map.cpp Library/Utils/Map.hpp Library/Utils/String.cpp Library/Utils/String.hpp Library/Utils/Vector.cpp Library/Utils/Vector.hpp diff --git a/module/Library/Utils.cpp b/module/Library/Utils.cpp index 1ce5b44a..170cd9db 100644 --- a/module/Library/Utils.cpp +++ b/module/Library/Utils.cpp @@ -85,6 +85,7 @@ static SQInteger SqExtractIPv4(HSQUIRRELVM vm) // ------------------------------------------------------------------------------------------------ extern void Register_Map(HSQUIRRELVM vm, Table & ns); extern void Register_Vector(HSQUIRRELVM vm, Table & ns); +extern void Register_Dictionary(HSQUIRRELVM vm, Table & ns); extern void Register_Native_String(HSQUIRRELVM vm, Table & ns); extern void Register_ServerAnnouncer(HSQUIRRELVM vm, Table & ns); @@ -95,6 +96,7 @@ void Register_Utils(HSQUIRRELVM vm) Register_Map(vm, ns); Register_Vector(vm, ns); + Register_Dictionary(vm, ns); Register_Native_String(vm, ns); Register_ServerAnnouncer(vm, ns); diff --git a/module/Library/Utils/Dictionary.cpp b/module/Library/Utils/Dictionary.cpp new file mode 100644 index 00000000..b8fa6765 --- /dev/null +++ b/module/Library/Utils/Dictionary.cpp @@ -0,0 +1,35 @@ +// ------------------------------------------------------------------------------------------------ +#include "Library/Utils/Dictionary.hpp" + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +// ------------------------------------------------------------------------------------------------ +SQMOD_DECL_TYPENAME(SqDictionaryTn, _SC("SqDictionary")) + +// ================================================================================================ +void Register_Dictionary(HSQUIRRELVM vm, Table & ns) +{ + ns.Bind(_SC("Dictionary"), + Class< SqDictionary >(vm, SqDictionaryTn::Str) + // Constructors + .Ctor() + .Ctor< SQInteger >() + // Meta-methods + .SquirrelFunc(_SC("_typename"), &SqDictionaryTn::Fn) + // Properties + .Prop(_SC("Empty"), &SqDictionary::Empty) + .Prop(_SC("Size"), &SqDictionary::Size) + // Member Methods + .Func(_SC("Get"), &SqDictionary::Get) + .Func(_SC("Set"), &SqDictionary::Set) + .Func(_SC("Clear"), &SqDictionary::Clear) + .Func(_SC("Erase"), &SqDictionary::Erase) + .CbFunc(_SC("Each"), &SqDictionary::Each) + .CbFunc(_SC("EachWith"), &SqDictionary::EachWith) + .CbFunc(_SC("While"), &SqDictionary::While) + .CbFunc(_SC("WhileWith"), &SqDictionary::WhileWith) + ); +} + +} // Namespace:: SqMod diff --git a/module/Library/Utils/Dictionary.hpp b/module/Library/Utils/Dictionary.hpp new file mode 100644 index 00000000..511ed6da --- /dev/null +++ b/module/Library/Utils/Dictionary.hpp @@ -0,0 +1,289 @@ +#pragma once + +// ------------------------------------------------------------------------------------------------ +#include "Core/Utility.hpp" + +// ------------------------------------------------------------------------------------------------ +#include +#include +#include +#include + +// ------------------------------------------------------------------------------------------------ +namespace Sqrat { + +/* ------------------------------------------------------------------------------------------------ + * Helper type used to retrieve the hash of a value instead of the value itself. +*/ +struct SqKeyHash +{ + SQHash mH{}; + constexpr SqKeyHash() noexcept = default; + constexpr explicit SqKeyHash(SQHash h) noexcept : mH(h) { } + constexpr SqKeyHash(const SqKeyHash &) noexcept = default; + constexpr SqKeyHash & operator = (const SqKeyHash &) noexcept = default; + constexpr operator SQHash () const noexcept { return mH; } //NOLINT (explicit) +}; + +/* ------------------------------------------------------------------------------------------------ + * Allows the binding system to know how to interact with SqKeyHash type. +*/ +template < > struct Var< SqKeyHash > { + + SqKeyHash value; + + /* -------------------------------------------------------------------------------------------- + * Base constructor. + */ + Var(HSQUIRRELVM vm, SQInteger idx) noexcept : value(sq_gethash(vm, idx)) { } + + /* -------------------------------------------------------------------------------------------- + * Push the associated object on the stack. + */ + static void push(HSQUIRRELVM vm, const SqKeyHash& value) + { + sq_pushinteger(vm, static_cast< SQInteger >(value)); + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Specialization for SqKeyHash reference. +*/ +template < > struct Var< SqKeyHash & > : Var< SqKeyHash > +{ + Var(HSQUIRRELVM vm, SQInteger idx) noexcept : Var< SqKeyHash >(vm, idx) { } +}; + +/* ------------------------------------------------------------------------------------------------ + * Specialization for constant SqKeyHash reference. +*/ +template < > struct Var< const SqKeyHash & > : Var< SqKeyHash > +{ + Var(HSQUIRRELVM vm, SQInteger idx) noexcept : Var< SqKeyHash >(vm, idx) { } +}; + +} // Namespace:: Sqrat + +// ------------------------------------------------------------------------------------------------ +namespace SqMod { + +/* ------------------------------------------------------------------------------------------------ + * Wrapper around a std::vector of std::pair values. Efficient contiguous associative container. +*/ +struct SqDictionary +{ + /* -------------------------------------------------------------------------------------------- + * Type stored in the container. + */ + using Element = std::pair< SQHash, LightObj >; + + /* -------------------------------------------------------------------------------------------- + * The typeof container that will be used. + */ + using Container = std::vector< Element >; + + /* -------------------------------------------------------------------------------------------- + * Container instance. + */ + Container mC{}; + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + SqDictionary() = default; + + /* -------------------------------------------------------------------------------------------- + * Construct with initial capacity. No element is created. + */ + explicit SqDictionary(SQInteger n) + : SqDictionary() + { + mC.reserve(static_cast< size_t >(n)); + } + + /* -------------------------------------------------------------------------------------------- + * Copy constructor. + */ + SqDictionary(const SqDictionary &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move constructor. + */ + SqDictionary(SqDictionary &&) noexcept = default; + + /* -------------------------------------------------------------------------------------------- + * Destroys the Statement. + */ + ~SqDictionary() = default; + + /* -------------------------------------------------------------------------------------------- + * Assignment operator. + */ + SqDictionary & operator = (const SqDictionary &) = default; + + /* -------------------------------------------------------------------------------------------- + * Move assignment. + */ + SqDictionary & operator = (SqDictionary &&) noexcept = default; + + /* -------------------------------------------------------------------------------------------- + * Retrieve a value from the container. + */ + SQMOD_NODISCARD LightObj & Get(SqKeyHash k) + { + for (auto & e : mC) + { + if (e.first == k.mH) return e.second; + } + // See if we can get the specified value as a string + StackStrF val(SqVM(), 2); + // Include the value in the error if we can + if (SQ_SUCCEEDED(val.Proc(false))) + { + STHROWF("No element found for ({}) key", val.ToStr()); + } + else + { + STHROWF("No element found for specified key"); + } + // This should not be reached + SQ_UNREACHABLE + } + + /* -------------------------------------------------------------------------------------------- + * Modify a value from the container. + */ + void Set(SqKeyHash k, LightObj & v) + { + for (auto & e : mC) + { + if (e.first == k.mH) + { + e.second = std::move(v); + return; // We updated existing element + } + } + // Create the element now + mC.emplace_back(k.mH, std::move(v)); + } + + /* -------------------------------------------------------------------------------------------- + * Check if the container has no elements. + */ + SQMOD_NODISCARD bool Empty() const + { + return mC.empty(); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements in the container. + */ + SQMOD_NODISCARD SQInteger Size() const + { + return static_cast< SQInteger >(mC.size()); + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the number of elements that the container has currently allocated space for. + */ + SQMOD_NODISCARD SQInteger Capacity() const + { + return static_cast< SQInteger >(mC.capacity()); + } + + /* -------------------------------------------------------------------------------------------- + * Increase the capacity of the container to a value that's greater or equal to the one specified. + */ + SqDictionary & Reserve(SQInteger n) + { + mC.reserve(ClampL< SQInteger, size_t >(n)); + return *this; + } + + /* -------------------------------------------------------------------------------------------- + * Request the removal of unused capacity. + */ + void Compact() + { + mC.shrink_to_fit(); + } + + /* -------------------------------------------------------------------------------------------- + * Erase all elements from the container. + */ + void Clear() + { + mC.clear(); + } + + /* -------------------------------------------------------------------------------------------- + * Erase the element with the specified key. + */ + bool Erase(SqKeyHash k) + { + auto itr = std::find_if(mC.cbegin(), mC.cend(), + [&](Container::const_reference e) -> bool { return e.first == k.mH; }); + if (itr != mC.end()) + { + mC.erase(itr); + return true; + } + return false; + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor. + */ + void Each(Function & fn) const + { + for (const auto & e : mC) + { + fn.Execute(static_cast< SQInteger >(e.first), e.second); + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor. + */ + void EachWith(LightObj & ctx, Function & fn) const + { + for (const auto & e : mC) + { + fn.Execute(ctx, static_cast< SQInteger >(e.first), e.second); + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor until stopped (i.e false is returned). + */ + void While(Function & fn) const + { + for (const auto & e : mC) + { + auto ret = fn.Eval(static_cast< SQInteger >(e.first), e.second); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } + + /* -------------------------------------------------------------------------------------------- + * Iterate all values through a functor until stopped (i.e false is returned). + */ + void WhileWith(LightObj & ctx, Function & fn) const + { + for (const auto & e : mC) + { + auto ret = fn.Eval(ctx, static_cast< SQInteger >(e.first), e.second); + // (null || true) == continue & false == break + if (!ret.IsNull() || !ret.template Cast< bool >()) + { + break; + } + } + } +}; + +} // Namespace:: SqMod diff --git a/module/Library/Utils/Map.cpp b/module/Library/Utils/Map.cpp index 6ec6350b..1f2209f9 100644 --- a/module/Library/Utils/Map.cpp +++ b/module/Library/Utils/Map.cpp @@ -12,7 +12,7 @@ SQMOD_DECL_TYPENAME(SqMapString, _SC("SqMapString")) template < class T, class U > static void Register_Map(HSQUIRRELVM vm, Table & ns, const SQChar * name) { - using Container = SqMap< T >; + using Container = SqMap< T >; // -------------------------------------------------------------------------------------------- ns.Bind(name, Class< Container, NoCopy< Container > >(vm, U::Str)