From 40e024e72d3d896cb7f81e214208225c97c6b6bd Mon Sep 17 00:00:00 2001 From: Sandu Liviu Catalin Date: Tue, 21 Jun 2016 17:25:43 +0300 Subject: [PATCH] Further improvements of the entity searching algorithms. --- source/Base/Algo.cpp | 416 ++++++++++++++++++++++++++++++++++++------- source/Base/Algo.hpp | 46 ++--- 2 files changed, 377 insertions(+), 85 deletions(-) diff --git a/source/Base/Algo.cpp b/source/Base/Algo.cpp index 5d2513cf..09b8c712 100644 --- a/source/Base/Algo.cpp +++ b/source/Base/Algo.cpp @@ -10,10 +10,100 @@ #include "Entity/Player.hpp" #include "Entity/Vehicle.hpp" +// ------------------------------------------------------------------------------------------------ +#define SQMOD_VALID_NAME_STR(t) if (!t) { STHROWF("The specified name is invalid"); } + // ------------------------------------------------------------------------------------------------ namespace SqMod { namespace Algo { +/* ------------------------------------------------------------------------------------------------ + * Used to fake a string so a raw buffer can be used with search algorithms. +*/ +class FakeString +{ +public: + + // -------------------------------------------------------------------------------------------- + SQChar mBuffer[SQMOD_PLAYER_TMP_BUFFER]; // Buffer to hold the data. + std::size_t mSize; // The size of the data in the buffer. + + /* -------------------------------------------------------------------------------------------- + * Default constructor. + */ + FakeString() + : mBuffer(), mSize(0) + { + /* ... */ + } + + /* -------------------------------------------------------------------------------------------- + * Grab the player name from the server into the buffer. + */ + void GrabName(int id) + { + // Clear any previous string (just in case) + mBuffer[0] = '\0'; + mSize = 0; + // Query the server for the name of the managed player + if (_Func->GetPlayerName(id, mBuffer, sizeof(mBuffer)) == vcmpErrorNone) + { + mSize = std::strlen(mBuffer); // Calculate the name length + } + } + + /* -------------------------------------------------------------------------------------------- + * Retrieve the size of the name. + */ + std::size_t size() const + { + return mSize; + } + + /* -------------------------------------------------------------------------------------------- + * Find in buffer contents of another string. + */ + std::size_t find(CSStr s) const + { + CCStr r = std::strstr(mBuffer, s); + return r == nullptr ? String::npos : (r - mBuffer); + } + + /* -------------------------------------------------------------------------------------------- + * Compare the buffer contents with another string. + */ + int compare(CSStr s) const + { + return std::strcmp(mBuffer, s); + } + + /* -------------------------------------------------------------------------------------------- + * Compare the buffer contents with another string. + */ + int compare(std::size_t pos, std::size_t len, CSStr s) const + { + return std::strncmp(mBuffer + pos, s, len); + } +}; + +/* ------------------------------------------------------------------------------------------------ + * Functor to retrieve the player name. +*/ +struct PlayerName +{ + // -------------------------------------------------------------------------------------------- + FakeString mStr; // The string where the name is stored. + + /* -------------------------------------------------------------------------------------------- + * Function call operator. + */ + const FakeString & operator () (const typename InstSpec< CPlayer >::Instance & inst) + { + mStr.GrabName(inst.mID); + return mStr; + } +}; + // ------------------------------------------------------------------------------------------------ static const Object & Blip_FindBySprID(Int32 sprid) { @@ -38,72 +128,201 @@ static const Object & Blip_FindBySprID(Int32 sprid) return NullObject(); } -// ------------------------------------------------------------------------------------------------ +/* ------------------------------------------------------------------------------------------------ + * Collect all players where the name matches or not the specified one. +*/ +static inline Array Player_AllWhereNameEquals(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Remember the current stack size + const StackGuard sg; + // Allocate an empty array on the stack + sq_newarray(DefaultVM::Get(), 0); + // Process each entity in the pool + EachEquals(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), + AppendElemFunc< CPlayer >(), name, neg); + // Return the array at the top of the stack + return Var< Array >(DefaultVM::Get(), -1).value; +} + +/* ------------------------------------------------------------------------------------------------ + * Collect all players where the name begins or not with the specified string. +*/ +static inline Array Player_AllWhereNameBegins(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Remember the current stack size + const StackGuard sg; + // Allocate an empty array on the stack + sq_newarray(DefaultVM::Get(), 0); + // Process each entity in the pool + EachBegins(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), + AppendElemFunc< CPlayer >(), name, strlen(name), neg); + // Return the array at the top of the stack + return Var< Array >(DefaultVM::Get(), -1).value; +} + +/* ------------------------------------------------------------------------------------------------ + * Collect all players where the name ends or not with the specified string. +*/ +static inline Array Player_AllWhereNameEnds(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Remember the current stack size + const StackGuard sg; + // Allocate an empty array on the stack + sq_newarray(DefaultVM::Get(), 0); + // Process each entity in the pool + EachEnds(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), + AppendElemFunc< CPlayer >(), name, strlen(name), neg); + // Return the array at the top of the stack + return Var< Array >(DefaultVM::Get(), -1).value; +} + +/* ------------------------------------------------------------------------------------------------ + * Collect all players where the name contains or not the specified string. +*/ +static inline Array Player_AllWhereNameContains(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Remember the current stack size + const StackGuard sg; + // Allocate an empty array on the stack + sq_newarray(DefaultVM::Get(), 0); + // Process each entity in the pool + EachContains(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), + AppendElemFunc< CPlayer >(), name, neg); + // Return the array at the top of the stack + return Var< Array >(DefaultVM::Get(), -1).value; +} + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the first player where the name matches or not the specified one. +*/ +static inline Object Player_FirstWhereNameEquals(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element receiver + RecvElemFunc< CPlayer > recv; + // Process each entity in the pool + FirstEquals(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), recv, name, neg); + // Return the received element, if any + return recv.mObj; +} + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the first player where the name begins or not with the specified string. +*/ +static inline Object Player_FirstWhereNameBegins(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element receiver + RecvElemFunc< CPlayer > recv; + // Process each entity in the pool + FirstBegins(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), recv, name, strlen(name), neg); + // Return the received element, if any + return recv.mObj; +} + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the first player where the name ends or not with the specified string. +*/ +static inline Object Player_FirstWhereNameEnds(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element receiver + RecvElemFunc< CPlayer > recv; + // Process each entity in the pool + FirstEnds(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), recv, name, strlen(name), neg); + // Return the received element, if any + return recv.mObj; +} + +/* ------------------------------------------------------------------------------------------------ + * Retrieve the first player where the name contains or not the specified string. +*/ +static inline Object Player_FirstWhereNameContains(bool neg, CSStr name) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element receiver + RecvElemFunc< CPlayer > recv; + // Process each entity in the pool + FirstContains(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), recv, name, neg); + // Return the received element, if any + return recv.mObj; +} + +/* -------------------------------------------------------------------------------------------- + * Process all entities of this type where the name matches or not the specified one. +*/ +static inline Uint32 Player_EachWhereNameEquals(bool neg, CSStr name, Object & env, Function & func) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element forwarder + ForwardElemFunc< CPlayer > fwd(env, func); + // Process each entity in the pool + EachEquals(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), fwd, name, neg); + // Return the forward count + return fwd.mCount; +} + +/* -------------------------------------------------------------------------------------------- + * Process all entities of this type where the name begins with the specified string. +*/ +static inline Uint32 Player_EachWhereNameBegins(bool neg, CSStr name, Object & env, Function & func) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element forwarder + ForwardElemFunc< CPlayer > fwd(env, func); + // Process each entity in the pool + EachBegins(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), fwd, name, strlen(name), neg); + // Return the forward count + return fwd.mCount; +} + +/* -------------------------------------------------------------------------------------------- + * Process all entities of this type where the name ends or not with the specified string. +*/ +static inline Uint32 Player_EachWhereNameEnds(bool neg, CSStr name, Object & env, Function & func) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element forwarder + ForwardElemFunc< CPlayer > fwd(env, func); + // Process each entity in the pool + EachEnds(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), fwd, name, strlen(name), neg); + // Return the forward count + return fwd.mCount; +} + +/* -------------------------------------------------------------------------------------------- + * Process all entities of this type where the name contains the specified string. +*/ +static inline Uint32 Player_EachWhereNameContains(bool neg, CSStr name, Object & env, Function & func) +{ + SQMOD_VALID_NAME_STR(name) + // Create a new element forwarder + ForwardElemFunc< CPlayer > fwd(env, func); + // Process each entity in the pool + EachContains(InstSpec< CPlayer >::CBegin(), InstSpec< CPlayer >::CEnd(), + ValidInstFunc< CPlayer >(), PlayerName(), fwd, name, neg); + // Return the forward count + return fwd.mCount; +} // ================================================================================================ void Register(HSQUIRRELVM vm) { - Table fns(vm); - - fns.Bind(_SC("Blip"), Table(vm) - .Func(_SC("WithID"), &Entity< CBlip >::FindByID) - .Func(_SC("TagEquals"), &Entity< CBlip >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CBlip >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CBlip >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CBlip >::FirstWhereTagContains) - .Func(_SC("WithSprID"), &Blip_FindBySprID) - ); - - fns.Bind(_SC("Checkpoint"), Table(vm) - .Func(_SC("WithID"), &Entity< CCheckpoint >::FindByID) - .Func(_SC("TagEquals"), &Entity< CCheckpoint >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CCheckpoint >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CCheckpoint >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CCheckpoint >::FirstWhereTagContains) - ); - - fns.Bind(_SC("Keybind"), Table(vm) - .Func(_SC("WithID"), &Entity< CKeybind >::FindByID) - .Func(_SC("TagEquals"), &Entity< CKeybind >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CKeybind >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CKeybind >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CKeybind >::FirstWhereTagContains) - ); - - fns.Bind(_SC("Object"), Table(vm) - .Func(_SC("WithID"), &Entity< CObject >::FindByID) - .Func(_SC("TagEquals"), &Entity< CObject >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CObject >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CObject >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CObject >::FirstWhereTagContains) - ); - - fns.Bind(_SC("Pickup"), Table(vm) - .Func(_SC("WithID"), &Entity< CPickup >::FindByID) - .Func(_SC("TagEquals"), &Entity< CPickup >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CPickup >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CPickup >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CPickup >::FirstWhereTagContains) - ); - - fns.Bind(_SC("Player"), Table(vm) - .Func(_SC("WithID"), &Entity< CPlayer >::FindByID) - .Func(_SC("TagEquals"), &Entity< CPlayer >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CPlayer >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CPlayer >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CPlayer >::FirstWhereTagContains) - ); - - fns.Bind(_SC("Vehicle"), Table(vm) - .Func(_SC("WithID"), &Entity< CVehicle >::FindByID) - .Func(_SC("TagEquals"), &Entity< CVehicle >::FirstWhereTagEquals) - .Func(_SC("TagBegins"), &Entity< CVehicle >::FirstWhereTagBegins) - .Func(_SC("TagEnds"), &Entity< CVehicle >::FirstWhereTagEnds) - .Func(_SC("TagContains"), &Entity< CVehicle >::FirstWhereTagContains) - ); - - RootTable(vm).Bind(_SC("SqFind"), fns); - Table cns(vm); cns.Bind(_SC("Blip"), Table(vm) @@ -152,6 +371,10 @@ void Register(HSQUIRRELVM vm) .Func(_SC("TagBegins"), &Entity< CPlayer >::AllWhereTagBegins) .Func(_SC("TagEnds"), &Entity< CPlayer >::AllWhereTagEnds) .Func(_SC("TagContains"), &Entity< CPlayer >::AllWhereTagContains) + .Func(_SC("NameEquals"), &Player_AllWhereNameEquals) + .Func(_SC("NameBegins"), &Player_AllWhereNameBegins) + .Func(_SC("NameEnds"), &Player_AllWhereNameEnds) + .Func(_SC("NameContains"), &Player_AllWhereNameContains) ); cns.Bind(_SC("Vehicle"), Table(vm) @@ -164,6 +387,71 @@ void Register(HSQUIRRELVM vm) RootTable(vm).Bind(_SC("SqCollect"), cns); + Table fns(vm); + + fns.Bind(_SC("Blip"), Table(vm) + .Func(_SC("WithID"), &Entity< CBlip >::FindByID) + .Func(_SC("TagEquals"), &Entity< CBlip >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CBlip >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CBlip >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CBlip >::FirstWhereTagContains) + .Func(_SC("WithSprID"), &Blip_FindBySprID) + ); + + fns.Bind(_SC("Checkpoint"), Table(vm) + .Func(_SC("WithID"), &Entity< CCheckpoint >::FindByID) + .Func(_SC("TagEquals"), &Entity< CCheckpoint >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CCheckpoint >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CCheckpoint >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CCheckpoint >::FirstWhereTagContains) + ); + + fns.Bind(_SC("Keybind"), Table(vm) + .Func(_SC("WithID"), &Entity< CKeybind >::FindByID) + .Func(_SC("TagEquals"), &Entity< CKeybind >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CKeybind >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CKeybind >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CKeybind >::FirstWhereTagContains) + ); + + fns.Bind(_SC("Object"), Table(vm) + .Func(_SC("WithID"), &Entity< CObject >::FindByID) + .Func(_SC("TagEquals"), &Entity< CObject >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CObject >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CObject >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CObject >::FirstWhereTagContains) + ); + + fns.Bind(_SC("Pickup"), Table(vm) + .Func(_SC("WithID"), &Entity< CPickup >::FindByID) + .Func(_SC("TagEquals"), &Entity< CPickup >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CPickup >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CPickup >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CPickup >::FirstWhereTagContains) + .Func(_SC("NameEquals"), &Player_FirstWhereNameEquals) + .Func(_SC("NameBegins"), &Player_FirstWhereNameBegins) + .Func(_SC("NameEnds"), &Player_FirstWhereNameEnds) + .Func(_SC("NameContains"), &Player_FirstWhereNameContains) + ); + + fns.Bind(_SC("Player"), Table(vm) + .Func(_SC("WithID"), &Entity< CPlayer >::FindByID) + .Func(_SC("TagEquals"), &Entity< CPlayer >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CPlayer >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CPlayer >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CPlayer >::FirstWhereTagContains) + ); + + fns.Bind(_SC("Vehicle"), Table(vm) + .Func(_SC("WithID"), &Entity< CVehicle >::FindByID) + .Func(_SC("TagEquals"), &Entity< CVehicle >::FirstWhereTagEquals) + .Func(_SC("TagBegins"), &Entity< CVehicle >::FirstWhereTagBegins) + .Func(_SC("TagEnds"), &Entity< CVehicle >::FirstWhereTagEnds) + .Func(_SC("TagContains"), &Entity< CVehicle >::FirstWhereTagContains) + ); + + RootTable(vm).Bind(_SC("SqFind"), fns); + Table ens(vm); ens.Bind(_SC("Blip"), Table(vm) @@ -212,6 +500,10 @@ void Register(HSQUIRRELVM vm) .Func(_SC("TagBegins"), &Entity< CPlayer >::EachWhereTagBegins) .Func(_SC("TagEnds"), &Entity< CPlayer >::EachWhereTagEnds) .Func(_SC("TagContains"), &Entity< CPlayer >::EachWhereTagContains) + .Func(_SC("NameEquals"), &Player_EachWhereNameEquals) + .Func(_SC("NameBegins"), &Player_EachWhereNameBegins) + .Func(_SC("NameEnds"), &Player_EachWhereNameEnds) + .Func(_SC("NameContains"), &Player_EachWhereNameContains) ); ens.Bind(_SC("Vehicle"), Table(vm) diff --git a/source/Base/Algo.hpp b/source/Base/Algo.hpp index a94138e3..949d46e9 100644 --- a/source/Base/Algo.hpp +++ b/source/Base/Algo.hpp @@ -62,10 +62,10 @@ void EachBegins(Iterator first, Iterator last, { continue; } - // Retrieve the tag - const String & tag = retrieve(*first); - // Compare the tag - if (tag.size() > len && (tag.compare(0, len, str) == 0) == neg) + // Retrieve the string + const auto & s = retrieve(*first); + // Compare the string + if (s.size() > len && (s.compare(0, len, str) == 0) == neg) { collect(*first); } @@ -87,10 +87,10 @@ void EachEnds(Iterator first, Iterator last, { continue; } - // Retrieve the tag - const String & tag = retrieve(*first); + // Retrieve the string + const auto & s = retrieve(*first); // Compare the tag - if (tag.size() > len && (tag.compare(tag.size() - len, len, str) == 0) == neg) + if (s.size() > len && (s.compare(s.size() - len, len, str) == 0) == neg) { collect(*first); } @@ -148,10 +148,10 @@ void FirstBegins(Iterator first, Iterator last, { continue; } - // Retrieve the tag - const String & tag = retrieve(*first); - // Compare the tag - if (tag.size() > len && (tag.compare(0, len, str) == 0) == neg) + // Retrieve the string + const auto & s = retrieve(*first); + // Compare the string + if (s.size() > len && (s.compare(0, len, str) == 0) == neg) { receive(*first); break; @@ -174,10 +174,10 @@ void FirstEnds(Iterator first, Iterator last, { continue; } - // Retrieve the tag - const String & tag = retrieve(*first); - // Compare the tag - if (tag.size() > len && (tag.compare(tag.size() - len, len, str) == 0) == neg) + // Retrieve the string + const auto & s = retrieve(*first); + // Compare the string + if (s.size() > len && (s.compare(s.size() - len, len, str) == 0) == neg) { receive(*first); break; @@ -659,7 +659,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Collect all entities of this type where the string match or not the specified one. + * Collect all entities of this type where the tag matches or not the specified one. */ static inline Array AllWhereTagEquals(bool neg, CSStr tag) { @@ -675,7 +675,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Collect all entities of this type where the string begins or not with the specified string. + * Collect all entities of this type where the tag begins or not with the specified string. */ static inline Array AllWhereTagBegins(bool neg, CSStr tag) { @@ -691,7 +691,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Collect all entities of this type where the string ends or not with the specified string. + * Collect all entities of this type where the tag ends or not with the specified string. */ static inline Array AllWhereTagEnds(bool neg, CSStr tag) { @@ -707,7 +707,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Collect all entities of this type where the string contains or not the specified string. + * Collect all entities of this type where the tag contains or not the specified string. */ static inline Array AllWhereTagContains(bool neg, CSStr tag) { @@ -792,7 +792,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Process all entities of this type where the string matches or not the specified one. + * Process all entities of this type where the tag matches or not the specified one. */ static inline Uint32 EachWhereTagEquals(bool neg, CSStr tag, Object & env, Function & func) { @@ -806,7 +806,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Process all entities of this type where the string begins with the specified string. + * Process all entities of this type where the tag begins with the specified string. */ static inline Uint32 EachWhereTagBegins(bool neg, CSStr tag, Object & env, Function & func) { @@ -820,7 +820,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Process all entities of this type where the string ends or not with the specified string. + * Process all entities of this type where the tag ends or not with the specified string. */ static inline Uint32 EachWhereTagEnds(bool neg, CSStr tag, Object & env, Function & func) { @@ -834,7 +834,7 @@ public: } /* -------------------------------------------------------------------------------------------- - * Process all entities of this type where the string contains the specified string. + * Process all entities of this type where the tag contains the specified string. */ static inline Uint32 EachWhereTagContains(bool neg, CSStr tag, Object & env, Function & func) {