From fb5a3b4097e491818e9296693fdf2c411d68b021 Mon Sep 17 00:00:00 2001 From: kai-ma Date: Fri, 4 Jul 2025 15:22:47 +0800 Subject: [PATCH] add csrc/utils --- accuracy_tools/msprobe/csrc/utils/Log.h | 156 ++++++++++++++ accuracy_tools/msprobe/csrc/utils/Path.cpp | 237 +++++++++++++++++++++ accuracy_tools/msprobe/csrc/utils/Path.h | 78 +++++++ accuracy_tools/msprobe/csrc/utils/Str.cpp | 197 +++++++++++++++++ accuracy_tools/msprobe/csrc/utils/Str.h | 64 ++++++ 5 files changed, 732 insertions(+) create mode 100644 accuracy_tools/msprobe/csrc/utils/Log.h create mode 100644 accuracy_tools/msprobe/csrc/utils/Path.cpp create mode 100644 accuracy_tools/msprobe/csrc/utils/Path.h create mode 100644 accuracy_tools/msprobe/csrc/utils/Str.cpp create mode 100644 accuracy_tools/msprobe/csrc/utils/Str.h diff --git a/accuracy_tools/msprobe/csrc/utils/Log.h b/accuracy_tools/msprobe/csrc/utils/Log.h new file mode 100644 index 00000000000..6f230e2d3d5 --- /dev/null +++ b/accuracy_tools/msprobe/csrc/utils/Log.h @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOG_H +#define LOG_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Utility { + class Log { + public: + static constexpr uint8_t BUF_SIZE = 32; + enum class LogLevel { DEBUG = 0, INFO, WARNING, ERROR }; + class LogStream { + public: + LogStream(LogLevel level, Log &logger) : level_(level), logger_(logger) { + } + + ~LogStream() { + if (logger_.CheckLogLevel(level_)) { + logger_.PrintLog(buffer_.str(), level_); + } + } + + template LogStream &operator<<(T &&arg) { + buffer_ << (std::forward(arg)); + return *this; + } + + private: + std::ostringstream buffer_; + LogLevel level_; + Log &logger_; + }; + + static Log &GetInstance() { + static Log instance; + return instance; + } + + LogStream DebugStream() { + return LogStream(LogLevel::DEBUG, *this); + } + LogStream InfoStream() { + return LogStream(LogLevel::INFO, *this); + } + LogStream WarningStream() { + return LogStream(LogLevel::WARNING, *this); + } + LogStream ErrorStream() { + return LogStream(LogLevel::ERROR, *this); + } + + void SetLogLevel(int level) { + logLv_ = level; + } + + int GetLogLevel() const { + return logLv_; + } + + bool CheckLogLevel(LogLevel level) const { + return static_cast(level) >= logLv_; + } + + void PrintLog(const std::string &msg, LogLevel lv) { + std::lock_guard lock(logMutex_); + std::string filteredMsg = msg; + filterSpecialChar(filteredMsg); + char buf[BUF_SIZE]; + auto now = std::chrono::system_clock::now(); + std::time_t time = std::chrono::system_clock::to_time_t(now); + std::tm *tm = std::localtime(&time); + std::strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm); + printf("%s (msprobe) (PID %ld) [%s] %s\n", buf, GetPid(), LogLevelString.at(lv), filteredMsg.c_str()); + fflush(stdout); + } + + private: + Log() = default; + ~Log() = default; + void filterSpecialChar(std::string &msg) { + for (const auto &s : SpecialChar) { + size_t pos = 0; + while ((pos = msg.find(s, pos)) != std::string::npos) { + msg.replace(pos, s.length(), "_"); + pos += 1; + } + } + } + + static uint64_t GetPid() { + return static_cast(getpid()); + } + + std::mutex logMutex_; + int logLv_ = 1; + const std::map LogLevelString = { + {LogLevel::DEBUG, "DEBUG"}, + {LogLevel::INFO, "INFO"}, + {LogLevel::WARNING, "WARNING"}, + {LogLevel::ERROR, "ERROR"}, + }; + const std::unordered_set SpecialChar = { + "\n", + "\r", + "\u007f", + "\b", + "\f", + "\t", + "\v", + "\u000b", + "%08", + "%09", + "%0a", + "%0b", + "%0c", + "%0d", + "%7f", + "//", + "\\", + "&", + }; + }; + +#define LOG_DEBUG Utility::Log::GetInstance().DebugStream() +#define LOG_INFO Utility::Log::GetInstance().InfoStream() +#define LOG_WARNING Utility::Log::GetInstance().WarningStream() +#define LOG_ERROR Utility::Log::GetInstance().ErrorStream() + +} // namespace Utility + +#endif diff --git a/accuracy_tools/msprobe/csrc/utils/Path.cpp b/accuracy_tools/msprobe/csrc/utils/Path.cpp new file mode 100644 index 00000000000..1ee2d1a6b6e --- /dev/null +++ b/accuracy_tools/msprobe/csrc/utils/Path.cpp @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/Path.h" + +#include +#include +#include +#include +#include + +#include "utils/Constant.h" +#include "utils/Exception.h" +#include "utils/Log.h" +#include "utils/Str.h" + +namespace Utility { + const int BYTE_TO_MB_SHIFT = 20; + + static int GetFreeSpace(const std::string &path, uint64_t *freeSpace) { + struct statvfs diskInfo; + if (statvfs(path.c_str(), &diskInfo) == -1) { + LOG_ERROR << "statvfs() error: " << errno; + return 1; + } + *freeSpace = diskInfo.f_bavail * diskInfo.f_bsize; + return 0; + } + + SafePath::SafePath( + std::string path, PathType pathType, Mode mode, uint64_t sizeLimit, std::string suffix, int maxDepth) + : formalPath(path), formalPathType(pathType), formalMode(mode), formalSizeLimit(sizeLimit), + formalSuffix(suffix), formalMaxDirDepth(maxDepth) { + } + + std::string SafePath::Check(bool pathExist, SoftLinkLevel linkLevel) { + formalPath = fs::absolute(fs::path(formalPath)).string(); + CheckSpecialChars(); + CheckPathLength(); + if (formalMode == Mode::WRITE && !pathExist) { + std::string parent = fs::absolute(fs::path(formalPath).parent_path()).string(); + CheckExists(parent); // The current directory doesn't exist, but the parent directory does. + parent = CheckSoftLink(parent, linkLevel); + EnsureDirectory(parent); + CheckPermissions(parent); + } else { + CheckExists(formalPath); + formalPath = CheckSoftLink(formalPath, linkLevel); + if (formalPathType == PathType::FILE) { + EnsureFile(formalPath); + CheckFileSuffix(); + CheckFileSize(); + } else if (formalPathType == PathType::DIRECTORY) { + EnsureDirectory(formalPath); + CheckDirectorySize(); + } + CheckPermissions(formalPath); + } + if (formalPathType == PathType::DIRECTORY && !StrSpace::IsSuffix(formalPath, "/")) { + formalPath += "/"; + } + return formalPath; + } + + void SafePath::CheckExists(const std::string &p) const { + if (!fs::exists(p)) { + throw MsprobeException("Path not found: " + p); + } + } + + void SafePath::EnsureDirectory(const std::string &p) const { + if (!fs::is_directory(p)) { + throw MsprobeException("Path is not a directory: " + p); + } + } + + void SafePath::EnsureFile(const std::string &p) const { + if (!fs::is_regular_file(p)) { + throw MsprobeException("Path is not a file: " + p); + } + } + + std::string SafePath::CheckSoftLink(const std::string &p, SoftLinkLevel level) const { + if (!fs::is_symlink(p)) + return p; + std::string realPath = fs::read_symlink(p).string(); + switch (level) { + case SoftLinkLevel::STRICT: + throw MsprobeException("Symlinks are prohibited: " + p); + case SoftLinkLevel::WARNING: + LOG_WARNING << "Symlink detected: " << p << " -> " << realPath; + break; + case SoftLinkLevel::IGNORE: + break; + } + return realPath; + } + + void SafePath::CheckPermissions(const std::string &p) const { + struct stat statbuf{}; + if (stat(p.c_str(), &statbuf) != 0) { + throw MsprobeException("Cannot stat path: " + p); + } + uid_t uid = geteuid(); + if (statbuf.st_uid != uid && statbuf.st_uid != 0) { + throw MsprobeException("Path must be owned by current user or root: " + p); + } + mode_t perm = statbuf.st_mode; + if ((perm & S_IWGRP) || (perm & S_IWOTH)) { + throw MsprobeException("Path writable by group/others: " + p); + } + if (formalMode == Mode::READ && !(perm & S_IRUSR)) { + throw MsprobeException("No read permission: " + p); + } + if (formalMode == Mode::WRITE && !(perm & S_IWUSR)) { + throw MsprobeException("No write permission: " + p); + } + if (formalMode == Mode::EXECUTE && !(perm & S_IXUSR)) { + throw MsprobeException("No execute permission: " + p); + } + } + + void SafePath::CheckSpecialChars() const { + std::regex valid(SPECIAL_CHAR_WHITE_LIST); + if (!std::regex_match(formalPath, valid)) { + throw MsprobeException("Path contains invalid characters: " + formalPath); + } + } + + void SafePath::CheckPathLength() const { + if (formalPath.length() > MAX_PATH_LENGTH) { + throw MsprobeException("Path length exceeds limit: " + std::to_string(formalPath.length())); + } + int depth = 0; + for (const auto &part : fs::path(formalPath)) { + if (++depth > formalMaxDirDepth) { + throw MsprobeException("Exceeded max directory depth: " + std::to_string(formalMaxDirDepth)); + } + if (part.string().length() > MAX_LAST_NAME_LENGTH) { + throw MsprobeException("Directory entry too long: " + part.string()); + } + } + } + + void SafePath::CheckFileSuffix() const { + if (!formalSuffix.empty() && !StrSpace::IsSuffix(formalPath, formalSuffix)) { + throw MsprobeException("File does not have expected suffix: " + formalSuffix); + } + } + + void SafePath::CheckFileSize() const { + if (formalSizeLimit > 0 && fs::file_size(formalPath) > static_cast(formalSizeLimit)) { + throw MsprobeException("File size exceeds limit."); + } + } + + void SafePath::CheckDirectorySize() const { + if (formalSizeLimit <= 0) + return; + uint64_t totalSize = 0; + for (const auto &entry : fs::recursive_directory_iterator(formalPath)) { + if (fs::is_regular_file(entry)) { + totalSize += fs::file_size(entry); + } + } + if (totalSize > static_cast(formalSizeLimit)) { + throw MsprobeException("Directory size exceeds limit."); + } + } + + bool SafePath::IsDiskSpaceValid(const std::string &path, const uint64_t &dataSize) { + uint64_t freeSpace = 0; + int ret = GetFreeSpace(path, &freeSpace); + if (ret == 0) { + if (freeSpace <= SafePath::SIZE_2G || freeSpace < dataSize * BYTE_TO_MB_SHIFT) { + LOG_ERROR << "Disk space is not enough, it's must more than 2G. Now free size(MB): " + << (freeSpace >> BYTE_TO_MB_SHIFT); + return false; + } + } else { + LOG_ERROR << "Failed to get disk space for path: " << path; + return false; + } + return true; + } + + void SafePath::ChangePermission(const fs::path &path, const fs::perms &permission) { + if (!fs::exists(path) || fs::is_symlink(path)) { + return; + } + try { + fs::permissions(path, permission, fs::perm_options::replace); + } catch (const fs::filesystem_error &e) { + throw MsprobeException("Failed to set permissions for " + path.string() + ": " + e.what()); + } + } + + void SafePath::MakeParentDir(const fs::path &path) { + fs::path parentPath = path.parent_path(); + if (!fs::exists(parentPath)) { + int depth = 0; + for (const auto &part : parentPath) { + if (!part.empty()) { + ++depth; + } + } + if (depth > MAX_DIR_DEPTH) { + throw MsprobeException("Exceeded max directory depth: " + std::to_string(MAX_DIR_DEPTH)); + } + fs::create_directories(parentPath); + } + SafePath::ChangePermission(parentPath, SafePath::PERM_750); + } + + fs::path GetMsprobeDir() { + std::string pathEnvVar = StrSpace::GetEnvVar(Cst::LINK_DUMP_PATH); + if (!pathEnvVar.empty()) { + return pathEnvVar; + } else { + throw MsprobeException("Dump dir has not been set."); + } + } + +} // namespace Utility diff --git a/accuracy_tools/msprobe/csrc/utils/Path.h b/accuracy_tools/msprobe/csrc/utils/Path.h new file mode 100644 index 00000000000..1ae3bd070c0 --- /dev/null +++ b/accuracy_tools/msprobe/csrc/utils/Path.h @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SAFE_PATH_H +#define SAFE_PATH_H + +#include +#include + +namespace Utility { + namespace fs = std::filesystem; + + class SafePath { + public: + static inline fs::perms PERM_750 = fs::perms::owner_all | fs::perms::group_read | fs::perms::group_exec; + static inline fs::perms PERM_640 = fs::perms::owner_read | fs::perms::owner_write | fs::perms::group_read; + static constexpr size_t SIZE_2G = 2ULL * 1024 * 1024 * 1024; + static constexpr size_t SIZE_10G = 10ULL * 1024 * 1024 * 1024; + static constexpr size_t SIZE_30G = 30ULL * 1024 * 1024 * 1024; + + static constexpr int MAX_PATH_LENGTH = 4096; + static constexpr int MAX_DIR_DEPTH = 32; + static constexpr int MAX_LAST_NAME_LENGTH = 255; + static inline const std::string SPECIAL_CHAR_WHITE_LIST = "^[a-zA-Z0-9_./-]+$"; + + enum class PathType { FILE, DIRECTORY }; + enum class Mode { READ, WRITE, EXECUTE }; + enum class SoftLinkLevel { IGNORE, WARNING, STRICT }; + + public: + static bool IsDiskSpaceValid(const std::string &path, const uint64_t &dataSize); + static void ChangePermission(const fs::path &path, const fs::perms &permission); + static void MakeParentDir(const fs::path &path); + SafePath(std::string path, + PathType pathType, + Mode mode, + uint64_t sizeLimit = 0, + std::string suffix = "", + int maxDepth = MAX_DIR_DEPTH); + std::string Check(bool pathExist = true, SoftLinkLevel linkLevel = SoftLinkLevel::STRICT); + + private: + std::string formalPath; + PathType formalPathType; + Mode formalMode; + int formalSizeLimit; + std::string formalSuffix; + int formalMaxDirDepth; + + void CheckExists(const std::string &p) const; + void EnsureDirectory(const std::string &p) const; + void EnsureFile(const std::string &p) const; + std::string CheckSoftLink(const std::string &p, SoftLinkLevel level) const; + void CheckPermissions(const std::string &p) const; + void CheckSpecialChars() const; + void CheckPathLength() const; + void CheckFileSuffix() const; + void CheckFileSize() const; + void CheckDirectorySize() const; + }; + + fs::path GetMsprobeDir(); +} // namespace Utility + +#endif diff --git a/accuracy_tools/msprobe/csrc/utils/Str.cpp b/accuracy_tools/msprobe/csrc/utils/Str.cpp new file mode 100644 index 00000000000..63df927900b --- /dev/null +++ b/accuracy_tools/msprobe/csrc/utils/Str.cpp @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "utils/Str.h" + +#include +#include +#include +#include + +#include "utils/Constant.h" +#include "utils/DataType.h" +#include "utils/Exception.h" +#include "utils/Log.h" + +namespace StrSpace { + const uint16_t MAX_LENGTH = 512; + + std::string GetEnvVar(const char *varName) { + const char *envVar = std::getenv(varName); + return (envVar != nullptr) ? std::string(envVar) : ""; + } + + std::vector Split(const std::string &inputString, const std::string &delimiter) { + if (delimiter.empty()) { + throw Utility::MsprobeException("delimiter is null."); + } + std::vector tokens; + size_t start = 0, end = 0; + while ((end = inputString.find(delimiter, start)) != std::string::npos) { + if (end != start) { + tokens.push_back(inputString.substr(start, end - start)); + } + start = end + delimiter.length(); + } + if (start < inputString.length()) { + tokens.push_back(inputString.substr(start)); + } + return tokens; + } + + uint64_t Str2Int(const char *inputString, const uint64_t &defaultValue, const char *tag) { + std::string tagStr = (tag != nullptr) ? std::string(tag) : "null"; + std::string tagInfo = "<" + tagStr + ">"; + if (inputString == nullptr) { + LOG_WARNING << "Input string is null. " << tagInfo << " Using default value: " << defaultValue; + return defaultValue; + } + std::string str(inputString); + if (str.empty()) { + LOG_INFO << "Input string is empty. " << tagInfo << " Using default value: " << defaultValue; + return defaultValue; + } + try { + size_t idx = 0; + int value = std::stoi(str, &idx); + if (idx != str.length()) { + LOG_WARNING << "Partial conversion: '" << str << "' -> " << value << ", unparsed: '" << str.substr(idx) + << "'. " << tagInfo; + } + return value; + } catch (const std::invalid_argument &e) { + LOG_ERROR << "Invalid argument: cannot convert '" << str << "' to int. " << tagInfo + << " Using default value: " << defaultValue; + } catch (const std::out_of_range &e) { + LOG_ERROR << "Out of range: cannot convert '" << str << "' to int. " << tagInfo + << " Using default value: " << defaultValue; + } catch (...) { + LOG_ERROR << "Unknown exception when converting '" << str << "' to int. " << tagInfo; + } + return defaultValue; + } + + std::vector SplitToInt(const std::string &inputString, const std::string &delimiter) { + auto str_vec = Split(inputString, delimiter); + std::vector int_vec; + for (const std::string &s : str_vec) { + uint64_t val = Str2Int(s.c_str(), 0, nullptr); + int_vec.push_back(val); + } + return int_vec; + } + + std::vector + Str2IntForVector(const std::vector &strVec, const int &defaultValue, const char *tag) { + std::vector IntVec; + IntVec.reserve(strVec.size()); + for (const auto &strEle : strVec) { + uint64_t val = Str2Int(strEle.c_str(), defaultValue, tag); + IntVec.push_back(static_cast(val)); + } + return IntVec; + } + + bool IsValueInGoal(const char *varName, const uint64_t &query) { + const std::string vvEnvVar = GetEnvVar(varName); // 5,6,7,8 + LOG_DEBUG << "IsValueInGoal: " << vvEnvVar << " for " << varName; + if (vvEnvVar.empty()) { + return true; + } + const std::vector vvString = Split(vvEnvVar, ","); + if (vvString.empty()) { + return false; + } + std::vector vvInt = Str2IntForVector(vvString, 0, varName); + if (vvInt.empty()) { + return false; + } + return std::find(vvInt.begin(), vvInt.end(), query) != vvInt.end(); + } + + bool IsValueInGoal(const char *varName, const std::string &query) { + const std::string vvEnvVar = GetEnvVar(varName); // "L0,L1,L2" + LOG_DEBUG << "IsValueInGoal: " << vvEnvVar << " for " << varName; + if (vvEnvVar.empty()) { + return true; + } + const std::vector vvString = Split(vvEnvVar, ","); + if (vvString.empty()) { + return false; + } + return std::find(vvString.begin(), vvString.end(), query) != vvString.end(); + } + + bool IsPrefix(const std::string &str, const std::string &prefix) { + return str.compare(0, prefix.length(), prefix) == 0; + } + + bool IsSuffix(const std::string &str, const std::string &suffix) { + if (str.length() < suffix.length()) { + return false; + } + return str.compare(str.length() - suffix.length(), suffix.length(), suffix) == 0; + } + + std::string ToLower(const std::string &input) { + std::string result = input; + for (char &c : result) { + c = std::tolower(c); + } + return result; + } + + bool IsStringLengthSafety(const std::string &ss) { + return (ss.size() <= MAX_LENGTH) && (ss.size() > 0); + } + + bool IsStringLengthSafety(const std::vector &vec) { + for (const auto &s : vec) { + if (!IsStringLengthSafety(s)) { + return false; + } + } + return true; + } + + ordered_json Str2Json(const std::string &value) { + try { + ordered_json value2dict; + value2dict = ordered_json::parse(value); + return value2dict; + } catch (const ordered_json::parse_error &ex) { + LOG_ERROR << ex.what() << ". Exception id: " << ex.id << ". Byte position of error: " << ex.byte; + return ordered_json(); + } + } + + std::unordered_map + Str2Map(const std::string &line, const std::string &delimiter, const std::string joint) { + // line: xxx1=xxx2;xxx3=xxx4;xxx5=xxx6 + std::vector pairs = Split(line, delimiter); + std::unordered_map kvs; + for (const auto &pair : pairs) { + size_t pos = pair.find(joint); + if (pos != std::string::npos) { + std::string key = pair.substr(0, pos); + std::string value = pair.substr(pos + 1); + kvs[key] = value; + } + } + return kvs; + } + +} // namespace StrSpace diff --git a/accuracy_tools/msprobe/csrc/utils/Str.h b/accuracy_tools/msprobe/csrc/utils/Str.h new file mode 100644 index 00000000000..7414e7603f9 --- /dev/null +++ b/accuracy_tools/msprobe/csrc/utils/Str.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025-2025. Huawei Technologies Co., Ltd. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STR_METHOD_H +#define STR_METHOD_H + +#include +#include +#include +#include +#include + +#include "nlohmann/json.hpp" + +namespace StrSpace { + using ordered_json = nlohmann::ordered_json; + + std::string GetEnvVar(const char *varName); + std::vector Split(const std::string &inputString, const std::string &delimiter); + uint64_t Str2Int(const char *inputString, const uint64_t &defaultValue, const char *tag); + std::vector SplitToInt(const std::string &inputString, const std::string &delimiter); + std::vector + Str2IntForVector(const std::vector &strVec, const int &defaultValue, const char *tag); + bool IsValueInGoal(const char *varName, const uint64_t &query); + bool IsValueInGoal(const char *varName, const std::string &query); + + bool IsPrefix(const std::string &str, const std::string &prefix); + bool IsSuffix(const std::string &str, const std::string &suffix); + std::string ToLower(const std::string &input); + bool IsStringLengthSafety(const std::string &ss); + bool IsStringLengthSafety(const std::vector &vec); + ordered_json Str2Json(const std::string &value); + std::unordered_map + Str2Map(const std::string &line, const std::string &delimiter, const std::string joint); + + template std::string Join(const Container &container, const std::string &delimiter) { + std::stringstream ss; + auto it = container.begin(); + if (it != container.end()) { + ss << *it; + ++it; + } + for (; it != container.end(); ++it) { + ss << delimiter; // 确保 delimiter 是 std::string + ss << *it; + } + return ss.str(); + } +} // namespace StrSpace + +#endif -- Gitee