827 lines
25 KiB
C
Raw Normal View History

2025-01-12 19:51:34 +08:00
// Copyright (c) 2023, AgiBot Inc.
// All rights reserved.
#pragma once
#include <algorithm>
#include <charconv>
#include <cstdint>
#include <cstring>
#include <iomanip>
#include <map>
#include <regex>
#include <set>
#include <sstream>
#include <string>
#include <vector>
namespace aimrt::common::util {
/**
* @brief
*
* @param[inout] s
* @return std::string& s的引用
*/
inline std::string& Trim(std::string& s) {
if (s.empty()) return s;
s.erase(s.find_last_not_of(' ') + 1);
s.erase(0, s.find_first_not_of(' '));
return s;
}
inline std::string_view& Trim(std::string_view& s) {
if (s.empty()) return s;
auto p = s.find_last_not_of(' ');
s = s.substr(0, (p != std::string_view::npos) ? (p + 1) : 0);
p = s.find_first_not_of(' ');
s = s.substr((p != std::string_view::npos) ? p : s.size());
return s;
}
/**
* @brief a=1&b=2&c=3map中
* @note
* vsep之间的子字符串msepmsep为界分为keyval
* @param[in] source
* @param[in] vsep kv之间的分隔符
* @param[in] msep kv内部的分隔符
* @param[in] trim k和v里的空格
* @param[in] clear key为空的情况
* @return std::map<std::string, std::string> map
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::map<StringType, StringType> SplitToMap(std::string_view source,
std::string_view vsep = "&",
std::string_view msep = "=",
bool trim = true,
bool clear = true) {
std::map<StringType, StringType> result;
if (source.empty() || vsep.empty() || msep.empty() || vsep == msep)
return result;
size_t v_pos_end = 0, v_pos_start = 0;
do {
v_pos_end = source.find(vsep, v_pos_start);
if (v_pos_end == std::string_view::npos) v_pos_end = source.length();
if (v_pos_end > v_pos_start) {
auto kv_str = source.substr(v_pos_start, v_pos_end - v_pos_start);
size_t msep_pos = kv_str.find(msep);
if (msep_pos != std::string_view::npos) {
auto first = kv_str.substr(0, msep_pos);
auto second = kv_str.substr(msep_pos + msep.size());
if (trim) {
Trim(first);
if (!(clear && first.empty())) {
result[StringType(first)] = StringType(Trim(second));
}
} else {
if (!(clear && first.empty())) {
result[StringType(first)] = StringType(second);
}
}
}
}
v_pos_start = v_pos_end + vsep.size();
} while (v_pos_end < source.length());
return result;
}
/**
* @brief map为a=1&b=2&c=3string
*
* @param[in] kvmap std::map<std::string,std::string>
* @param[in] vsep kv之间的分隔符
* @param[in] msep kv内部的分隔符
* @return std::string
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinMap(const std::map<StringType, StringType>& kvmap,
std::string_view vsep = "&",
std::string_view msep = "=") {
std::stringstream result;
for (auto itr = kvmap.begin(); itr != kvmap.end(); ++itr) {
if (itr != kvmap.begin()) result << vsep;
result << itr->first << msep << itr->second;
}
return result.str();
}
/**
* @brief map<std::string,std::string>val数据
*
* @param[in] m std::map<std::string,std::string>
* @param[in] key key
* @param[in] defval m中没有对应的key时defval
* @return std::string
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline const StringType& GetMapItemWithDef(const std::map<StringType, StringType>& m,
const StringType& key,
const StringType& defval = "") {
auto finditr = m.find(key);
return (finditr != m.end()) ? (finditr->second) : defval;
}
/**
* @brief s中添加kv字段
*
* @param[inout] s
* @param[in] key key
* @param[in] val val
* @param[in] vsep kv之间的分隔符
* @param[in] msep kv内部的分隔符
* @return std::string& s的引用
*/
inline std::string& AddKV(std::string& s,
std::string_view key,
std::string_view val,
std::string_view vsep = "&",
std::string_view msep = "=") {
if (!s.empty()) s += vsep;
s += key;
s += msep;
s += val;
return s;
}
/**
* @brief a=1&b=2&c=3key对应的val
* @note
* map再find要快一些map再find
* @param[in] str
* @param[in] key keyvsep或msep
* @param[in] vsep kv之间的分隔符
* @param[in] msep kv内部的分隔符
* @param[in] trim
* @return std::string key对应的val
*/
inline std::string_view GetValueFromStrKV(std::string_view str,
std::string_view key,
std::string_view vsep = "&",
std::string_view msep = "=",
bool trim = true) {
if (key.empty() || vsep.empty() || msep.empty() || vsep == msep) return "";
if (key.find(vsep) != std::string_view::npos || key.find(msep) != std::string_view::npos)
return "";
size_t key_pos = str.find(key);
while (key_pos != std::string_view::npos) {
size_t msep_pos = str.find(msep, key_pos + key.size());
if (msep_pos == std::string_view::npos) return "";
size_t key_start_pos = str.rfind(vsep, key_pos);
if (key_start_pos == std::string_view::npos)
key_start_pos = 0;
else
key_start_pos += vsep.length();
bool right_key_flag = false;
if (trim) {
auto real_key = str.substr(key_start_pos, msep_pos - key_start_pos);
Trim(real_key);
if (real_key == key) right_key_flag = true;
} else {
if ((key_start_pos == key_pos) && (msep_pos == (key_pos + key.size())))
right_key_flag = true;
}
if (right_key_flag) {
size_t val_start_pos = msep_pos + msep.size();
size_t val_end_pos = str.find(vsep, val_start_pos);
if (val_end_pos == std::string_view::npos) val_end_pos = str.length();
if (trim) {
auto re = str.substr(val_start_pos, val_end_pos - val_start_pos);
return Trim(re);
}
return str.substr(val_start_pos, val_end_pos - val_start_pos);
}
key_pos = str.find(key, msep_pos + msep.length());
}
return "";
}
/**
* @brief vector
*
* @param[in] source
* @param[in] sep
* @param[in] trim
* @param[in] clear
* @return std::vector<std::string>
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::vector<StringType> SplitToVec(std::string_view source,
std::string_view sep,
bool trim = true,
bool clear = true) {
std::vector<StringType> result;
if (source.empty() || sep.empty()) return result;
size_t pos_end = 0, pos_start = 0;
do {
pos_end = source.find(sep, pos_start);
if (pos_end == std::string_view::npos) pos_end = source.length();
auto sub_str = source.substr(pos_start, pos_end - pos_start);
if (trim) Trim(sub_str);
if (!(clear && sub_str.empty())) result.emplace_back(sub_str);
pos_start = pos_end + sep.size();
} while (pos_end < source.length());
return result;
}
/**
* @brief vector到string
*
* @param[in] vec vector
* @param[in] sep
* @return std::string
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinVec(const std::vector<StringType>& vec, std::string_view sep) {
std::string result;
for (auto itr = vec.begin(); itr != vec.end(); ++itr) {
if (itr != vec.begin()) result += sep;
result += *itr;
}
return result;
}
/**
* @brief set
*
* @param[in] source
* @param[in] sep
* @param[in] trim
* @param[in] clear
* @return std::set<std::string>
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::set<StringType> SplitToSet(std::string_view source,
std::string_view sep,
bool trim = true,
bool clear = true) {
std::set<StringType> result;
if (source.empty() || sep.empty()) return result;
size_t pos_end = 0, pos_start = 0;
do {
pos_end = source.find(sep, pos_start);
if (pos_end == std::string_view::npos) pos_end = source.length();
auto sub_str = source.substr(pos_start, pos_end - pos_start);
if (trim) Trim(sub_str);
if (!(clear && sub_str.empty())) result.emplace(sub_str);
pos_start = pos_end + sep.size();
} while (pos_end < source.length());
return result;
}
/**
* @brief set到string
*
* @param[in] st set
* @param[in] sep
* @return std::string
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string JoinSet(const std::set<StringType>& st, std::string_view sep) {
std::string result;
for (auto itr = st.begin(); itr != st.end(); ++itr) {
if (itr != st.begin()) result += sep;
result += *itr;
}
return result;
}
/**
* @brief key字符串是否在list字符串中"123""123,456,789"
* @note
* vector再find要快一些vector再find
* @param[in] str list字符串
* @param[in] key key字符串sep
* @param[in] sep list的分隔符
* @param[in] trim
* @return true key在list中
* @return false key不在list中
*/
inline bool CheckIfInList(std::string_view str,
std::string_view key,
std::string_view sep = ",",
bool trim = true) {
if (key.empty() || sep.empty()) return false;
if (key.find(sep) != std::string_view::npos) return false;
size_t key_pos = str.find(key);
while (key_pos != std::string_view::npos) {
size_t key_start_pos = str.rfind(sep, key_pos);
if (key_start_pos == std::string_view::npos)
key_start_pos = 0;
else
key_start_pos += sep.length();
size_t key_end_pos = str.find(sep, key_pos + key.length());
if (key_end_pos == std::string_view::npos) key_end_pos = str.length();
if (trim) {
auto real_key = str.substr(key_start_pos, key_end_pos - key_start_pos);
Trim(real_key);
if (real_key == key) return true;
} else {
if ((key_start_pos == key_pos) && (key_end_pos == (key_pos + key.size())))
return true;
}
key_pos = str.find(key, key_end_pos + sep.length());
}
return false;
}
/**
* @brief 6.1.1 6.2.8
*
* @param[in] ver1 1
* @param[in] ver2 2
* @return int 10-1
*/
inline int CmpVersion(std::string_view ver1, std::string_view ver2) {
const auto& version1_detail =
SplitToVec<std::string_view>(ver1, ".", true, true);
const auto& version2_detail =
SplitToVec<std::string_view>(ver2, ".", true, true);
size_t idx = 0;
for (idx = 0; idx < version1_detail.size() && idx < version2_detail.size(); ++idx) {
int ver1_num = 0, ver2_num = 0;
auto [ptr1, ec1] = std::from_chars(version1_detail[idx].data(),
version1_detail[idx].data() + version1_detail[idx].size(),
ver1_num);
auto [ptr2, ec2] = std::from_chars(version2_detail[idx].data(),
version2_detail[idx].data() + version2_detail[idx].size(),
ver2_num);
if (ec1 != std::errc() || ec2 != std::errc() ||
ptr1 != version1_detail[idx].data() + version1_detail[idx].size() ||
ptr2 != version2_detail[idx].data() + version2_detail[idx].size()) {
throw std::runtime_error("Invalid version format");
}
if (ver1_num < ver2_num)
return -1;
if (ver1_num > ver2_num)
return 1;
}
if (idx == version1_detail.size() && idx == version2_detail.size()) {
return 0;
}
return version1_detail.size() > version2_detail.size() ? 1 : -1;
}
/**
* @brief
* @note start_ver<end_verend_ver空999.9.9.9
* @param[in] ver
* @param[in] start_ver
* @param[in] end_ver
* @return true
* @return false
*/
inline bool CheckVersionInside(std::string_view ver,
std::string_view start_ver,
std::string_view end_ver) {
return (CmpVersion(ver, start_ver.empty() ? "0.0.0.0" : start_ver) >= 0 &&
CmpVersion(ver, end_ver.empty() ? "999.9.9.9" : end_ver) <= 0);
}
/**
* @brief ov换为字符串nv
*
* @param[inout] str
* @param[in] ov
* @param[in] nv
* @return std::string& str的引用
*/
inline std::string& ReplaceString(std::string& str,
std::string_view ov,
std::string_view nv) {
if (str.empty() || ov.empty()) return str;
std::vector<size_t> vec_pos;
size_t pos = 0, old_len = ov.size(), new_len = nv.size();
while ((pos = str.find(ov, pos)) != std::string::npos) {
vec_pos.emplace_back(pos);
pos += old_len;
}
size_t& vec_len = pos = vec_pos.size();
if (vec_len) {
if (old_len == new_len) {
for (size_t ii = 0; ii < vec_len; ++ii)
memcpy(const_cast<char*>(str.c_str() + vec_pos[ii]), nv.data(),
new_len);
} else if (old_len > new_len) {
char* p = const_cast<char*>(str.c_str()) + vec_pos[0];
vec_pos.emplace_back(str.size());
for (size_t ii = 0; ii < vec_len; ++ii) {
memcpy(p, nv.data(), new_len);
p += new_len;
size_t cplen = vec_pos[ii + 1] - vec_pos[ii] - old_len;
memmove(p, str.c_str() + vec_pos[ii] + old_len, cplen);
p += cplen;
}
str.resize(p - str.c_str());
} else {
size_t diff = new_len - old_len;
vec_pos.emplace_back(str.size());
str.resize(str.size() + diff * vec_len);
char* p = const_cast<char*>(str.c_str()) + str.size();
for (size_t ii = vec_len - 1; ii < vec_len; --ii) {
size_t cplen = vec_pos[ii + 1] - vec_pos[ii] - old_len;
p -= cplen;
memmove(p, str.c_str() + vec_pos[ii] + old_len, cplen);
p -= new_len;
memcpy(p, nv.data(), new_len);
}
}
}
return str;
}
/**
* @brief
*
* @param[in] str
* @return true
* @return false
*/
inline bool IsAlnumStr(std::string_view str) {
if (str.length() == 0) return false;
for (const auto& c : str) {
if (!isalnum(c)) return false;
}
return true;
}
/**
* @brief
*
* @param[in] str
* @return true
* @return false
*/
inline bool IsDigitStr(std::string_view str) {
if (str.length() == 0) return false;
for (const auto& c : str) {
if (c > '9' || c < '0') return false;
}
return true;
}
/**
* @brief map中的key的集合
*
* @tparam KeyType map中key的类型
* @tparam ValType map中val的类型
* @param m map
* @return std::set<KeyType> key的集合
*/
template <typename KeyType, typename ValType>
inline std::set<KeyType> GetMapKeys(const std::map<KeyType, ValType>& m) {
std::set<KeyType> re;
for (const auto& it : m) re.emplace(it.first);
return re;
}
/**
* @brief
*
* @param table
* @param with_header
* @return std::string
*/
template <class StringType = std::string_view>
requires(std::is_same_v<StringType, std::string_view> ||
std::is_same_v<StringType, std::string>)
inline std::string DrawTable(
const std::vector<std::vector<StringType>>& table, bool with_header = true) {
size_t row = table.size();
if (row == 0) return "<empty table>";
std::vector<size_t> column_width;
for (auto& row_item : table) {
if (column_width.size() < row_item.size())
column_width.resize(row_item.size());
for (size_t ii = 0; ii < row_item.size(); ++ii)
column_width[ii] = std::max(column_width[ii], row_item[ii].size());
}
for (auto& item : column_width)
item += 2;
size_t column = column_width.size();
std::stringstream result;
// first line
for (size_t jj = 0; jj < column; ++jj) {
result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-";
}
result << "+\n";
for (size_t ii = 0; ii < row; ++ii) {
for (size_t jj = 0; jj < column; ++jj) {
std::string cur_data = " ";
if (table[ii].size() > jj)
cur_data += table[ii][jj];
result << "|" << std::left << std::setfill(' ') << std::setw(column_width[jj]) << cur_data;
}
result << "|\n";
// table header
if (with_header && ii == 0) {
for (size_t jj = 0; jj < column; ++jj) {
result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-";
}
result << "+\n";
}
}
// last line
for (size_t jj = 0; jj < column; ++jj) {
result << "+" << std::setfill('-') << std::setw(column_width[jj]) << "-";
}
result << "+\n";
return result.str();
}
/**
* @brief ${XXX_ENV}
*
* @param input
* @return std::string
*/
inline std::string ReplaceEnvVars(std::string_view input) {
std::regex pattern(R"(\$\{([^}]+)\})");
std::smatch match;
std::string result(input);
size_t cur_pos = 0;
while (std::regex_search(result.cbegin() + cur_pos, result.cend(), match, pattern)) {
std::string env_name = match[1].str();
const char* env_val = std::getenv(env_name.c_str());
if (env_val == nullptr) env_val = "";
result.replace(cur_pos + match.position(0), match.length(0), env_val);
cur_pos += (match.position(0) + strlen(env_val));
}
return result;
}
/**
* @brief
*
* @param c
* @return char
*/
inline char CharToLower(char c) { return static_cast<char>(tolower(c)); }
/**
* @brief
*
* @param c
* @return char
*/
inline char CharToUpper(char c) { return static_cast<char>(toupper(c)); }
/**
* @brief
*
* @param str
* @return std::string&
*/
inline std::string& StrToLower(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), CharToLower);
return str;
}
/**
* @brief
*
* @param str
* @return std::string
*/
inline std::string StrToLower(std::string_view str) {
std::string result;
result.resize(str.size());
std::transform(str.begin(), str.end(), result.begin(), CharToLower);
return result;
}
/**
* @brief
*
* @param str
* @return std::string&
*/
inline std::string& StrToUpper(std::string& str) {
std::transform(str.begin(), str.end(), str.begin(), CharToUpper);
return str;
}
/**
* @brief
*
* @param str
* @return std::string
*/
inline std::string StrToUpper(std::string_view str) {
std::string result;
result.resize(str.size());
std::transform(str.begin(), str.end(), result.begin(), CharToUpper);
return result;
}
/**
* @brief
*
* @param str1
* @param str2
* @return true
* @return false
*/
inline bool CheckIEqual(std::string_view str1, std::string_view str2) {
if (str1.size() != str2.size()) return false;
return std::equal(str1.begin(), str1.end(), str2.begin(),
[](char a, char b) { return std::tolower(a) == std::tolower(b); });
}
/**
* @brief
*
* @param str
* @return std::string&
*/
inline std::string& StrToTitleCase(std::string& str) {
std::string::iterator it = str.begin();
*it = CharToUpper(*it);
for (; it != str.end() - 1; ++it) {
if (*it == ' ') {
*(it + 1) = CharToUpper(*(it + 1));
}
}
return str;
}
/**
* @brief standardise a path string
*
* @param path
* @return std::string&
*/
inline std::string& StandardisePath(std::string& path) {
if (path.empty()) return (path = "/");
std::replace(path.begin(), path.end(), '\\', '/');
if (path[path.length() - 1] != '/') path += '/';
return path;
}
/**
* @brief judge if str start with pattern
*
* @param str
* @param pattern
* @param ignore_case if ignore the case
* @return true
* @return false
*/
inline bool StartsWith(std::string_view str,
std::string_view pattern,
bool ignore_case = false) {
const size_t str_len = str.length();
const size_t pattern_len = pattern.length();
if (str_len < pattern_len || pattern_len == 0) return false;
if (ignore_case) {
for (size_t i = 0; i < pattern_len; ++i) {
if (tolower(pattern[i]) != tolower(str[i])) return false;
}
return true;
}
for (size_t i = 0; i < pattern_len; ++i) {
if (pattern[i] != str[i]) return false;
}
return true;
}
/**
* @brief judge if str end with pattern
*
* @param str
* @param pattern
* @param ignore_case if ignore the case
* @return true
* @return false
*/
inline bool EndsWith(std::string_view str,
std::string_view pattern,
bool ignore_case = false) {
const size_t str_len = str.length();
const size_t pattern_len = pattern.length();
if (str_len < pattern_len || pattern_len == 0) return false;
const size_t& begin_pos = str_len - pattern_len;
if (ignore_case) {
for (size_t i = 0; i < pattern_len; ++i) {
if (tolower(pattern[i]) != tolower(str[begin_pos + i])) return false;
}
return true;
}
for (size_t i = 0; i < pattern_len; i++) {
if (pattern[i] != str[begin_pos + i]) return false;
}
return true;
}
/**
* @brief fnv1a hash算法64
*
* @param data
* @param len
* @return uint64_t hash值
*/
inline uint64_t Hash64Fnv1a(const char* data, size_t len) {
constexpr uint64_t kPrime = 0x100000001b3;
uint64_t hash = 0xcbf29ce484222325;
for (size_t i = 0; i < len; ++i) {
hash = (hash ^ static_cast<uint8_t>(data[i])) * kPrime;
}
return hash;
}
/**
* @brief fnv1a hash算法32
*
* @param data
* @param len
* @return uint32_t hash值
*/
inline uint32_t Hash32Fnv1a(const char* data, size_t len) {
constexpr uint32_t kPrime = 0x1000193;
uint32_t hash = 0x811c9dc5;
for (size_t i = 0; i < len; ++i) {
hash = (hash ^ static_cast<uint8_t>(data[i])) * kPrime;
}
return hash;
}
template <typename T>
inline std::string SSToString(const T& obj) {
std::stringstream ss;
ss << obj;
return ss.str();
}
struct StringHash {
using hash_type = std::hash<std::string_view>;
using is_transparent = void;
std::size_t operator()(const char* str) const { return hash_type{}(str); }
std::size_t operator()(std::string_view str) const { return hash_type{}(str); }
std::size_t operator()(std::string const& str) const { return hash_type{}(str); }
};
} // namespace aimrt::common::util