[Code] Cleanup Strings Header (#4950)

* [Code] Cleanup Strings Header

* Include optimize
This commit is contained in:
Chris Miles 2025-06-22 13:52:13 -05:00 committed by GitHub
parent f0c041e8b3
commit 5ac9dd04e4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 204 additions and 221 deletions

View File

@ -708,6 +708,20 @@ const std::string Database::GetNPCNameByID(uint32 npc_id)
return e.id ? e.name : std::string(); return e.id ? e.name : std::string();
} }
template<typename InputIterator, typename OutputIterator>
inline auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
{
for (; first != last; ++first) {
if (*first == '_') {
*result = ' ';
}
else if (isalpha(*first) || *first == '`') {
*result = *first;
}
}
return result;
}
const std::string Database::GetCleanNPCNameByID(uint32 npc_id) const std::string Database::GetCleanNPCNameByID(uint32 npc_id)
{ {
const auto& e = NpcTypesRepository::FindOne(*this, npc_id); const auto& e = NpcTypesRepository::FindOne(*this, npc_id);

View File

@ -1,6 +1,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include "process.h" #include "process.h"
#include <fmt/format.h>
std::string Process::execute(const std::string &cmd) std::string Process::execute(const std::string &cmd)
{ {

View File

@ -43,6 +43,47 @@ public:
* method and encapsulate filters there * method and encapsulate filters there
*/ */
template<typename T1, typename T2, typename T3, typename T4>
static std::vector<std::string> join_tuple(
const std::string &glue,
const std::pair<char, char> &encapsulation,
const std::vector<std::tuple<T1, T2, T3, T4>> &src
)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::tuple<T1, T2, T3, T4> &src_iter: src) {
output.emplace_back(
fmt::format(
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
encapsulation.first,
std::get<0>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<1>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<2>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<3>(src_iter),
encapsulation.second
)
);
}
return output;
}
// Custom extended repository methods here // Custom extended repository methods here
static std::vector<std::string> GetRuleNames(Database &db, int rule_set_id) static std::vector<std::string> GetRuleNames(Database &db, int rule_set_id)
{ {
@ -87,12 +128,28 @@ public:
return v; return v;
} }
template<typename T>
static std::string
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{
if (src.empty()) {
return {};
}
std::ostringstream oss;
for (const T &src_iter: src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
static bool DeleteOrphanedRules(Database& db, std::vector<std::string>& v) static bool DeleteOrphanedRules(Database& db, std::vector<std::string>& v)
{ {
const auto query = fmt::format( const auto query = fmt::format(
"DELETE FROM {} WHERE rule_name IN ({})", "DELETE FROM {} WHERE rule_name IN ({})",
TableName(), TableName(),
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), v) ImplodePair(",", std::pair<char, char>('\'', '\''), v)
); );
return db.QueryDatabase(query).Success(); return db.QueryDatabase(query).Success();
@ -103,7 +160,7 @@ public:
const auto query = fmt::format( const auto query = fmt::format(
"REPLACE INTO {} (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}", "REPLACE INTO {} (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES {}",
TableName(), TableName(),
Strings::ImplodePair( ImplodePair(
",", ",",
std::pair<char, char>('(', ')'), std::pair<char, char>('(', ')'),
join_tuple(",", std::pair<char, char>('\'', '\''), v) join_tuple(",", std::pair<char, char>('\'', '\''), v)

View File

@ -1544,12 +1544,60 @@ bool SharedDatabase::GetCommandSettings(std::map<std::string, std::pair<uint8, s
return true; return true;
} }
template<typename T1, typename T2>
inline std::vector<std::string> join_pair(
const std::string &glue,
const std::pair<char, char> &encapsulation,
const std::vector<std::pair<T1, T2>> &src
)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::pair<T1, T2> &src_iter: src) {
output.emplace_back(
fmt::format(
"{}{}{}{}{}{}{}",
encapsulation.first,
src_iter.first,
encapsulation.second,
glue,
encapsulation.first,
src_iter.second,
encapsulation.second
)
);
}
return output;
}
template<typename T>
inline std::string
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{
if (src.empty()) {
return {};
}
std::ostringstream oss;
for (const T &src_iter: src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
bool SharedDatabase::UpdateInjectedCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected) bool SharedDatabase::UpdateInjectedCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected)
{ {
if (injected.size()) { if (injected.size()) {
const std::string query = fmt::format( const std::string query = fmt::format(
"REPLACE INTO `command_settings`(`command`, `access`) VALUES {}", "REPLACE INTO `command_settings`(`command`, `access`) VALUES {}",
Strings::ImplodePair( ImplodePair(
",", ",",
std::pair<char, char>('(', ')'), std::pair<char, char>('(', ')'),
join_pair(",", std::pair<char, char>('\'', '\''), injected) join_pair(",", std::pair<char, char>('\'', '\''), injected)
@ -1576,7 +1624,7 @@ bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector<std::string
if (orphaned.size()) { if (orphaned.size()) {
std::string query = fmt::format( std::string query = fmt::format(
"DELETE FROM `command_settings` WHERE `command` IN ({})", "DELETE FROM `command_settings` WHERE `command` IN ({})",
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned) ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
); );
auto results = QueryDatabase(query); auto results = QueryDatabase(query);
@ -1586,7 +1634,7 @@ bool SharedDatabase::UpdateOrphanedCommandSettings(const std::vector<std::string
query = fmt::format( query = fmt::format(
"DELETE FROM `command_subsettings` WHERE `parent_command` IN ({})", "DELETE FROM `command_subsettings` WHERE `parent_command` IN ({})",
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned) ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
); );
auto results_two = QueryDatabase(query); auto results_two = QueryDatabase(query);

View File

@ -34,6 +34,7 @@
*/ */
#include "strings.h" #include "strings.h"
#include <cereal/external/rapidjson/document.h>
#include <fmt/format.h> #include <fmt/format.h>
#include <algorithm> #include <algorithm>
#include <cctype> #include <cctype>
@ -41,6 +42,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <iostream> #include <iostream>
#include <sstream>
#include <random> #include <random>
#include <string> #include <string>
@ -49,6 +51,12 @@
#include "strings_legacy.cpp" // legacy c functions #include "strings_legacy.cpp" // legacy c functions
#include "strings_misc.cpp" // anything non "Strings" scoped #include "strings_misc.cpp" // anything non "Strings" scoped
#ifdef _WINDOWS
#include <ctype.h>
#include <functional>
#include <algorithm>
#endif
std::string Strings::Random(size_t length) std::string Strings::Random(size_t length)
{ {
static auto &chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; static auto &chrs = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
@ -701,6 +709,18 @@ std::string &Strings::Trim(std::string &str, const std::string &chars)
return LTrim(RTrim(str, chars), chars); return LTrim(RTrim(str, chars), chars);
} }
const std::string NUM_TO_ENGLISH_X[] = {
"", "One ", "Two ", "Three ", "Four ",
"Five ", "Six ", "Seven ", "Eight ", "Nine ", "Ten ", "Eleven ",
"Twelve ", "Thirteen ", "Fourteen ", "Fifteen ",
"Sixteen ", "Seventeen ", "Eighteen ", "Nineteen "
};
const std::string NUM_TO_ENGLISH_Y[] = {
"", "", "Twenty ", "Thirty ", "Forty ",
"Fifty ", "Sixty ", "Seventy ", "Eighty ", "Ninety "
};
// Function to convert single digit or two digit number into words // Function to convert single digit or two digit number into words
std::string Strings::ConvertToDigit(int n, const std::string& suffix) std::string Strings::ConvertToDigit(int n, const std::string& suffix)
{ {

View File

@ -36,53 +36,19 @@
#define _STRINGUTIL_H_ #define _STRINGUTIL_H_
#include <charconv> #include <charconv>
#include <sstream> #include <cstring>
#include <string.h>
#include <string_view> #include <string_view>
#include <string>
#include <vector> #include <vector>
#include <cstdarg> #include <cstdarg>
#include <tuple>
#include <type_traits> #include <type_traits>
#include <fmt/format.h> #ifdef _WIN32
#include <cereal/external/rapidjson/document.h>
#ifndef _WIN32
// this doesn't appear to affect linux-based systems..need feedback for _WIN64
#endif
#ifdef _WINDOWS
#include <ctype.h> #include <ctype.h>
#include <functional>
#include <algorithm>
#endif #endif
#include "types.h" #include "types.h"
namespace detail {
// template magic to check if std::from_chars floating point functions exist
template <typename T, typename = void>
struct has_from_chars_float : std::false_type { };
// basically it "uses" this template if they do exist because reasons
template <typename T>
struct has_from_chars_float < T,
std::void_t<decltype(std::from_chars(std::declval<const char *>(), std::declval<const char *>(),
std::declval<T &>()))>> : std::true_type { };
}; // namespace detail
namespace EQ {
// lame -- older GCC's didn't define this, clang's libc++ however does, even though they lack FP support
#if defined(__GNUC__) && (__GNUC__ < 11) && !defined(__clang__)
enum class chars_format {
scientific = 1, fixed = 2, hex = 4, general = fixed | scientific
};
#else
using chars_format = std::chars_format;
#endif
}; // namespace EQ
class Strings { class Strings {
public: public:
static bool Contains(std::vector<std::string> container, const std::string& element); static bool Contains(std::vector<std::string> container, const std::string& element);
@ -133,61 +99,6 @@ public:
static bool BeginsWith(const std::string& subject, const std::string& search); static bool BeginsWith(const std::string& subject, const std::string& search);
static bool EndsWith(const std::string& subject, const std::string& search); static bool EndsWith(const std::string& subject, const std::string& search);
static std::string ZoneTime(const uint8 hours, const uint8 minutes); static std::string ZoneTime(const uint8 hours, const uint8 minutes);
template<typename T>
static std::string
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{
if (src.empty()) {
return {};
}
std::ostringstream oss;
for (const T &src_iter: src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
// basic string_view overloads that just use std stuff since they work!
template <typename T>
std::enable_if_t<std::is_floating_point_v<T> && detail::has_from_chars_float<T>::value, std::from_chars_result>
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
{
return std::from_chars(str.data(), str.data() + str.size(), value, fmt);
}
template <typename T>
std::enable_if_t<std::is_integral_v<T>, std::from_chars_result>
static from_chars(std::string_view str, T& value, int base = 10)
{
return std::from_chars(str.data(), str.data() + str.size(), value, base);
}
// fallback versions of floating point in case they're not implemented
// TODO: add error handling ...
// This does have to allocate since from_chars doesn't need a null terminated string and neither does string_view
template <typename T>
std::enable_if_t<std::is_floating_point_v<T> && !detail::has_from_chars_float<T>::value && std::is_same_v<T, float>, std::from_chars_result>
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
{
std::from_chars_result res{};
std::string tmp_str(str.data(), str.size());
value = strtof(tmp_str.data(), nullptr);
return res;
}
template <typename T>
std::enable_if_t<std::is_floating_point_v<T> && !detail::has_from_chars_float<T>::value && std::is_same_v<T, double>, std::from_chars_result>
static from_chars(std::string_view str, T& value, EQ::chars_format fmt = EQ::chars_format::general)
{
std::from_chars_result res{};
std::string tmp_str(str.data(), str.size());
value = strtod(tmp_str.data(), nullptr);
return res;
}
static std::string Slugify(const std::string &input, const std::string &separator = "-"); static std::string Slugify(const std::string &input, const std::string &separator = "-");
static bool IsValidJson(const std::string& json); static bool IsValidJson(const std::string& json);
}; };
@ -199,93 +110,6 @@ const std::string vStringFormat(const char *format, va_list args);
// Used for grid nodes, as NPC names remove numerals. // Used for grid nodes, as NPC names remove numerals.
// But general purpose // But general purpose
const std::string NUM_TO_ENGLISH_X[] = {
"", "One ", "Two ", "Three ", "Four ",
"Five ", "Six ", "Seven ", "Eight ", "Nine ", "Ten ", "Eleven ",
"Twelve ", "Thirteen ", "Fourteen ", "Fifteen ",
"Sixteen ", "Seventeen ", "Eighteen ", "Nineteen "
};
const std::string NUM_TO_ENGLISH_Y[] = {
"", "", "Twenty ", "Thirty ", "Forty ",
"Fifty ", "Sixty ", "Seventy ", "Eighty ", "Ninety "
};
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
template<typename T1, typename T2>
std::vector<std::string> join_pair(
const std::string &glue,
const std::pair<char, char> &encapsulation,
const std::vector<std::pair<T1, T2>> &src
)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::pair<T1, T2> &src_iter: src) {
output.emplace_back(
fmt::format(
"{}{}{}{}{}{}{}",
encapsulation.first,
src_iter.first,
encapsulation.second,
glue,
encapsulation.first,
src_iter.second,
encapsulation.second
)
);
}
return output;
}
// _WIN32 builds require that #include<fmt/format.h> be included in whatever code file the invocation is made from (no header files)
template<typename T1, typename T2, typename T3, typename T4>
std::vector<std::string> join_tuple(
const std::string &glue,
const std::pair<char, char> &encapsulation,
const std::vector<std::tuple<T1, T2, T3, T4>> &src
)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::tuple<T1, T2, T3, T4> &src_iter: src) {
output.emplace_back(
fmt::format(
"{}{}{}{}{}{}{}{}{}{}{}{}{}{}{}",
encapsulation.first,
std::get<0>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<1>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<2>(src_iter),
encapsulation.second,
glue,
encapsulation.first,
std::get<3>(src_iter),
encapsulation.second
)
);
}
return output;
}
// misc functions // misc functions
std::string SanitizeWorldServerName(std::string server_long_name); std::string SanitizeWorldServerName(std::string server_long_name);
std::vector<std::string> GetBadWords(); std::vector<std::string> GetBadWords();
@ -310,18 +134,4 @@ std::string FormatName(const std::string &char_name);
bool IsAllowedWorldServerCharacterList(char c); bool IsAllowedWorldServerCharacterList(char c);
void SanitizeWorldServerName(char *name); void SanitizeWorldServerName(char *name);
template<typename InputIterator, typename OutputIterator>
auto CleanMobName(InputIterator first, InputIterator last, OutputIterator result)
{
for (; first != last; ++first) {
if (*first == '_') {
*result = ' ';
}
else if (isalpha(*first) || *first == '`') {
*result = *first;
}
}
return result;
}
#endif #endif

View File

@ -30,7 +30,6 @@ public:
TEST_ADD(StringUtilTest::EscapeStringTest); TEST_ADD(StringUtilTest::EscapeStringTest);
TEST_ADD(StringUtilTest::SearchDeliminatedStringTest); TEST_ADD(StringUtilTest::SearchDeliminatedStringTest);
TEST_ADD(StringUtilTest::SplitStringTest); TEST_ADD(StringUtilTest::SplitStringTest);
TEST_ADD(StringUtilTest::FromCharsTest);
TEST_ADD(StringUtilTest::TestIsFloat); TEST_ADD(StringUtilTest::TestIsFloat);
TEST_ADD(StringUtilTest::TestIsNumber); TEST_ADD(StringUtilTest::TestIsNumber);
} }
@ -108,21 +107,6 @@ public:
TEST_ASSERT(v[2] == "789"); TEST_ASSERT(v[2] == "789");
} }
void FromCharsTest() {
char int_chars[] = "123";
int int_value = 0;
char float_chars[] = "3.14";
float float_value = 0.0f;
Strings::from_chars(int_chars, int_value);
TEST_ASSERT(int_value == 123);
Strings::from_chars(float_chars, float_value);
TEST_ASSERT(float_value == 3.14f);
}
void TestIsFloat() { void TestIsFloat() {
TEST_ASSERT_EQUALS(Strings::IsFloat("0.23424523"), true); TEST_ASSERT_EQUALS(Strings::IsFloat("0.23424523"), true);
TEST_ASSERT_EQUALS(Strings::IsFloat("12312312313.23424523"), true); TEST_ASSERT_EQUALS(Strings::IsFloat("12312312313.23424523"), true);

View File

@ -74,13 +74,61 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
return true; return true;
} }
template<typename T1, typename T2>
inline std::vector<std::string> join_pair(
const std::string &glue,
const std::pair<char, char> &encapsulation,
const std::vector<std::pair<T1, T2>> &src
)
{
if (src.empty()) {
return {};
}
std::vector<std::string> output;
for (const std::pair<T1, T2> &src_iter: src) {
output.emplace_back(
fmt::format(
"{}{}{}{}{}{}{}",
encapsulation.first,
src_iter.first,
encapsulation.second,
glue,
encapsulation.first,
src_iter.second,
encapsulation.second
)
);
}
return output;
}
template<typename T>
inline std::string
ImplodePair(const std::string &glue, const std::pair<char, char> &encapsulation, const std::vector<T> &src)
{
if (src.empty()) {
return {};
}
std::ostringstream oss;
for (const T &src_iter: src) {
oss << encapsulation.first << src_iter << encapsulation.second << glue;
}
std::string output(oss.str());
output.resize(output.size() - glue.size());
return output;
}
bool BotDatabase::UpdateInjectedBotCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected) bool BotDatabase::UpdateInjectedBotCommandSettings(const std::vector<std::pair<std::string, uint8>> &injected)
{ {
if (injected.size()) { if (injected.size()) {
query = fmt::format( query = fmt::format(
"REPLACE INTO `bot_command_settings`(`bot_command`, `access`) VALUES {}", "REPLACE INTO `bot_command_settings`(`bot_command`, `access`) VALUES {}",
Strings::ImplodePair( ImplodePair(
",", ",",
std::pair<char, char>('(', ')'), std::pair<char, char>('(', ')'),
join_pair(",", std::pair<char, char>('\'', '\''), injected) join_pair(",", std::pair<char, char>('\'', '\''), injected)
@ -107,7 +155,7 @@ bool BotDatabase::UpdateOrphanedBotCommandSettings(const std::vector<std::string
query = fmt::format( query = fmt::format(
"DELETE FROM `bot_command_settings` WHERE `bot_command` IN ({})", "DELETE FROM `bot_command_settings` WHERE `bot_command` IN ({})",
Strings::ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned) ImplodePair(",", std::pair<char, char>('\'', '\''), orphaned)
); );
if (!database.QueryDatabase(query).Success()) { if (!database.QueryDatabase(query).Success()) {
@ -248,7 +296,7 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list<BotsAvailableLis
SELECT `account_id` FROM `character_data` WHERE `id` = {} SELECT `account_id` FROM `character_data` WHERE `id` = {}
) )
) )
AND AND
`name` NOT LIKE '%-deleted-%' `name` NOT LIKE '%-deleted-%'
), ),
owner_id owner_id
@ -2226,7 +2274,7 @@ bool BotDatabase::LoadBotSettings(Mob* m)
else { else {
query = fmt::format("`bot_id` = {} AND `stance` = {}", mob_id, stance_id); query = fmt::format("`bot_id` = {} AND `stance` = {}", mob_id, stance_id);
} }
if (stance_id == Stance::Passive) { if (stance_id == Stance::Passive) {
LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive);
return true; return true;
@ -2278,7 +2326,7 @@ bool BotDatabase::SaveBotSettings(Mob* m)
if (!m->IsOfClientBot()) { if (!m->IsOfClientBot()) {
return false; return false;
} }
uint32 bot_id = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); uint32 bot_id = (m->IsBot() ? m->CastToBot()->GetBotID() : 0);
uint32 character_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); uint32 character_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0);
uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0);
@ -2289,10 +2337,10 @@ bool BotDatabase::SaveBotSettings(Mob* m)
} }
std::string query = ""; std::string query = "";
if (m->IsClient()) { if (m->IsClient()) {
query = fmt::format("`character_id` = {} AND `stance` = {}", character_id, stance_id); query = fmt::format("`character_id` = {} AND `stance` = {}", character_id, stance_id);
} }
else { else {
query = fmt::format("`bot_id` = {} AND `stance` = {}", bot_id, stance_id); query = fmt::format("`bot_id` = {} AND `stance` = {}", bot_id, stance_id);
} }

View File

@ -6,6 +6,7 @@
#include "../common/data_verification.h" #include "../common/data_verification.h"
#include <numbers> #include <numbers>
#include "../common/types.h" #include "../common/types.h"
#include <fmt/format.h>
constexpr float position_eps = 0.0001f; constexpr float position_eps = 0.0001f;